运动相机的简易深度模糊基于深度纹理——UnityShader学习笔记

博主分享学习某效果的经历,在矩阵转换方面遇到困难,查阅资料也未能完全理解其转换意义,还有一些小问题不得其解。该效果可能仅用于练习,无法用于实际项目,博主表示这节学习挫败感强,但会继续坚持。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


自言自语

这个效果学习的我真的是各种难受,矩阵转换来转换去,到现在也没有理解其全部转换的意义。看不懂啊查阅资料也没能懂。另外还有一两点小问题 也是不得其解。只能先如此记忆了。。
另外这个效果可能也只是练习用的,作为motionblur并不咋地。。也没法用于项目实际中。可能只是想让我们学习这两种矩阵和几个空间中转换的原理。

然而 我等于没学会,真的转来转去转懵逼了。我脑子这的是。。。 服了我自己了

效果吧

这恶心的效果

C#部分

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[ExecuteInEditMode]
public class MotionBlurWithDepthTexture : PostProcessWangBase
{
    [Range(0f, 1f)]
    public float BlurSize = 0.01f;
    [Range(3, 50)]
    public int Iteration = 3;

    public Shader MDshader;
    private Material mymaterial = null;
    public Material MDmaterial
    {
        get
        {
            mymaterial = CheckShaderAndMaterial(MDshader, mymaterial);
            return mymaterial;
        }
    }
    //因为要通过相机获取深度纹理 因此需要声明一个相机变量获得相机组件
    private Camera myCamera;
    public Camera MDCamera
    {
        get
        {
            if (myCamera == null )
            {
                myCamera = GetComponent<Camera>();
            }
            return myCamera;
        }
    }
    //声明一个空的投影*视角变化矩阵 但第一次传给shader的这个矩阵应该是空矩阵或者是单位矩阵 
    private Matrix4x4 PreviousVPM;
    //获得深度图供shader中直接通过_CameraDepthTexture直接获取使用
    private void OnEnable()
    {
        MDCamera.depthTextureMode = DepthTextureMode.Depth;
    }

    protected void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        if(MDmaterial ==null )
        {
            Graphics.Blit(source, destination);
        }
        else
        {
            MDmaterial.SetFloat("_BlurSize", BlurSize);
            MDmaterial.SetInt("_Iteration", Iteration);
            //此时第一次传给shasder的投影*视角变换矩阵可能为空或者单位矩阵. 因为并没有对这个矩阵变量赋值啊 一开始
            MDmaterial.SetMatrix("_PreviousVPM", PreviousVPM);
            //这个通过相机组件中的封装代码获得我们需要的投影*视角变换矩阵 
            Matrix4x4 currentVPM = MDCamera.projectionMatrix * MDCamera.worldToCameraMatrix;
            //再获得逆矩阵
            Matrix4x4 currentVPMI = currentVPM.inverse;
            MDmaterial.SetMatrix("_CurrentVPMI", currentVPMI);
            //直到此步骤才对之前声明的矩阵赋值  供下一帧计算使用
            PreviousVPM = currentVPM;

            Graphics.Blit(source, destination, MDmaterial);
        }
    }
}

Shader部分

