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

3D Cloud shadertoy抄入

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

3D Cloud shadertoy抄入


源码来自iq的shadertoyhttps://www.shadertoy.com/view/XslGRr
其中光照部分的算法,没看太懂。但代码结构简单清晰。还是有些参数的作用还是能猜出个大概。
在此先记录下来,以供今后的学习
此篇亦是基于Ray Marching

3D Cloud实现思路:

从摄像机开始步进,进入worldPos的y值进入云层范围,将其传入分形后的3DNoise进行采样求出当前坐标点的雾浓度。然后根据求得的值进行lighting与Alpha的计算。再次步进,再次重复上面的lighting与Alpha的计算。将计算结果叠加。最总,光线穿过云层或浓度达到一定的值 停止计算。
1.3D noise与分形
2.raymarching的方法
3.lighting计算
4.总render

1.3D noise与分形

value Noise+FBM (只展示了FBM5)

    float VNoise(float3 p)
    {
        float3 pi = floor(p);
        float3 pf = p - pi;
        
        float3 w = pf * pf * (3.0 - 2.0 * pf);
        
        return  lerp(
                    lerp(
                        lerp(Hash13(pi + float3(0, 0, 0)), Hash13(pi + float3(1, 0, 0)), w.x),
                        lerp(Hash13(pi + float3(0, 0, 1)), Hash13(pi + float3(1, 0, 1)), w.x), 
                        w.z),
                    lerp(
                        lerp(Hash13(pi + float3(0, 1, 0)), Hash13(pi + float3(1, 1, 0)), w.x),
                        lerp(Hash13(pi + float3(0, 1, 1)), Hash13(pi + float3(1, 1, 1)), w.x), 
                        w.z),
                    w.y);
    }

    float FBM5(  float3 p )
    {
        p*=uv2;
        float n = 0.0;
        n += 0.50000*VNoise( p*1.0 );
        n += 0.25000*VNoise( p*2.0 );
        n += 0.12500*VNoise( p*4.0 );
        n += 0.06250*VNoise( p*8.0 );
        n += 0.03125*VNoise( p*16.0 );
        return clamp( 1.5 - p.y - 2.0 + 1.75*n, 0.0, 1.0 );
    }

2.raymarching

将解释写在代码注释上

float4 raymarch(  float3 ro,  float3 rd, float3 bgcol)
{
    float4 sum = float4(0,0,0,0);

    float t = 0.0;//0.05*texelFetch( iChannel0, px&255, 0 ).x;

    MARCH(30,FBM5);
    MARCH(30,FBM4);
    MARCH(30,FBM3);
    MARCH(30,FBM2);

    return clamp( sum, 0.0, 1.0 );
}

#define MARCH(STEPS,MAPLOD) \
    for(int i=0; i<STEPS; i++)\
    {\
        float3  pos = ro + t*rd;\
         //减少不必要的步进运算 云层控制在-3到2 浓度>0.99 停止步进
        if( pos.y<-3.0 || pos.y>2.0 || sum.a > 0.99 ) break;\  
         //查看校验这次步进达到的地方有没有云的分布:密度
        float den = MAPLOD(pos);\                              
        if( den>0.01 )\ //如果浓度大于0,进行dif 计算
        {\ 
            //像太阳方向步进 0.3 看该处云的值
            float dif =  clamp((den - MAPLOD(pos+0.3*sundir))/0.6, 0.0, 1.0 );\
           //积分光照
            sum = integrate( sum, dif, den, bgcol, t );\            
        }\ 
        //每次至少步进0.05,或者是 t已经步进的距离的0.02倍
        t += max(0.05,0.02*t);\                 
    }

3.Lighting计算

float4 integrate( float4 sum,  float dif,  float den, float3 bgcol,  float t )
{
    // lighting
    float3 lin = float3(0.65,0.7,0.75)*1.4 + float3(1.0, 0.6, 0.3)*dif;        
    //太阳光的颜色与云朵阴影的颜色
   float4 col =float4( lerp( float3(1.0,0.95,0.8), float3(0.25,0.3,0.35), den ), den );  
    col.xyz *= lin;
    //参考云层的散射公式
    col.xyz = lerp( col.xyz, bgcol, 1.0-exp(-0.003*t*t) );   
    // front to back blending    
    //密度*0.4 为当前的a
    col.a *= 0.4;       
    //再将当前的a乘以颜色
    col.rgb *= col.a;   
    //此层的value * (1-a)
    return sum + col*(1.0-sum.a);  
}

4.总render

float4 render(  float3 ro,  float3 rd )
{

    // background sky     
    float sun = clamp( dot(sundir,rd), 0.0, 1.0 );
    float3 col = float3(0.6,0.71,0.75) - rd.y*0.2*float3(1.0,0.5,1.0) + 0.15*0.5;
    col += 0.2*float3(1.0,.6,0.1)*pow( sun, 8.0 );

    // clouds    
    float4 res = raymarch( ro, rd, col);
    col = col*(1.0-res.w) + res.xyz;
    
    // sun glare    
    col += 0.2*float3(1.0,0.4,0.2)*pow( sun, 3.0 );

    return float4( col, 1.0 );
}
上一篇 下一篇

猜你喜欢

热点阅读