unity shader 技术从入门到放弃shaderUnity Shader分享

RayMarching(二)

2019-02-12  本文已影响0人  万里_aa3f

RayMarching(二)

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


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之间的距离 ,将其的一半当作下次步进的长度


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;
    }
}

缺点:当地形起伏较大时,会出现错误



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;
}

与天空合并

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;
}
上一篇 下一篇

猜你喜欢

热点阅读