Shader教程:基于高度的雾效实现
发表于2018-03-28
学习Unity的都知道,Unity可以进行基本的雾效模拟,其中Unity里设置雾效有两种方式,一种最简单的就是直接开启全局雾效,也就是在Edit->Render Settings里配置。本篇文章就和大家介绍下基于高度的雾效实现。
代码如下:
using UnityEngine; using System.Collections; [ExecuteInEditMode] [RequireComponent (typeof(Camera))] public class PostEffectsBase : MonoBehaviour { protected void Start() { CheckResources(); } // Called when start protected void CheckResources() { bool isSupported = CheckSupport(); if (isSupported == false) { NotSupported(); } } // Called in CheckResources to check support on this platform protected bool CheckSupport() { if (SystemInfo.supportsImageEffects == false || SystemInfo.supportsRenderTextures == false) { Debug.LogWarning("This platform does not support image effects or render textures."); return false; } return true; } // Called when the platform doesn't support this effect protected void NotSupported() { enabled = false; } // Called when need to create the material used by this effect protected Material CheckShaderAndCreateMaterial(Shader shader, Material material) { if (shader == null) { return null; } if (shader.isSupported && material && material.shader == shader) return material; if (!shader.isSupported) { return null; } else { material = new Material(shader); material.hideFlags = HideFlags.DontSave; if (material) return material; else return null; } } }
using UnityEngine; using System.Collections; public class FogWithDepthTexture : PostEffectsBase { public Shader fogShader; private Material fogMaterial = null; public Material material { get { fogMaterial = CheckShaderAndCreateMaterial(fogShader, fogMaterial); return fogMaterial; } } private Camera myCamera; public Camera camera { get { if (myCamera == null) { myCamera = GetComponent<Camera>(); } return myCamera; } } private Transform myCameraTransform; public Transform cameraTransform { get { if (myCameraTransform == null) { myCameraTransform = camera.transform; } return myCameraTransform; } } [Range(0.0f, 3.0f)] public float fogDensity = 1.0f; public Color fogColor = Color.white; public float fogStart = 0.0f; public float fogEnd = 2.0f; void OnEnable() { camera.depthTextureMode |= DepthTextureMode.Depth; } void OnRenderImage (RenderTexture src, RenderTexture dest) { if (material != null) { Matrix4x4 frustumCorners = Matrix4x4.identity; float fov = camera.fieldOfView; float near = camera.nearClipPlane; float aspect = camera.aspect; float halfHeight = near * Mathf.Tan(fov * 0.5f * Mathf.Deg2Rad); Vector3 toRight = cameraTransform.right * halfHeight * aspect; Vector3 toTop = cameraTransform.up * halfHeight; Vector3 topLeft = cameraTransform.forward * near + toTop - toRight; float scale = topLeft.magnitude / near; topLeft.Normalize(); topLeft *= scale; Vector3 topRight = cameraTransform.forward * near + toRight + toTop; topRight.Normalize(); topRight *= scale; Vector3 bottomLeft = cameraTransform.forward * near - toTop - toRight; bottomLeft.Normalize(); bottomLeft *= scale; Vector3 bottomRight = cameraTransform.forward * near + toRight - toTop; bottomRight.Normalize(); bottomRight *= scale; frustumCorners.SetRow(0, bottomLeft); frustumCorners.SetRow(1, bottomRight); frustumCorners.SetRow(2, topRight); frustumCorners.SetRow(3, topLeft); material.SetMatrix("_FrustumCornersRay", frustumCorners); material.SetFloat("_FogDensity", fogDensity); material.SetColor("_FogColor", fogColor); material.SetFloat("_FogStart", fogStart); material.SetFloat("_FogEnd", fogEnd); Graphics.Blit (src, dest, material); } else { Graphics.Blit(src, dest); } } }
Shader "Fog With Depth Texture" { Properties { _MainTex ("Base (RGB)", 2D) = "white" {} _FogDensity ("Fog Density", Float) = 1.0 _FogColor ("Fog Color", Color) = (1, 1, 1, 1) _FogStart ("Fog Start", Float) = 0.0 _FogEnd ("Fog End", Float) = 1.0 } SubShader { CGINCLUDE #include "UnityCG.cginc" // 脚本传递过来这个参数 // (0, bottomLeft); // (1, bottomRight); // (2, topRight); // (3, topLeft); float4x4 _FrustumCornersRay; sampler2D _MainTex; half4 _MainTex_TexelSize; sampler2D _CameraDepthTexture; half _FogDensity; fixed4 _FogColor; float _FogStart; float _FogEnd; struct v2f { float4 pos : SV_POSITION; half2 uv : TEXCOORD0; half2 uv_depth : TEXCOORD1; // 保存了插值后的像素向量 float4 interpolatedRay : TEXCOORD2; }; v2f vert(appdata_img v) { v2f o; o.pos = mul(UNITY_MATRIX_MVP, v.vertex); o.uv = v.texcoord; o.uv_depth = v.texcoord; // unity左下角0,0 右上角1,1 //判断是否是directx平台,如果开启了抗锯齿,如果此时使用了渲染到纹理技术 //那么此时纹理不会翻转,在顶点着色器中加入这样的处理 #if UNITY_UV_STARTS_AT_TOP if (_MainTex_TexelSize.y < 0) o.uv_depth.y = 1 - o.uv_depth.y; #endif int index = 0; if (v.texcoord.x < 0.5 && v.texcoord.y < 0.5) { index = 0; } else if (v.texcoord.x > 0.5 && v.texcoord.y < 0.5) { index = 1; } else if (v.texcoord.x > 0.5 && v.texcoord.y > 0.5) { index = 2; } else { index = 3; } #if UNITY_UV_STARTS_AT_TOP if (_MainTex_TexelSize.y < 0) index = 3 - index; #endif // 插值后的像素向量 o.interpolatedRay = _FrustumCornersRay[index]; return o; } fixed4 frag(v2f i) : SV_Target { // LinearEyeDepth:视角空间下的线性深度值, // SAMPLE_DEPTH_TEXTURE:深度纹理采样 float linearDepth = LinearEyeDepth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv_depth)); // 1.摄像机的世界空间中的位置 // 2.空间世界下该像素相对于摄像机的偏移量,把他们相加,得到的就是该像素的世界坐标 float3 worldPos = _WorldSpaceCameraPos + linearDepth * i.interpolatedRay.xyz; // 雾的浓度 float fogDensity = (_FogEnd - worldPos.y) / (_FogEnd - _FogStart); // 限制到0~1之间 fogDensity = saturate(fogDensity * _FogDensity); fixed4 finalColor = tex2D(_MainTex, i.uv); // 雾的浓度=0的时候,显示原颜色 finalColor.rgb = lerp(finalColor.rgb, _FogColor.rgb, fogDensity); return finalColor; } ENDCG Pass { ZTest Always Cull Off ZWrite Off CGPROGRAM #pragma vertex vert #pragma fragment frag ENDCG } } FallBack Off }