Unity Shader入门教程(十九):运动模糊

发表于2018-06-07
评论1 3k浏览
运动模糊是真实世界中摄像机的一种效果,如果摄像机曝光时,拍摄场景发生了变化,就会产生模糊的画面。

运动模糊的实现有多种方法。
1. 利用一块累积缓存(accumulation buffer)来混合多张连续的图像。当物体快速移动产生多张图像后,我们取它们之间的平均值作为最后的运动模糊图像。然而,这种暴力的方法对性能的消耗很大,因为想要获取多帧图像往往意味着我们需要在同一帧渲染多次场景。

2. 创建和使用速度缓存(velocity buffer),这个缓存中存储了各个像素当前的运动速度,然后利用该值来决定模糊的方向和大小。

代码实现:
using UnityEngine;
public class OwnMotionBlur : PostEffectsBase {
    public Shader motionBlurShader;
    private Material motionBlurMaterial = null;
    public Material material
    {
        get
        {
            motionBlurMaterial = CheckShaderAndCreateMaterial(motionBlurShader, motionBlurMaterial);
            return motionBlurMaterial;
        }
    }
    //定义运动模糊在混合图像时使用的模糊参数,blurAmount的值越大,运动拖尾的效果就越明显,为了防止拖尾效果
    //完全替代当前帧的渲染结果,我们把它的值截取在0.0~0.9范围内。
    [Range(0.0f, 0.9f)]
    public float blurAmount = 0.5f;
    //保存之前图像叠加的结果
    private RenderTexture accumulationTexture;
    private void OnDisable()
    {
        DestroyImmediate(accumulationTexture);
    }
    private void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        if(material != null)
        {
            if(accumulationTexture == null || accumulationTexture.width != source.width || accumulationTexture.height != source.height)
            {
                DestroyImmediate(accumulationTexture);
                accumulationTexture = new RenderTexture(source.width, source.height, 0);
                //这个变量不会显示在Hierarchy中,也不会保存在场景中
                accumulationTexture.hideFlags = HideFlags.HideAndDontSave;
                Graphics.Blit(source, accumulationTexture);
            }
            //表明我们需要进行一个渲染纹理的恢复操作。恢复操作发生在渲染到纹理而该纹理又没有被提前清空或销毁的清空的情况下
            accumulationTexture.MarkRestoreExpected();
            material.SetFloat("_BlurAmount", 1.0f - blurAmount);
            Graphics.Blit(source, accumulationTexture, material);
            Graphics.Blit(accumulationTexture, destination);
        }
        else
        {
            Graphics.Blit(source, destination);
        }
    }
}

Shader "Own/Chapter12-MotionBlur"
{
    Properties
    {
        //对应了输入的纹理
        _MainTex("Base (RGB)",2D) = "white"{}
        //混合图像时使用的混合系数
        _BlurAmount("Blur Amount",Float) = 1.0
    }
    SubShader
    {
        CGINCLUDE
        #include "UnityCG.cginc"
        sampler2D _MainTex;
        fixed _BlurAmount;
        struct v2f{
            float4 pos : SV_POSITION;
            half2 uv : TEXCOORD0;
        };
        v2f vert(appdata_img v){
            v2f o;
            o.pos = mul(UNITY_MATRIX_MVP,v.vertex);
            o.uv = v.texcoord;
            return o;
        }
        fixed4 fragRGB(v2f i):SV_Target{
            return fixed4(tex2D(_MainTex,i.uv).rgb,_BlurAmount);
        }
        half fragA(v2f i):SV_Target{
            return tex2D(_MainTex,i.uv);
        }
        ENDCG
        ZTest Always Cull Off ZWrite Off
        Pass{
            Blend SrcAlpha OneMinusSrcAlpha
            //ColorMask可以让我们制定渲染结果的输出通道,而不是通常情况下的RGBA这4个通道全部写入。可选参数是 RGBA 的任意组合以及 
            //0,这将意味着不会写入到任何通道,可以用来单独做一次Z测试,而不将结果写入颜色通道
            ColorMask RGB
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment fragRGB
            ENDCG
        }
        Pass{
            Blend One Zero
            ColorMask A
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment fragA
            ENDCG
        }
    }
    FallBack Off
}


如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引