FishMan的技术专栏 Game Developer/Technical Artist

中级Shader教程15 地形渲染

2018-04-23
Jiepeng Tan

1.前置知识链接

1.FBM请参考这篇文章中给出的链接
2.ranymarching 框架

2.抽取地形shader 通用函数

  在处理类地形raymarching的过程中,为了优化,在raycast阶段会有一个大循环,为了性能,我们在这里采样的精度低一点,在计算法线的时候精度高一点,因为计算法线的时候,只有一次。

所以我们分为三种不同精度的Map函数,同样为了减少重复代码,我们使用宏来展开

#define Terrain(pos,NUM)\
    float2 p = pos.xz*.9/_MaxTerrianH;\
    float a = 0.0;\
    float b = 0.491;\
    float2  d = float2(0.0,0.);\
    for( int i=0; i<NUM; i++ ){\
        float n = Noised(p).x;\
        a += b*n;\
        b *= 0.49;\
        p = p*2.01;\
    }\
    return float2(pos.y - _MaxTerrianH*a,1.);

float2 TerrainL(float3 pos){ 
    Terrain(pos,5.);
} 
float2 TerrainM(float3 pos){
    Terrain(pos,9.);
} 
float2 TerrainH(float3 pos){
    Terrain(pos,15.);
}  


float RaycastTerrain(float3 ro, float3 rd) { 
    _MRCRO_RAY_CAST(ro,rd,10000.,TerrainL);  
}
float3 NormalTerrian( in float3 pos, float rz ){
    _MACRO_CALC_NORMAL(pos,rz,TerrainH); 
}

float SoftShadow(in float3 ro, in float3 rd,float tmax){    
    _MACRO_SOFT_SHADOW(ro,rd,tmax,TerrainM);  
}  

3.地形渲染

1.地形Map函数
我们使用FBM来模拟地形的高程图

float2 MapTerrain(float3 pos){
    float2 p = pos.xz*.9/_MaxTerrianH;//调整地形的宽高比
    float a = 0.0;
    float b = 0.491;
    float2  d = float2(0.0,0.);
    for( int i=0; i<5.; i++ ){//基本fbm
        float n = Noised(p).x;
        a += b*n;
        b *= 0.49;
        p = p*2.01;
    }
    return float2(pos.y - _MaxTerrianH*a,1.);
}

2.地形着色
  这部分和传统的材质shader没什么区别,你需要考虑的有阴影,diffuse,specular,环境光,背光,以及雾效果。当然这里没有进行贴图处理,在unity中我们可以方便的添加纹理贴图。你只需添加相应的代码即可。
在shadertoy中,如果没有纹理可用,这时候,就可以实用哦个procedures纹理,如fbm等生成。

 float3 RenderMountain(float3 pos, float3 rd,float rz, float3 nor, float3 lightDir) {  
    float3 col = float3(0.,0.,0.);
    //base color 
    col = float3(0.10,0.09,0.08);

    //lighting     
    float amb = clamp(0.5+0.5*nor.y,0.0,1.0);
    float dif = clamp( dot( lightDir, nor ), 0.0, 1.0 );
    float bac = clamp( 0.2 + 0.8*dot( normalize( float3(-lightDir.x, 0.0, lightDir.z ) ), nor ), 0.0, 1.0 );
    
    //shadow
    float sh = SoftShadow(pos+lightDir*_MaxTerrianH*0.08,lightDir,_MaxTerrianH*1.2);

    //brdf 
    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);
    col *= lin;
    // fog
    float fo = 1.0-exp(-pow(0.1*rz/_MaxTerrianH,1.5));
    float3 fco = 0.65*float3(0.4,0.65,1.0);
    col = lerp( col, fco, fo );
    return col;
}

3.整合
raycast检测碰撞点,如果没有碰撞到,就采样天空盒,有碰撞到,就进行响应的shading就好了。最后添加gamma矫正。

  float4 ProcessRayMarch(float2 uv,float3 ro,float3 rd,inout float sceneDep,float4 sceneCol){ 
    float tmax = 3000.;
    float rz = RaycastTerrain(ro,rd).x; 
    float3 pos = ro + rd *rz;
    float3 nor = NormalTerrian(pos,rz);
    // color 
    float3 col = float3(0.,0.,0.);
    if(rz >tmax ){ 
        col= Sky(pos,rd,_LightDir);
    }else{
        col = RenderMountain(pos,rd,rz,nor,_LightDir);
    } 
    //gamma
    col = pow( col, float3(0.4545,0.4545,0.4545) );
    sceneCol.xyz = col; 
    return sceneCol; 
}

配套视频


Similar Posts

Comments