RayMarching(二)
2019-02-12 本文已影响0人
万里_aa3f
RayMarching(二)
![](https://img.haomeiwen.com/i11208481/dd3824e8da559f79.png)
RayMarching不需要任何的模型数据,需知道每个片元上的投射光线便可通过算法实现 3D图形的绘制
RayMarching的基本步骤:
1.构建距离场:用于求出步进时所在pos与场景的最小距离,将其用于下次步进的长度;
2.进行RayMarching,设定最小阈值break出来求出pos;
3.根据特定算法求出、法线等信息用于lighting;
iq大侠blog:http://iquilezles.org/www/articles/distfunctions/distfunctions.htm【距离场】
下面具体谈山脉做法
Terrian RayMarching
1.地形高度场:使用Noise分型后,将其数值乘上一个高度,最为高度场。用世界坐标系下的XZ轴读取它;
2.RayMarching精确算法与高性能算法;
3.求出法线、阴影、lighting
iq的terrian raymarching:http://iquilezles.org/www/articles/terrainmarching/terrainmarching.htm
1.Noise与FBM
这里Noise用的是Value Noise,原因相比Perlin性能好,还可能比较有锐利感,哈哈,不造
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);
}
FBM 分5次,为性能考虑,如细节不够normal上找
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;
}
2.RayMarching算法
从iq文中介绍的算法是:从摄像机发射射线,每前进一小端距离判断以下 当前的Pos.y是否大于高度场Map(Pos.xz)
如果大于,则说明射线与山川没有相交;
当小于时,说明刚刚进入山川下面,则将其位置减去delt的一半得出最终的Pos
![](https://img.haomeiwen.com/i11208481/13f393efbb9f2d70.png)
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);
if( p.y < h )
{
rayLenght = t - 0.5f*delt;
return true;
}
}
return false;
}
优化算法
每次步进后,求出Pos与Map之间的距离 ,将其的一半当作下次步进的长度
![](https://img.haomeiwen.com/i11208481/1a9b119118d1f79b.png)
bool castRay(float3 ro, float3 rd , inout float rayLenght){
float t = .1;
float tmax=3000;
for( int i=0; i<512; i++ ) {
float3 p = ro + rd*t;
float precis = 0.00001*t;
float res = p.y - Map(p.xz);
if( res<precis || t>tmax ) break;
t += 0.5*res;
}
rayLenght=t;
if( t>tmax ){
return false;
}else{
return true;
}
}
缺点:当地形起伏较大时,会出现错误
![](https://img.haomeiwen.com/i11208481/b5a095f1650e8f20.png)
![](https://img.haomeiwen.com/i11208481/dafcb95fe382e80f.gif)
3.Normal、shadow、lighting
这个直接搬的iq
normal 值得说的是,这里用的FBM分形9次,否则看起来像沙漠
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) ) );
}
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 );
}
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;
}
![](https://img.haomeiwen.com/i11208481/eeea7d806c7c4f98.gif)
与天空合并
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;
}