RayMarching(ii) – Jianshu.com

RayMarching does not require any model data. You need to know the projected light on each element so that 3D graphics can be drawn through the algorithm

The basic steps of RayMarching:

1. Construct distance field: it is used to work out the minimum distance between POS and scene when stepping, and it is used for the next step length; 2. Perform RayMarching and set the minimum threshold break to figure out POS; 3. According to the specific algorithm to calculate, normal and other information for lighting;

IQ warrior blog:iquilezles.org/www/article… 【 Distance field 】 The following specific mountain practice

Terrian RayMarching

1. Terrain height field: After using Noise classification, multiply its value by a height to reach the height field. Read it on the world’s XZ axis; 2.RayMarching precision algorithm and high performance algorithm; 3. Figure out normals, shadows, lighting

The IQ of the terrian raymarching:iquilezles.org/www/article…

1. The Noise and the FBM

Value Noise is used here, because Perlin performance is better, but also may be more sharp, ha ha, not made

    float Noise(float2 p)
    {
        float2 pi = floor(p);
        float2 pf = p - pi;
        
        float2 w = pf * pf * (3.0 - 2.0 * pf);
        
        return lerp(lerp(Hash12(pi + float2(0.0.0.0)), Hash12(pi + float2(1.0.0.0)), w.x),
                lerp(Hash12(pi + float2(0.0.1.0)), Hash12(pi + float2(1.0.1.0)), w.x),
                w.y);
    }
Copy the code

FBM is divided into 5 times, for performance consideration, if the details are not enough, normal can find

    float FBM( float2 p )
    {
        float f = 0.0;
        f += 0.50000*Noise( p*1.0  );
        f += 0.25000*Noise( p*2.03  ); 
        f += 0.12500*Noise( p*4.01  ); 
        f += 0.06250*Noise( p*8.05  ); 
        f += 0.03125*Noise( p*16.02 );
        return f/0.984375;
    }
Copy the code

2. RayMarching algorithm

  • According to the algorithm introduced in the IQ article, from the camera emission ray, each advance of a small end distance to judge whether the following current Pos. Y is greater than the height field Map(Pos.
  • If it is greater than, it means that the ray does not intersect with the mountains;
  • When is less than, it means that it has just entered the mountains and rivers, then its position is subtracted by half of delt to get the final Pos

//ro ray origin rd ray direction 
bool castRay(float3  ro, float3 rd , inout float rayLenght) 
    {
    float delt = 0.4;
    float mint = 0.001;
    float maxt = 1000.0;
    for( float t = mint; t < maxt; t += delt )
    {
        float3  p = ro + rd*t;
        float h = Map(p.xz); //p is position xz is xoz and h is the corresponding model height
        if( p.y < h )
        {
            rayLenght = t - 0.5f*delt;
            return true; }}return false;
}
Copy the code

3

This is straight from the IQ

Normal is worth saying that the FBM fractal used here is 9 times, otherwise it looks like a desert
float3 getNormal( float2  p ) // for terrain Map(p)
{
    float eps = 0.1; // or some other value
    float2 h = float2(eps,0);
    return normalize( float3( NormalMap(p-h.xy) - NormalMap(p+h.xy),
                            2.0*h.x,
                            NormalMap(p-h.yx) - NormalMap(p+h.yx) ) );
}
Copy the code
shadow
float terrainShadow( float3 ro,  float3 rd, float mint )
{
    float res = 1.0;
    float t = mint;
    for( int i=0; i<80; i++ )
    {
        float3  pos = ro + t*rd;
        float2  env = Map( pos.xz );
        float hei = pos.y - env.x;
        res = min( res, 32.0*hei/t );
        if( res<0.001) break;
        t += clamp( hei, 0.5+t*0.1.30.0 );
    }
    return clamp( res, 0.0.1.0 );
}
Copy the code
lighting
float3 RenderTerrian(float3 lightDir,float3 ro ,float3 rd , float rayLenght){
    float3 color=float3(0.10.0.09.0.08);
    //data
    float3 rayPos = ro + rd * rayLenght;
    float3 normal = getNormal(rayPos.xz);
    //lighting
    float amb = clamp(0.5+0.5*normal.y,0.0.1.0);
    float dif = clamp( dot( lightDir, normal), 0.0.1.0 );
    float bac = clamp( 0.2 + 0.8*dot( normalize( float3(-lightDir.x, 0.0, lightDir.z ) ), normal ), 0.0.1.0 );
    
    //shadow
    float sh=terrainShadow(rayPos+normal*0.3,lightDir,0.6);

    float3 lin  = float3(0.0.0.0.0.0);
    lin += dif*float3(7.00.5.00.3.00)* float3( sh, sh*sh*0.5+0.5*sh, sh*sh*0.8+0.2*sh );
    lin += amb*float3(0.40.0.60.1.00) *1.2;
    lin += bac*float3(0.40.0.50.0.60);
    color *=lin;

    // fog
    float fo = 1.0-exp(-pow(0.1 * rayLenght/_MountainHeight , 1.5));
    float3 fco = 0.65*float3(0.4.0.65.1.0);
    color = lerp( color, fco, fo );

    return color;
}
Copy the code

Merge with the sky

float4 ProcessRayMarch(float3 ro,float3 rd,float4 sceneCol){
    //data
    float3 lightDir=normalize( _LightDir.xyz);
    float4 finalColor=float4(0.0.0.1);

    //sky-------------------------------------------------------------------------------
    //cloud
    float cos0 = dot(normalize(rd),float3(0.1.0));
    float cloudNoise = 0;
    float s = 0.5;
    for(int i=0; i<3; i++){float height = 2000 + i * 1000;
        float3 pos = height/cos0 * rd;
        float2 worldXZ = ro.xz + pos.xz;
        cloudNoise += s * FBMRTIME(worldXZ/2000 + i * 500 ,(_Time.y / (1+i*2)) /5 );
        s*=0.5;
    }
    cloudNoise = smoothstep(0.4.0.6, cloudNoise);
    // sun
    float sundot = clamp(dot(rd,lightDir),0.0.1.0);
    float3 sunColor= 0.25*float3(1.0.0.7.0.4) *pow( sundot,5.0 );
    sunColor += 0.25*float3(1.0.0.8.0.6) *pow( sundot,64.0 );
    sunColor += 0.4*float3(1.0.0.8.0.6) *pow( sundot,512.0 );
    //skycolor
    float3 skyColor= float3(0.2.0.5.0.85) *1.1 - rd.y*rd.y*0.5;
    skyColor= lerp( skyColor, 0.85*float3(0.7.0.75.0.85), pow( 1.0-max(rd.y,0.0), 4.0)); skyColor+=sunColor;//skydown
    float3 sky =lerp(skyColor ,float3(1.0.0.95.1.0) , cloudNoise );
    sky =lerp(skyColor , sky ,smoothstep(0.1.0.25,rd.y) ) ;
    sky =lerp(float3(0.4.0.4.0.4) , sky ,smoothstep(0.1.0.1,rd.y) );

    //terrian-----------------------------------------------------------------------------

    float rayLenght = 0.01;
    bool beTerrian = castRay(ro, rd, rayLenght);
    if( beTerrian )
    {
        finalColor.xyz = RenderTerrian(lightDir , ro , rd , rayLenght);
    }else{
        finalColor.xyz = sky;
    }
    
    return finalColor;
}
Copy the code