【Unity Shader编程】之十五 屏幕高斯模糊(Gaussian Blur)后期特效的实现
using UnityEngine; using System.Collections; //设置在编辑模式下也执行该脚本 [ExecuteInEditMode] //添加选项到菜单中 [AddComponentMenu("Learning Unity Shader/Lecture 15/RapidBlurEffect")] public class RapidBlurEffect:MonoBehaviour { //-------------------变量声明部分------------------- #region Variables //指定Shader名称 private string ShaderName = "Learning Unity Shader/Lecture 15/RapidBlurEffect"; //着色器和材质实例 public Shader CurShader; private Material CurMaterial; //几个用于调节参数的中间变量 public static int ChangeValue; public static float ChangeValue2; public static int ChangeValue3; //降采样次数 [Range(0,6), Tooltip("[降采样次数]向下采样的次数。此值越大,则采样间隔越大,需要处理的像素点越少,运行速度越快。")] public int DownSampleNum = 2; //模糊扩散度 [Range(0.0f,20.0f), Tooltip("[模糊扩散度]进行高斯模糊时,相邻像素点的间隔。此值越大相邻像素间隔越远,图像越模糊。但过大的值会导致失真。")] public float BlurSpreadSize = 3.0f; //迭代次数 [Range(0,8), Tooltip("[迭代次数]此值越大,则模糊操作的迭代次数越多,模糊效果越好,但消耗越大。")] public int BlurIterations = 3; #endregion //-------------------------材质的get&set---------------------------- #region MaterialGetAndSet Material material { get { if(CurMaterial == null) { CurMaterial = new Material(CurShader); CurMaterial.hideFlags = HideFlags.HideAndDontSave; } return CurMaterial; } } #endregion #region Functions //-----------------------------------------【Start()函数】--------------------------------------------- // 说明:此函数仅在Update函数第一次被调用前被调用 //-------------------------------------------------------------------------------------------------------- void Start() { //依次赋值 ChangeValue = DownSampleNum; ChangeValue2 = BlurSpreadSize; ChangeValue3 = BlurIterations; //找到当前的Shader文件 CurShader = Shader.Find(ShaderName); //判断当前设备是否支持屏幕特效 if(!SystemInfo.supportsImageEffects) { enabled = false; return; } } //-------------------------------------【OnRenderImage()函数】------------------------------------ // 说明:此函数在当完成所有渲染图片后被调用,用来渲染图片后期效果 //-------------------------------------------------------------------------------------------------------- void OnRenderImage(RenderTexture sourceTexture,RenderTexture destTexture) { //着色器实例不为空,就进行参数设置 if(CurShader != null) { //【0】参数准备 //根据向下采样的次数确定宽度系数。用于控制降采样后相邻像素的间隔 float widthMod = 1.0f / (1.0f * (1 << DownSampleNum)); //Shader的降采样参数赋值 material.SetFloat("_DownSampleValue",BlurSpreadSize * widthMod); //设置渲染模式:双线性 sourceTexture.filterMode = FilterMode.Bilinear; //通过右移,准备长、宽参数值 int renderWidth = sourceTexture.width >> DownSampleNum; int renderHeight = sourceTexture.height >> DownSampleNum; // 【1】处理Shader的通道0,用于降采样 ||Pass 0,for down sample //准备一个缓存renderBuffer,用于准备存放最终数据 RenderTexture renderBuffer = RenderTexture.GetTemporary(renderWidth,renderHeight,0,sourceTexture.format); //设置渲染模式:双线性 renderBuffer.filterMode = FilterMode.Bilinear; //拷贝sourceTexture中的渲染数据到renderBuffer,并仅绘制指定的pass0的纹理数据 Graphics.Blit(sourceTexture,renderBuffer,material,0); //【2】根据BlurIterations(迭代次数),来进行指定次数的迭代操作 for(int i = 0;i < BlurIterations;i++) { //【2.1】Shader参数赋值 //迭代偏移量参数 float iterationOffs = (i * 1.0f); //Shader的降采样参数赋值 material.SetFloat("_DownSampleValue",BlurSpreadSize * widthMod + iterationOffs); // 【2.2】处理Shader的通道1,垂直方向模糊处理 || Pass1,for vertical blur // 定义一个临时渲染的缓存tempBuffer RenderTexture tempBuffer = RenderTexture.GetTemporary(renderWidth,renderHeight,0,sourceTexture.format); // 拷贝renderBuffer中的渲染数据到tempBuffer,并仅绘制指定的pass1的纹理数据 Graphics.Blit(renderBuffer,tempBuffer,material,1); // 清空renderBuffer RenderTexture.ReleaseTemporary(renderBuffer); // 将tempBuffer赋给renderBuffer,此时renderBuffer里面pass0和pass1的数据已经准备好 renderBuffer = tempBuffer; // 【2.3】处理Shader的通道2,竖直方向模糊处理 || Pass2,for horizontal blur // 获取临时渲染纹理 tempBuffer = RenderTexture.GetTemporary(renderWidth,renderHeight,0,sourceTexture.format); // 拷贝renderBuffer中的渲染数据到tempBuffer,并仅绘制指定的pass2的纹理数据 Graphics.Blit(renderBuffer,tempBuffer,CurMaterial,2); //【2.4】得到pass0、pass1和pass2的数据都已经准备好的renderBuffer // 再次清空renderBuffer RenderTexture.ReleaseTemporary(renderBuffer); // 再次将tempBuffer赋给renderBuffer,此时renderBuffer里面pass0、pass1和pass2的数据都已经准备好 renderBuffer = tempBuffer; } //拷贝最终的renderBuffer到目标纹理,并绘制所有通道的纹理到屏幕 Graphics.Blit(renderBuffer,destTexture); //清空renderBuffer RenderTexture.ReleaseTemporary(renderBuffer); } //着色器实例为空,直接拷贝屏幕上的效果。此情况下是没有实现屏幕特效的 else { //直接拷贝源纹理到目标渲染纹理 Graphics.Blit(sourceTexture,destTexture); } } //-----------------------------------------【OnValidate()函数】-------------------------------------- // 说明:此函数在编辑器中该脚本的某个值发生了改变后被调用 //-------------------------------------------------------------------------------------------------------- void OnValidate() { //将编辑器中的值赋值回来,确保在编辑器中值的改变立刻让结果生效 ChangeValue = DownSampleNum; ChangeValue2 = BlurSpreadSize; ChangeValue3 = BlurIterations; } //-----------------------------------------【Update()函数】-------------------------------------- // 说明:此函数每帧都会被调用 //-------------------------------------------------------------------------------------------------------- void Update() { //若程序在运行,进行赋值 if(Application.isPlaying) { //赋值 DownSampleNum = ChangeValue; BlurSpreadSize = ChangeValue2; BlurIterations = ChangeValue3; } //若程序没有在运行,去寻找对应的Shader文件 #if UNITY_EDITOR if(Application.isPlaying != true) { CurShader = Shader.Find(ShaderName); } #endif } //-----------------------------------------【OnDisable()函数】--------------------------------------- // 说明:当对象变为不可用或非激活状态时此函数便被调用 //-------------------------------------------------------------------------------------------------------- void OnDisable() { if(CurMaterial) { //立即销毁材质实例 DestroyImmediate(CurMaterial); } } #endregion }
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)' Shader "Learning Unity Shader/Lecture 15/RapidBlurEffect" { //-----------------------------------【属性 || Properties】------------------------------------------ Properties { //主纹理 _MainTex("Base (RGB)", 2D) = "white" {} } //----------------------------------【子着色器 || SubShader】--------------------------------------- SubShader { ZWrite Off Blend Off //---------------------------------------【通道0 || Pass 0】------------------------------------ //通道0:降采样通道 ||Pass 0: Down Sample Pass Pass { ZTest Off Cull Off CGPROGRAM //指定此通道的顶点着色器为vert_DownSmpl #pragma vertex vert_DownSmpl //指定此通道的像素着色器为frag_DownSmpl #pragma fragment frag_DownSmpl ENDCG } //---------------------------------------【通道1 || Pass 1】------------------------------------ //通道1:垂直方向模糊处理通道 ||Pass 1: Vertical Pass Pass { ZTest Always Cull Off CGPROGRAM //指定此通道的顶点着色器为vert_BlurVertical #pragma vertex vert_BlurVertical //指定此通道的像素着色器为frag_Blur #pragma fragment frag_Blur ENDCG } //---------------------------------------【通道2 || Pass 2】------------------------------------ //通道2:水平方向模糊处理通道 ||Pass 2: Horizontal Pass Pass { ZTest Always Cull Off CGPROGRAM //指定此通道的顶点着色器为vert_BlurHorizontal #pragma vertex vert_BlurHorizontal //指定此通道的像素着色器为frag_Blur #pragma fragment frag_Blur ENDCG } } //-------------------------CG着色语言声明部分 || Begin CG Include Part---------------------- CGINCLUDE //【1】头文件包含 || include #include "UnityCG.cginc" //【2】变量声明 || Variable Declaration sampler2D _MainTex; //UnityCG.cginc中内置的变量,纹理中的单像素尺寸|| it is the size of a texel of the texture uniform half4 _MainTex_TexelSize; //C#脚本控制的变量 || Parameter uniform half _DownSampleValue; //【3】顶点输入结构体 || Vertex Input Struct struct VertexInput { //顶点位置坐标 float4 vertex : POSITION; //一级纹理坐标 half2 texcoord : TEXCOORD0; }; //【4】降采样输出结构体 || Vertex Input Struct struct VertexOutput_DownSmpl { //像素位置坐标 float4 pos : SV_POSITION; //一级纹理坐标(右上) half2 uv20 : TEXCOORD0; //二级纹理坐标(左下) half2 uv21 : TEXCOORD1; //三级纹理坐标(右下) half2 uv22 : TEXCOORD2; //四级纹理坐标(左上) half2 uv23 : TEXCOORD3; }; //【5】准备高斯模糊权重矩阵参数7x4的矩阵 || Gauss Weight static const half4 GaussWeight[7] = { half4(0.0205,0.0205,0.0205,0), half4(0.0855,0.0855,0.0855,0), half4(0.232,0.232,0.232,0), half4(0.324,0.324,0.324,1), half4(0.232,0.232,0.232,0), half4(0.0855,0.0855,0.0855,0), half4(0.0205,0.0205,0.0205,0) }; //【6】顶点着色函数 || Vertex Shader Function VertexOutput_DownSmpl vert_DownSmpl(VertexInput v) { //【6.1】实例化一个降采样输出结构 VertexOutput_DownSmpl o; //【6.2】填充输出结构 //将三维空间中的坐标投影到二维窗口 o.pos = UnityObjectToClipPos(v.vertex); //对图像的降采样:取像素上下左右周围的点,分别存于四级纹理坐标中 o.uv20 = v.texcoord + _MainTex_TexelSize.xy * half2(0.5h, 0.5h);; o.uv21 = v.texcoord + _MainTex_TexelSize.xy * half2(-0.5h, -0.5h); o.uv22 = v.texcoord + _MainTex_TexelSize.xy * half2(0.5h, -0.5h); o.uv23 = v.texcoord + _MainTex_TexelSize.xy * half2(-0.5h, 0.5h); //【6.3】返回最终的输出结果 return o; } //【7】片段着色函数 || Fragment Shader Function fixed4 frag_DownSmpl(VertexOutput_DownSmpl i) : SV_Target { //【7.1】定义一个临时的颜色值 fixed4 color = (0,0,0,0); //【7.2】四个相邻像素点处的纹理值相加 color += tex2D(_MainTex, i.uv20); color += tex2D(_MainTex, i.uv21); color += tex2D(_MainTex, i.uv22); color += tex2D(_MainTex, i.uv23); //【7.3】返回最终的平均值 return color / 4; } //【8】顶点输入结构体 || Vertex Input Struct struct VertexOutput_Blur { //像素坐标 float4 pos : SV_POSITION; //一级纹理(纹理坐标) half4 uv : TEXCOORD0; //二级纹理(偏移量) half2 offset : TEXCOORD1; }; //【9】顶点着色函数 || Vertex Shader Function VertexOutput_Blur vert_BlurHorizontal(VertexInput v) { //【9.1】实例化一个输出结构 VertexOutput_Blur o; //【9.2】填充输出结构 //将三维空间中的坐标投影到二维窗口 o.pos = UnityObjectToClipPos(v.vertex); //纹理坐标 o.uv = half4(v.texcoord.xy, 1, 1); //计算X方向的偏移量 o.offset = _MainTex_TexelSize.xy * half2(1.0, 0.0) * _DownSampleValue; //【9.3】返回最终的输出结果 return o; } //【10】顶点着色函数 || Vertex Shader Function VertexOutput_Blur vert_BlurVertical(VertexInput v) { //【10.1】实例化一个输出结构 VertexOutput_Blur o; //【10.2】填充输出结构 //将三维空间中的坐标投影到二维窗口 o.pos = UnityObjectToClipPos(v.vertex); //纹理坐标 o.uv = half4(v.texcoord.xy, 1, 1); //计算Y方向的偏移量 o.offset = _MainTex_TexelSize.xy * half2(0.0, 1.0) * _DownSampleValue; //【10.3】返回最终的输出结果 return o; } //【11】片段着色函数 || Fragment Shader Function half4 frag_Blur(VertexOutput_Blur i) : SV_Target { //【11.1】获取原始的uv坐标 half2 uv = i.uv.xy; //【11.2】获取偏移量 half2 OffsetWidth = i.offset; //从中心点偏移3个间隔,从最左或最上开始加权累加 half2 uv_withOffset = uv - OffsetWidth * 3.0; //【11.3】循环获取加权后的颜色值 half4 color = 0; for (int j = 0; j < 7; j++) { //偏移后的像素纹理值 half4 texCol = tex2D(_MainTex, uv_withOffset); //待输出颜色值+=偏移后的像素纹理值 x 高斯权重 color += texCol * GaussWeight[j]; //移到下一个像素处,准备下一次循环加权 uv_withOffset += OffsetWidth; } //【11.4】返回最终的颜色值 return color; } //-------------------结束CG着色语言声明部分 || End CG Programming Part------------------ ENDCG FallBack Off }
本系列文章由@浅墨_毛星云 出品,转载请注明出处。
文章链接: http://blog.csdn.net/poem_qianmo/article/details/51871531
作者:毛星云(浅墨) 微博:http://weibo.com/u/1723155442
posted on 2023-04-28 15:20 zqiang0803 阅读(251) 评论(0) 编辑 收藏 举报