积分解 Unity 高度雾
高度雾的基本模型为,对于某个高度 y 及一个高度区间 (S,E):
当 y ≤ S 时,某一点的雾浓度 T 为 最大值 1
当 y ≥ E 时,T = 0
当 S < y < E 时,
设光线从视点 v 出发,到达像素点 w(世界坐标),为一条线段,对这条线段所经过点的浓度求积分,即为该点雾的浓度
建立直角坐标系,原点为视点在 y=0 平面的投影,x 轴方向为 vw 在 y=0 平面的投影,y 轴向上,令 y=kx+b ,当 k=0 时直接计算,当 k≠0 时,沿线段 vw 对 T 求曲线积分
假设面积 0 点在 y=S 处,解积分得
float _FogEnd;
float _FogStart;
float _FogPower;
inline float integral(float x,float k,float b)
{
float s = _FogStart;
float e = _FogEnd;
float p = _FogPower;
float s_bk = (s - b)/k;
float y = k * x + b;
if (y <= s) return (x - s_bk);
if (y >= e) return (e - s) / ((p + 1) * k);
return (pow(e - s,p + 1) - pow(e - y,p + 1)) / (k * (p + 1) * pow(e - s, p));
}
inline float integralZeroK(float x,float b)
{
if (b >= _FogEnd) return 0;
if (b <= _FogStart) return x;
return x * pow((_FogEnd - b) / (_FogEnd - _FogStart),_FogPower);
}
float GetAltitudeFog(float3 worldPos)
{
float3 viewPos = _WorldSpaceCameraPos;
float3 distDir = worldPos - viewPos;
float hDist = sqrt(dot(distDir.xz,distDir.xz));
float vDist = distDir.y;
float k = vDist / hDist;
float b = viewPos.y;
if (k != 0)
{
return sqrt(1 + k * k) * (integral(hDist,k,b) - integral(0,k,b));
}
else
{
return sqrt(1 + k * k) * integralZeroK(hDist,b);
}
}
最后再以求出的积分浓度对颜色进行插值
lerp(color,_FogColor,saturate(GetVerticalFog(i.worldPos) * _FogColor.a));