RayMarching(二)

作者: 万里_aa3f | 来源:发表于2019-02-12 02:40 被阅读0次

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

    相关文章

      网友评论

        本文标题:RayMarching(二)

        本文链接:https://www.haomeiwen.com/subject/gtxheqtx.html