## BrightnessSaturationAndContrast ### 给摄像机添加脚本 添加2个Meta,让其可以在编辑器模式下运行,并且只能绑定Camera组件。 ``` [ExecuteInEditMode] [RequireComponent(typeof(Camera))] ``` 实现基础类 ``` using UnityEngine; [ExecuteInEditMode] [RequireComponent(typeof(Camera))] public class PostEffectsBase : MonoBehaviour { protected void CheckResources() { bool isSupported = CheckSupport(); if (isSupported == false) { NotSupported(); } } protected bool CheckSupport() { if (SystemInfo.supportsImageEffects == false || SystemInfo.supportsRenderTextures == false) { Debug.LogWarning("Not Supported!"); return false; } return true; } protected void NotSupported() { enabled = false; } 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; } } protected void Start() { CheckResources(); } } ``` 之后根据需求在子类中添加变量: ``` using UnityEngine; public class BrightnessSaturationAndContrast : PostEffectsBase { [Range(0.0f, 3.0f)] public float brightness = 1.0f; [Range(0.0f, 3.0f)] public float saturation = 1.0f; [Range(0.0f, 3.0f)] public float contrast = 1.0f; public Shader briSatConShader; private Material briSatConMaterial; public Material material { get { briSatConMaterial = CheckShaderAndCreateMaterial(briSatConShader, briSatConMaterial); return briSatConMaterial; } } private void OnRenderImage(RenderTexture src, RenderTexture dest) { if (material != null) { material.SetFloat("_Brightness", brightness); material.SetFloat("_Saturation", saturation); material.SetFloat("_Contrast", contrast); Graphics.Blit(src, dest, material); } else { Graphics.Blit(src, dest); } } } ``` 最后在OnRenderImage中调用Graphics.Blit()进行渲染。 ### 添加后处理Shader ``` Shader "PostProcess/BrightnessSaturationAndContrast" { Properties { _MainTex ("Base", 2D) = "white" {} _Brightness("Brightness",Float)=1 _Saturation("Saturation",Float)=1 _Contrast("Constrast",Float)=1 } SubShader { Pass{ ZTest Always Cull Off ZWrite Off CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" sampler2D _MainTex; half _Brightness; half _Saturation; half _Contrast; struct v2f{ float4 pos : SV_POSITION; half2 uv : TEXCOORD0; }; v2f vert(appdata_img v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.uv=v.texcoord; return o; } fixed4 frag(v2f i) : SV_Target{ fixed4 renderTex=tex2D(_MainTex,i.uv); fixed3 finalColor=renderTex.rgb * _Brightness; fixed luminance=0.2125*renderTex.r+0.7154*renderTex.g+0.0721*renderTex.b; fixed3 luminanceColor=fixed3(luminance,luminance,luminance); finalColor =lerp(luminanceColor,finalColor,_Saturation); fixed3 avgColor=fixed3(0.5,0.5,0.5); finalColor=lerp(avgColor,finalColor,_Contrast); return fixed4(finalColor,renderTex.a); } ENDCG } } Fallback Off } ``` ## 高斯模糊 与之前不同,这里利用RenderTexture.GetTemporary函数分配了一块与屏幕图像大小相同的缓冲区。这是因为,高斯模糊需要调用两个Pass,我们需要使用一块中间缓存来存储第一个Pass执行完毕后得到的模糊结果。`RenderTexture buffer = RenderTexture.GetTemporary(rtW, rtH, 0);`我们首先调用`Graphics.Blit(src, buffer, material, 0),`使用Shader中的第一个pass对src进行处理,并将结果存储在了buffer中。然后在调用`Graphics.Blit(src, buffer, material, 1)`,使用Shader中的第二个Pass对buffer进行处理,返回最终的屏幕图像。最后,我们还需要调用`RenderTexture.ReleaseTemporary`来释放之前分配的缓存。 ``` private void OnRenderImage(RenderTexture src, RenderTexture dest) { if (material != null) { int rtW = src.width; int rtH = src.height; RenderTexture buffer = RenderTexture.GetTemporary(rtW, rtH, 0); Graphics.Blit(src, buffer, material, 0); Graphics.Blit(buffer, dest, material, 1); RenderTexture.Release(buffer); } else { Graphics.Blit(src, dest); } } ``` 实现降采样: ``` private void OnRenderImage(RenderTexture src, RenderTexture dest) { if (material != null) { int rtW = src.width / downSample; int rtH = src.height / downSample; RenderTexture buffer = RenderTexture.GetTemporary(rtW, rtH, 0); buffer.filterMode=FilterMode.Bilinear; Graphics.Blit(src, buffer, material, 0); Graphics.Blit(buffer, dest, material, 1); RenderTexture.Release(buffer); } else { Graphics.Blit(src, dest); } } ``` ### CGINCLUDE 使用CGINCLUDE与ENDCG关键字将通用部分引用给其他Pass ``` Shader "Unity Shaders Book/Chapter 12/Gaussian Blur" { Properties { _MainTex ("Base (RGB)", 2D) = "white" {} _BlurSize ("Blur Size", Float) = 1.0 } SubShader { CGINCLUDE #include "UnityCG.cginc" sampler2D _MainTex; half4 _MainTex_TexelSize; float _BlurSize; struct v2f { float4 pos : SV_POSITION; half2 uv[5]: TEXCOORD0; }; v2f vertBlurVertical(appdata_img v) { v2f o; o.pos = mul(UNITY_MATRIX_MVP, v.vertex); half2 uv = v.texcoord; o.uv[0] = uv; o.uv[1] = uv + float2(0.0, _MainTex_TexelSize.y * 1.0) * _BlurSize; o.uv[2] = uv - float2(0.0, _MainTex_TexelSize.y * 1.0) * _BlurSize; o.uv[3] = uv + float2(0.0, _MainTex_TexelSize.y * 2.0) * _BlurSize; o.uv[4] = uv - float2(0.0, _MainTex_TexelSize.y * 2.0) * _BlurSize; return o; } v2f vertBlurHorizontal(appdata_img v) { v2f o; o.pos = mul(UNITY_MATRIX_MVP, v.vertex); half2 uv = v.texcoord; o.uv[0] = uv; o.uv[1] = uv + float2(_MainTex_TexelSize.x * 1.0, 0.0) * _BlurSize; o.uv[2] = uv - float2(_MainTex_TexelSize.x * 1.0, 0.0) * _BlurSize; o.uv[3] = uv + float2(_MainTex_TexelSize.x * 2.0, 0.0) * _BlurSize; o.uv[4] = uv - float2(_MainTex_TexelSize.x * 2.0, 0.0) * _BlurSize; return o; } fixed4 fragBlur(v2f i) : SV_Target { float weight[3] = {0.4026, 0.2442, 0.0545}; fixed3 sum = tex2D(_MainTex, i.uv[0]).rgb * weight[0]; for (int it = 1; it < 3; it++) { sum += tex2D(_MainTex, i.uv[it*2-1]).rgb * weight[it]; sum += tex2D(_MainTex, i.uv[it*2]).rgb * weight[it]; } return fixed4(sum, 1.0); } ENDCG ZTest Always Cull Off ZWrite Off Pass { NAME "GAUSSIAN_BLUR_VERTICAL" CGPROGRAM #pragma vertex vertBlurVertical #pragma fragment fragBlur ENDCG } Pass { NAME "GAUSSIAN_BLUR_HORIZONTAL" CGPROGRAM #pragma vertex vertBlurHorizontal #pragma fragment fragBlur ENDCG } } FallBack "Diffuse" } ```