美文网首页
Unity Shader实现运动模糊(二) : 物体运动产生模糊

Unity Shader实现运动模糊(二) : 物体运动产生模糊

作者: 上午八点 | 来源:发表于2018-12-14 15:05 被阅读0次

    上一篇 关于运动模糊的效果是根据VP矩阵重建世界空间坐标,然后根据上一帧和当前帧的位置差作为速度方向,这种方式需要摄像机有位移才能产生效果,如果摄像机静止不动,那么VP矩阵也就不会有变化,所以上一帧和当前帧在NDC空间的位置也没有变化,即speed==0,也就不会有运动模糊的效果,而想要在摄像机不动而物体移动时产生模糊效果,则需要另一种实现方式,这种方式更简洁直观。

    大致思路是:

    1. C#中记录运动物体的 当前帧的世界坐标 和 当前帧的VP矩阵
    2. C#中记录运动物体的 上一帧的世界坐标 和 上一帧的VP矩阵
    3. Shader中根据上面的信息求出 当前帧 和 上一帧的NDC坐标
    4. NDC坐标求差值得出速度方向,然后沿速度方向进行多次采样后求平均值

    这只是我自己的一个不成熟的实现思路,如果哪位大神有更合适的实现方式请一定赐教哈。

    这种实现方式不需要对深度纹理进行采样,因此摄像机也不用设置depthTextureMode。但是按照这个思路实现出来以后发现整个画面都被模糊了,不只是运动的物体,连背后静止的plane物体也被模糊了,原因在于使用了后处理,而后处理是对整个屏幕画面进行的处理,所以想要只对运动的物体进行模糊,那么需要单独使用一个摄像机来看运动的物体,Shader中需要写两个pass,第一个pass实现运动物体的模糊,第二个pass把主摄像机的画面和模糊后的画面叠加到一起,思路如下:

    1. 调用Shader的第一个pass,实现运动的物体被模糊,结果渲染到一张RenderTexture上
    2. 把这张RenderTexture作为纹理参数传递给Shader
    3. 把主摄像机的画面渲染到另一张RenderTexture上
    4. 调用Shader的第二个pass,把主摄像机的RT渲染到最终的RT上

    第二个pass中会用到第一个pass处理后的结果。

    摄像机相关的操作是:

    1. 把运动物体的layer设置到和静止物体不同的层,比如可以新建一个MovingObject层
    2. 新建一个摄像机,用来只看运动物体,设置culling mask = MovingObject,Depth 比主摄像机小,其余参数和主摄像机一致。
    3. 从主相机的culling mask中去除MovingObject层

    效果图:


    使用一个摄像机时,运动的Cube和静止的Plane都模糊了 使用两个摄像机后,只有运动的Cube是模糊的,静止的Plane是清晰的

    那么现在开始上代码,这样看起来可能更清晰些。

    C# 部分:

    using UnityEngine;
    
    // 运动模糊, 物体运动产生模糊效果 //
    public class MotionBlur_ObjectMove : MonoBehaviour
    {
        public Transform target;
        [Range(0, 2)]
        public float BlurSize;
    
        private Material m_mat;
        private const string ShaderName = "MJ/PostEffect/MotionBlur_ObjectMove";
        private Vector3 m_curWorldPos;                              // 当前帧的世界空间坐标 //
        private Vector3 m_lastWorldPos;                             // 上一帧的世界空间坐标 //
        private Matrix4x4 m_curVP;                                       // 当前帧的Vp矩阵 // 
        private Matrix4x4 m_lastVP;                                      // 上一帧的Vp矩阵 // 
        private Camera m_cam;
        private Camera m_mainCam;
    
        void Start()
        {
            if (target == null)
            {
                enabled = false;
                return;
            }
    
            Shader shader = Shader.Find(ShaderName);
            if (shader == null)
            {
                enabled = false;
                return;
            }
    
            m_mat = new Material(shader);
    
            m_cam = GetComponent<Camera>();
            if (m_cam == null)
            {
                enabled = false;
                return;
            }
    
            m_mainCam = Camera.main;
            if (m_mainCam == null)
            {
                enabled = false;
                return;
            }
        }
    
        void OnRenderImage(RenderTexture srcRT, RenderTexture dstRT)
        {
            if (m_mat == null || m_cam == null || target == null)
            {
                return;
            }
    
            RenderTexture mainCamRT = RenderTexture.GetTemporary(srcRT.width, srcRT.height, 24);
            RenderTexture blurRT = RenderTexture.GetTemporary(srcRT.width, srcRT.height, 24);
            
            m_mainCam.targetTexture = mainCamRT;
    
            m_curVP = m_cam.projectionMatrix * m_cam.worldToCameraMatrix;
            m_curWorldPos = target.position;
    
            m_mat.SetFloat("_BlurSize", BlurSize);
            m_mat.SetVector("_CurWorldPos", m_curWorldPos);
            m_mat.SetMatrix("_CurVP", m_curVP);
            m_mat.SetVector("_LastWorldPos", m_lastWorldPos);
            m_mat.SetMatrix("_LastVP", m_lastVP);
    
            m_lastWorldPos = m_curWorldPos;
            m_lastVP = m_curVP;
    
            // 运动模糊 //
            Graphics.Blit(srcRT, blurRT, m_mat, 0);
    
            m_mat.SetTexture("_BlurTex", blurRT);
            // 合并画面 //
            Graphics.Blit(mainCamRT, dstRT, m_mat, 1);
    
            RenderTexture.ReleaseTemporary(mainCamRT);
            RenderTexture.ReleaseTemporary(blurRT);
        }
    }
    

    Shader部分:

    Shader "MJ/PostEffect/MotionBlur_ObjectMove"
    {
        Properties
        {
            _MainTex ("Main Texture", 2D) = "white" {}
            _BlurSize("Blur Size", Range(0, 10)) = 1
        }
    
        CGINCLUDE
        #include "UnityCG.cginc"
        struct appdata
        {
            float4 vertex : POSITION;
            float2 uv : TEXCOORD0;
        };
    
        struct v2f
        {
            float4 vertex : SV_POSITION;
            float2 uv : TEXCOORD0;
        };
    
        sampler2D _MainTex;
        float2 _MainTex_TexelSize;
        float4 _MainTex_ST;
        
        uniform float _BlurSize;
        uniform float4 _CurWorldPos;
        uniform float4 _LastWorldPos;
        uniform float4x4 _CurVP;
        uniform float4x4 _LastVP;
        uniform sampler2D _BlurTex;             // 运动模糊后的画面 //
    
        v2f vert (appdata v)
        {
            v2f o;
            o.vertex = UnityObjectToClipPos(v.vertex);
            o.uv = TRANSFORM_TEX(v.uv, _MainTex);
            return o;
        }
    
        float4 frag1 (v2f i) : SV_Target
        {
            _CurWorldPos.w = 1;
            float4 curClipPos = mul(_CurVP, _CurWorldPos);
            float3 curNDCPos = curClipPos.rgb/curClipPos.w;
    
            _LastWorldPos.w = 1;
            float4 lastClipPos = mul(_LastVP, _LastWorldPos);
            float3 lastNDCPos = lastClipPos.rgb/lastClipPos.w;
    
            float2 speed = (curNDCPos.xy - lastNDCPos.xy)*0.5;              // 转到ndc空间做速度计算 //
            float4 finalColor = float4(0,0,0,0);                            // 有颜色的部分透明度大于0, 其余部分透明度等于0 //
            for(int j=0; j<4; j++)
            {
                float2 tempUV = i.uv+j*speed*_BlurSize;
                finalColor += tex2D(_MainTex, tempUV);
            }
            finalColor *= 0.25;
            return finalColor;
        }
    
        float4 frag2 (v2f i) : SV_Target
        {
            float4 mainTex = tex2D(_MainTex, i.uv);
            float4 blurTex = tex2D(_BlurTex, i.uv);
            float4 finalColor = float4(0,0,0,1);
            if(blurTex.a > 0)
            {
                finalColor.rgb = lerp(mainTex.rgb, blurTex.rgb, blurTex.a);
            }
            else
            {
                finalColor.rgb = mainTex.rgb;
            }
            return finalColor;
        }
        ENDCG
    
        SubShader
        {
            Tags { "Queue"="Geometry" "RenderType"="Opaque" "IgnoreProjector"="True" }
    
    
            Cull Off
            ZWrite Off
            ZTest Always
            
            LOD 100
    
            // #0 //
            Pass
            {
                CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag1
                ENDCG
            }
    
            // #1 //
            Pass
            {
                CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag2          
                ENDCG
            }
        }
        
        Fallback Off
    }
    

    package文件
    提取码:j1ld

    相关文章

      网友评论

          本文标题:Unity Shader实现运动模糊(二) : 物体运动产生模糊

          本文链接:https://www.haomeiwen.com/subject/endghqtx.html