Shader "TNShaderPractise/ShaderPractise_MotionBlurWithDepthTexture"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" "Queue"="Transparent" }
        
		CGINCLUDE
		sampler2D _MainTex;
		//用于进行平台判断
		float4 _MainTex_TexelSize;
		//声明此专属变量 获得深度纹理
		sampler2D _CameraDepthTexture;
		float _BlurSize;
		fixed _Iteration;
		float4x4 _PreviousVPM;
		float4x4 _CurrentVPMI;

		struct a2v 
		{
			float4 vertex : POSITION;
			float4 texcoord : TEXCOORD0;
		};

		struct v2f 
		{
			float4 pos : SV_POSITION;
			float4 uv : TEXCOORD1;
			float4 uv_depth : TEXCOORD2;
		};

		v2f vert (a2v v)
		{
			v2f o;
			o.pos = UnityObjectToClipPos(v.vertex);
			o.uv = v.texcoord;
			o.uv_depth = v.texcoord;
		   //---- 这部分平台判断别忘记了-----
			#if UNTIY_UV_STARTS_AT_TOP
			  if(_MainTex_TexelSize.y<0)
				o.uv_depth.y=1-o.uv_depth.y;
			#endif
		   //---- 这部分平台判断别忘记了-----
			return o;
		}

		fixed4 frag (v2f i): SV_Target
		{
			//原理先获得用深度值表示的当前像素的世界坐标表示
			//先利用unity和C#脚本中的语句获得深度图中的深度值d  这个值是-1,1的范围的  他本身是经过宏计算 进行了 投影*视角矩阵的变换得到
			float d = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv_depth.xy);
			//然后我们要得到像素的NDC的视口空间下的表示 则需要用解析深度值d的逆函数来倒推  重新映射会0,1之间  
			float4 H = float4 (i.uv.x*2-1,i.uv.y*2-1,d*2-1,1);
			// 然后再用C#中的投影*视角变换矩阵的逆矩阵倒推出世界空间下的该像素的表示D
			float4 D = mul (_CurrentVPMI,H);
			//再用其次除法 从齐次坐标空间 将D点转换到世界空间下 得到以深度d来表示的 世界空间下的该像素的点 worldPos
			float4 worldPos = D/D.w;
		   //而在视口空间,当前像素的位置 就相当于是H 
			float4 currentPos = H;


			//******按照上面的理解   下面的这段理解就 说不通了.. OMG 这些矩阵变换我是真的变晕了 理解不对貌. 只能先这么记忆了


			//然后我们再利用已经得到的当前像素在世界空间下的坐标 去用前一状态的投影*视角变换矩阵 同样也是C#脚本中传过来的矩阵 求得前一状态下的该点的位置
			float4 previousPos = mul (_PreviousVPM ,worldPos);
			//再用齐次除法去齐次空间 得到该点的实际 视口空间下的坐标位置
			previousPos /= previousPos.w;
		   //再用两点相减得到速度.但为啥除以2 没明白. 貌似意义不大 通过_BlurSize参数的调节可以得到同样的效果
			float2 velocity = (currentPos.xy-previousPos.xy)/2;


			// 获取用于最终纹理采样的 坐标 
			float2 uv = i.uv.xy;
			//对处理过后的纹理进行正常采样等待再处理后渲染到屏幕上
			float4 c = tex2D(_MainTex,uv);
			//采样坐标再进行第二次迭代前进行 _BlurSize的距离控制
			uv += velocity *_BlurSize;

			//这个For循环表示方法 以前没学到过。后来问了朋友得知 其作用就相当于 uv+=velocity*_BlurSize 放入循环体内计算一次。 
			//至于为什么不直接放入循环体,我测试后发现报错.可能不能这么干.. 毕竟循环体内没有声明相应的变量
			for (int it =1; it<_Iteration;it++,uv+=velocity*_BlurSize)
			{
				//这里得到当状态像素的采样颜色   因为要做迭代处理 所以用 加成速度后的UV来采样  
				//第一遍写完后没有报错,但也没有效果,检查了半天才发想这里采样的UV错了 用成了i.uv 实际上要用uv
				fixed4  currentColor =tex2D (_MainTex , uv);
				//进行次数叠加之后 再进行平均计算
				c+=currentColor;
			}
			c/=_Iteration;
			//最终输出渲染完整处理后的图片到屏幕上
			return c;
		}
		
		ENDCG

		Pass
		{
			ZWrite Off ZTest Always Cull Off

			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			ENDCG
		}
    }
	FallBack Off
	//看似写了那么多注释,也只是一步一步记忆下来了. 至于过程中 有些操作为什么这么做 并不是真的理解了....
	//作为一个3D美术真的啃这些好累. 乐乐女神总是说很简单 很简单..然后我就卡在了这些很简单上.或者他没有说明的地方.
	//比如那俩速度计算最后为啥要除以2. 求平均值? 相减能得到速度太大? 所以除以2... 哎 不懂

	//不过这个可能仍然是个理解练习.实际效果并不怎么样..
}

总结

这一节学的挫败感极强。。。。。 继续爬吧。。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值