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