UnityShader屏幕后处理-高斯模糊
2018-02-27 15:28
387 查看
原理:通过把一个像素(其实也不是像素,是纹素,Texel)及其周围8个像素按照一定权重相加求得该像素最终的颜色。因此该纹素的最终颜色很大程度上取决于该纹素周围的纹素的颜色,从而达到模糊的效果。
1 取得周围纹素uv
_BlurSize为模糊程度,如果选取的周围8个纹素距离目标纹素很远,则模糊程度更大。
2 权重相加
由于需要对横向的周围四个像素和纵向的周围四个像素分别按照权重相加,因此需要两个Pass做处理。当然可以放到一个Pass里面做。上面的v2f里面的uv[]长度是5个,如果放到一个Pass里面可以把长度拓展到9个,然后再vert()里面先求前5个横向,然后求纵向的5个。
因为分了两个Pass,所以对C#和Shader都会有影响。
1 Shader中因为两个Pass存在重复代码,因此使用CGINCLUDE、ENDCG来作为“头文件”,然后定义两个Pass中调用INCLUDE中的不同vert()函数就可以。
代码如下。
2 C#中需要调用两次Pass,因此需要一张纹理来暂存第一张纹理的结果。
比较高级的用法是:可以重复的模糊一张图像,得到更加模糊的效果
具体代码
PostEffectsBase.cs
GaussianBlur.cs
GaussianBlur.shader
1 取得周围纹素uv
_BlurSize为模糊程度,如果选取的周围8个纹素距离目标纹素很远,则模糊程度更大。
v2f vertBlurVertical(appdata_img v) { v2f o; o.pos = UnityObjectToClipPos(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 = UnityObjectToClipPos(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; }
2 权重相加
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); }
由于需要对横向的周围四个像素和纵向的周围四个像素分别按照权重相加,因此需要两个Pass做处理。当然可以放到一个Pass里面做。上面的v2f里面的uv[]长度是5个,如果放到一个Pass里面可以把长度拓展到9个,然后再vert()里面先求前5个横向,然后求纵向的5个。
因为分了两个Pass,所以对C#和Shader都会有影响。
1 Shader中因为两个Pass存在重复代码,因此使用CGINCLUDE、ENDCG来作为“头文件”,然后定义两个Pass中调用INCLUDE中的不同vert()函数就可以。
代码如下。
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) { ... return o; } v2f vertBlurHorizontal(appdata_img v) { ... return o; } fixed4 fragBlur(v2f i) : SV_Target { ... 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 } }
2 C#中需要调用两次Pass,因此需要一张纹理来暂存第一张纹理的结果。
void OnRenderImage(RenderTexture src, RenderTexture dest) { if (material != null) { int rtW = src.width; int rtH = src.height; RenderTexture buffer = RenderTexture.GetTemporary(rtW, rtH, 0); material.SetFloat("_BlurSize", 1.0f + blurSpread); // Render the vertical pass Graphics.Blit(src, buffer, material, 0); // Render the horizontal pass Graphics.Blit(buffer, dest, material, 1); //释放内存 RenderTexture.ReleaseTemporary(buffer); } else { Graphics.Blit(src, dest); } }
比较高级的用法是:可以重复的模糊一张图像,得到更加模糊的效果
void OnRenderImage (RenderTexture src, RenderTexture dest) { if (material != null) { int rtW = src.width/downSample; int rtH = src.height/downSample; RenderTexture buffer0 = RenderTexture.GetTemporary(rtW, rtH, 0); buffer0.filterMode = FilterMode.Bilinear; Graphics.Blit(src, buffer0); for (int i = 0; i < iterations; i++) { material.SetFloat("_BlurSize", 1.0f + i * blurSpread); RenderTexture buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0); // Render the vertical pass Graphics.Blit(buffer0, buffer1, material, 0); RenderTexture.ReleaseTemporary(buffer0); buffer0 = buffer1; buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0); // Render the horizontal pass Graphics.Blit(buffer0, buffer1, material, 1); RenderTexture.ReleaseTemporary(buffer0); buffer0 = buffer1; } Graphics.Blit(buffer0, dest); RenderTexture.ReleaseTemporary(buffer0); } else { Graphics.Blit(src, dest); } }
具体代码
PostEffectsBase.cs
using System.Collections; using System.Collections.Generic; using UnityEngine; [ExecuteInEditMode] [RequireComponent (typeof(Camera))] public class PostEffectsBase : MonoBehaviour { protected bool CheckSupport() { if (SystemInfo.supportsImageEffects == false) { Debug.LogWarning("This platform does not support image effects."); return false; } return true; } protected void NotSupported() { enabled = false; } protected void CheckResources() { bool isSupported = CheckSupport(); if (isSupported == false) { NotSupported(); } } protected void Start() { CheckResources(); } // 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; } } }
GaussianBlur.cs
using UnityEngine;
using System.Collections;
public class GaussianBlur : PostEffectsBase {
public Shader gaussianBlurShader;
private Material gaussianBlurMaterial = null;
public Material material {
get {
gaussianBlurMaterial = CheckShaderAndCreateMaterial(gaussianBlurShader, gaussianBlurMaterial);
return gaussianBlurMaterial;
}
}
// Blur iterations - larger number means more blur.
[Range(0, 4)]
public int iterations = 3;
// Blur spread for each iteration - larger value means more blur
[Range(0.2f, 3.0f)]
public float blurSpread = 0.6f;
[Range(1, 8)]
public int downSample = 2;
/// 1st edition: just apply blur
// void OnRenderImage(RenderTexture src, RenderTexture dest) {
// if (material != null) {
// int rtW = src.width;
// int rtH = src.height;
// RenderTexture buffer = RenderTexture.GetTemporary(rtW, rtH, 0);
// material.SetFloat("_BlurSize", 1.0f + blurSpread);
// // Render the vertical pass
// Graphics.Blit(src, buffer, material, 0);
// // Render the horizontal pass
// Graphics.Blit(buffer, dest, material, 1);
//
// RenderTexture.ReleaseTemporary(buffer);
// } else {
// Graphics.Blit(src, dest);
// }
// }
/// 2nd edition: scale the render texture
// 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;
// material.SetFloat("_BlurSize", 1.0f + blurSpread);
// // Render the vertical pass
// Graphics.Blit(src, buffer, material, 0);
// // Render the horizontal pass
// Graphics.Blit(buffer, dest, material, 1);
//
// RenderTexture.ReleaseTemporary(buffer);
// } else {
// Graphics.Blit(src, dest);
// }
// }
/// 3rd edition: use iterations for larger blur
void OnRenderImage (RenderTexture src, RenderTexture dest) { if (material != null) { int rtW = src.width/downSample; int rtH = src.height/downSample; RenderTexture buffer0 = RenderTexture.GetTemporary(rtW, rtH, 0); buffer0.filterMode = FilterMode.Bilinear; Graphics.Blit(src, buffer0); for (int i = 0; i < iterations; i++) { material.SetFloat("_BlurSize", 1.0f + i * blurSpread); RenderTexture buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0); // Render the vertical pass Graphics.Blit(buffer0, buffer1, material, 0); RenderTexture.ReleaseTemporary(buffer0); buffer0 = buffer1; buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0); // Render the horizontal pass Graphics.Blit(buffer0, buffer1, material, 1); RenderTexture.ReleaseTemporary(buffer0); buffer0 = buffer1; } Graphics.Blit(buffer0, dest); RenderTexture.ReleaseTemporary(buffer0); } else { Graphics.Blit(src, dest); } }
}
GaussianBlur.shader
Shader "Custom/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 = UnityObjectToClipPos(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 = UnityObjectToClipPos(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"
}
相关文章推荐
- unity使用屏幕后处理实现闪烁特效,创建新的shader文件过程
- UnityShader屏幕后处理-边缘检测,经典的插值lerp
- UnityShader屏幕后处理-Bloom效果(朦胧模糊)
- Unity_Shader高级篇_12.3_后处理_屏幕水波效果
- 【unity】【shader】屏幕高斯模糊(Gaussian Blur)后期特效的实现
- Unity Shader入门精要学习笔记 - 第12章 屏幕后处理效果
- Unity shader学习之屏幕后期处理效果之边缘检测
- UnityShader - 屏幕特效 - 高斯模糊(Gaussian Blur)
- Unity Shader-后处理:屏幕水波效果
- 在Unity的后处理shader中通过屏幕像素坐标和深度贴图反推世界坐标
- UnityShader屏幕后处理
- 【Unity Shader编程】之十五 屏幕高斯模糊(Gaussian Blur)后期特效的实现
- Unity Shader-后处理:Bloom全屏泛光
- Unity Shader-后处理:时空扭曲效果
- Unity中surfaceShader的处理机制和finalColor
- Unity Shader-后处理:径向模糊效果
- unity屏幕空间坐标shader
- Unity Shader-后处理:景深
- UnityShader实例13:屏幕特效之均值模糊(Box Blur)
- UnityShader 屏幕特效入门前