1.前置知识链接
1.Noise和FBM请参考这篇文章中给出的链接
2.ranymarching 框架
2.实现原理
1.利用FBM来模拟空间雾的密度分布
2.通过raymarch 距离根据密度从前到后多层颜色混合
这里的Noise 使用triNoise,其实PerlinNoise 也行,不过perlinNoise,需要的指令较多
3.源码实现
1.noise 模拟 fog 浓度
1.noise
float _tri(in float x){return abs(frac(x)-.5);}
float2 _tri2(in float2 p){return float2(_tri(p.x+_tri(p.y*2.)), _tri(p.y+_tri(p.x*2.)));}
float3 _tri3(in float3 p){return float3(_tri(p.z+_tri(p.y*1.)), _tri(p.z+_tri(p.x*1.)), _tri(p.y+_tri(p.x*1.)));}
//https://www.shadertoy.com/view/4ts3z2
float TNoise(in float3 p, float time,float spd)
{
float z=1.4;
float rz = 0.;
float3 bp = p;
//类似FBM 效果 但是指令更少
for (float i=0.; i<=3.; i++ )
{
float3 dg = _tri3(bp*2.);
p += dg+time*spd;
bp *= 1.8;
z *= 1.5;
p *= 1.2;
rz+= (_tri(p.z+_tri(p.x+_tri(p.y))))/z;
bp += 0.14;
}
return rz;
}
整个场景中Noise 分布 效果如下
2.Fog实现
通过FBM 给整个场景中的noise 添加更多的变化,然后通过raymarch 从前王rayhit 的方向进行颜色混合来模拟fog 对于最终场景的影响。
注意这里因为速度问题,我们采样的量不会太多,为了是雾气效果平滑的过渡,我们需要对采样点之间进行插值过渡
fixed3 Fog(in fixed3 bgCol, in fixed3 ro, in fixed3 rd, in fixed maxT,
float3 fogCol,float3 spd,float2 heightRange)
{
fixed d = .4;
float3 col = bgCol;
for(int i=0; i<7; i++)
{
fixed3 p = ro + rd*d;
// add some movement at some dir
p += spd * ftime;
p.z += sin(p.x*.5);
// get height desity
float hDen = (1.-smoothstep(heightRange.x,heightRange.y,p.y));
// get final density
fixed den = TNoise(p*2.2/(d+20.),ftime, 0.2)* hDen;
fixed3 col2 = fogCol *( den *0.5+0.5);
col = lerp(col,col2,clamp(den*smoothstep(d-0.4,d+2.+d*.75,maxT),0.,1.) );
d *= 1.5+0.3;
if (d>maxT)break;
}
return col;
}
3.全部源码
// create by JiepengTan
// https://github.com/JiepengTan/FishManShaderTutorial2018-04-13
// email: jiepengtan@gmail.com
Shader "FishManShaderTutorial/Fog" {
Properties{
_MainTex("Base (RGB)", 2D) = "white" {}
_LoopNum ("_LoopNum", Vector) = (40.,128., 1, 1)
_FogSpd ("_FogSpd", Vector) = (1.,0.,0.,0.5)
_FogHighRange ("_FogHighRange", Vector) = (-5,10,0.,0.5)
_FogCol ("_FogCol", COLOR) = (.025, .2, .125,0.)
}
SubShader{
Pass {
ZTest Always Cull Off ZWrite Off
CGPROGRAM
float4 _LoopNum = float4(40.,128.,0.,0.);
float3 _FogSpd ;
float2 _FogHighRange;
fixed3 _FogCol;
#pragma vertex vert
#pragma fragment frag
#include "ShaderLibs/Framework3D.cginc"
#define ITR 100
#define FAR 50.
fixed3 Normal(in fixed3 p){
return float3(0.,1.0,0.);
}
fixed RayCast(in fixed3 ro, in fixed3 rd) {
if (rd.y>=0.0) {
return 100000;
}
float d = -(ro.y - 0.)/rd.y;
d = min(100000.0, d);
return d;
}
float4 ProcessRayMarch(float2 uv,float3 ro,float3 rd,inout float sceneDep,float4 sceneCol){
fixed3 ligt = normalize( fixed3(.5, .05, -.2) );
fixed3 ligt2 = normalize( fixed3(.5, -.1, -.2) );
fixed rz = RayCast(ro,rd);
fixed3 fogb = lerp(fixed3(.7,.8,.8 )*0.3, fixed3(1.,1.,.77)*.95, pow(dot(rd,ligt2)+1.2, 2.5)*.25);
fogb *= clamp(rd.y*.5+.6, 0., 1.);
fixed3 col = fogb;
if ( rz < FAR ){
fixed3 pos = ro+rz*rd;
fixed3 nor= Normal( pos );
fixed dif = clamp( dot( nor, ligt ), 0.0, 1.0 );
fixed spe = pow(clamp( dot( reflect(rd,nor), ligt ), 0.0, 1.0 ),50.);
col = lerp(fixed3(0.1,0.2,1),fixed3(.3,.5,1),pos.y*.5)*0.2+.1;
col = col*dif + col*spe*.5 ;
}
MergeRayMarchingIntoUnity(rz,col,sceneDep,sceneCol);
col = lerp(col, fogb, smoothstep(FAR-7.,FAR,rz));
//then volumetric fog
col = Fog(col, ro, rd, rz,_FogCol,_FogSpd,_FogHighRange);
//post
col = pow(col,float3(0.8,0.8,0.8));
sceneCol.xyz = col;
return sceneCol;
}
ENDCG
}//end pass
}//end SubShader
FallBack Off
}