自言自语
这个效果学习的我真的是各种难受,矩阵转换来转换去,到现在也没有理解其全部转换的意义。看不懂啊查阅资料也没能懂。另外还有一两点小问题 也是不得其解。只能先如此记忆了。。
另外这个效果可能也只是练习用的,作为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... 哎 不懂
//不过这个可能仍然是个理解练习.实际效果并不怎么样..
}
总结
这一节学的挫败感极强。。。。。 继续爬吧。。。。