Unity Shader:在Unity中使用GlobalFog
发表于2018-03-20
全局雾 (globalfog) 图像效果可创建基于相机的指数雾,而Unity自带的 GlobalFog 通过一个屏幕特效实现了全局雾效,本篇文章就基于此给大家介绍下在Unity中使用GlobalFog。
Camera cam = GetComponent<Camera>(); Transform camtr = cam.transform; float camNear = cam.nearClipPlane; float camFar = cam.farClipPlane; float camFov = cam.fieldOfView; float camAspect = cam.aspect; Matrix4x4 frustumCorners = Matrix4x4.identity; float fovWHalf = camFov * 0.5f; Vector3 toRight = camtr.right * camNear * Mathf.Tan (fovWHalf * Mathf.Deg2Rad) * camAspect; Vector3 toTop = camtr.up * camNear * Mathf.Tan (fovWHalf * Mathf.Deg2Rad); Vector3 topLeft = (camtr.forward * camNear - toRight + toTop); float camScale = topLeft.magnitude * camFar/camNear; topLeft.Normalize(); topLeft *= camScale; Vector3 topRight = (camtr.forward * camNear + toRight + toTop); topRight.Normalize(); topRight *= camScale; Vector3 bottomRight = (camtr.forward * camNear + toRight - toTop); bottomRight.Normalize(); bottomRight *= camScale; Vector3 bottomLeft = (camtr.forward * camNear - toRight - toTop); bottomLeft.Normalize(); bottomLeft *= camScale; frustumCorners.SetRow (0, topLeft); frustumCorners.SetRow (1, topRight); frustumCorners.SetRow (2, bottomRight); frustumCorners.SetRow (3, bottomLeft);
首先 Unity 通过上面一大堆代码算出了相机在远截面的四个角的位置,实际上用下面的函数可以代替:
Vector3[] corners = new Vector3[4]; cam.CalculateFrustumCorners(new Rect(0, 0, 1, 1),cam.farClipPlane , Camera.MonoOrStereoscopicEye.Mono, corners);

接下来的 CustomGraphicsBlit 函数就是核心所在
static void CustomGraphicsBlit (RenderTexture source, RenderTexture dest, Material fxMaterial, int passNr) { RenderTexture.active = dest; fxMaterial.SetTexture ("_MainTex", source); GL.PushMatrix (); GL.LoadOrtho (); fxMaterial.SetPass (passNr); GL.Begin (GL.QUADS); GL.MultiTexCoord2 (0, 0.0f, 0.0f); GL.Vertex3 (0.0f, 0.0f, 3.0f); // BL GL.MultiTexCoord2 (0, 1.0f, 0.0f); GL.Vertex3 (1.0f, 0.0f, 2.0f); // BR GL.MultiTexCoord2 (0, 1.0f, 1.0f); GL.Vertex3 (1.0f, 1.0f, 1.0f); // TR GL.MultiTexCoord2 (0, 0.0f, 1.0f); GL.Vertex3 (0.0f, 1.0f, 0.0f); // TL GL.End (); GL.PopMatrix (); }
利用 GL 库绘制了一个四边形,在 XY 平面,四边形刚好覆盖整个屏幕,Z 坐标则对应了 frustumCorners 矩阵中的行,并在 vertex shader 中赋值
o.interpolatedRay = _FrustumCornersWS[(int)index]; o.interpolatedRay.w = index;
于是,经过光栅化的插值以后 interpolatedRay 则是由相机出发射向远截面的每一像素的向量,乘上深度纹理的值后,通过脚本传入的相机世界坐标,便可得到屏幕上每个像素点的世界坐标
float rawDepth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, UnityStereoTransformScreenSpaceTex(i.uv_depth)); float dpth = Linear01Depth(rawDepth); float4 wsDir = dpth * i.interpolatedRay; float4 wsPos = _CameraWS + wsDir;
至此,该有的条件都有了,便可以计算雾了,距离雾的计算较为简单,高度雾的计算则完全采用了 论文中的方法,连变量名都没改。
2、修改
根据论文的计算方法,在高度计算中加入了噪声,这里就没有考虑效率了,并且只是简单的将原本高度替换为交电处的噪声采样高度
//custom if(FdotC * FdotP < 0.0) { float x = FdotC/V.y * V.x; float z = FdotC/V.y * V.z; float2 spos = C.xz + float2(x,z); spos *= 0.1; float NH = tex2D(_NoiseTex, spos + float2(_Time.x,0.0)).r * _NoiseScale; float differ = NH - FH; FdotC += differ; FdotP += differ; if(FdotC <= 0.0) k = 1.0; else k = 0.0; }
效果如下:

同样的在距离雾中加入了噪声
float3 wpos = camDir/dist; float ND = tex2D(_NoiseTex, wpos.xy + float2(_Time.x,0.0)).r; dist -= pow(ND,5) * _NoiseScaleDis; return max(dist,0.0);
效果如下:

两者一起效果如下:

3、天空盒 Blend

改进的全局雾效果