Chapter13_使用深度和法线纹理

深度纹理

深度纹理实际就是一张渲染纹理,只不过它里边存储的是像素值不是颜色值,而是一个高精度的深度值。

顶点坐标转化到NDC(归一化的设备坐标)下的坐标的z分量就是顶点的深度值。NDC中,z分量范围在[-1,1],为了让z分量可以存到一张纹理中,需要使用公式将z分量映射:

                                                                          d = 0.5 * \large z_{ndc} + 0.5

Unity获取深度纹理方式分为两种方式:

在Unity中,可以让摄像机生成一张深度纹理或是深度+法线纹理。

1.当只生成一张深度纹理时,Unity在使用延迟渲染路径时(渲染路径决定了光照如何应用到Shader中,决定了把光源信息和处理后的光照信息放到那些数据中。),直接从G-buffer中获取深度缓存

2.在使用前向渲染路劲时,渲染深度纹理通过使用着色器替换技术,渲染需要的不透明物体,并使用它投射阴影时使用的Pass(即LightMode 设置为ShadowCaster 的Pass)来得到深度纹理。具体实现是,Unity会使用着色器替换技术渲染那些渲染类型(即SubShader的RenderType标签)为Opaque的物体,判断它们使用的渲染队列是否 <= 2500,如果满足,就把它渲染到深度和法线纹理中。

深度纹理的精度通常是24位活16位。如果选择生成一张深度+法线纹理,Unity会创建一张和屏幕分辨率相同,精度为32位(每个通道8位)的纹理。法线信息存到R和G通道。深度信息存到B和A通道。

  • 使用延迟渲染路径时:Unity只需要合并深度和法线缓存即可。
  • 使用前向渲染路劲时:默认不会创建法线缓存。因此Unity底层使用了一个单独的Pass把整个场景再次渲染一遍。这个Pass被放在一个内置Shader中。

代码具体获取深度纹理步骤:

  1. camera.depehtTextureMode = DepthTextureMode.Depth; //获取深度纹理
  2. Shader中直接访问特定纹理名称(_CameraDepthTexture)即可。

camera.depehtTextureMode = DepthTextureMode.DepthNormals;//获取深度纹理 + 法线纹理

camera.depehtTextureMode |= DepthTextureMode.Depth;

camera.depehtTextureMode = DepthTextureMode.DepthNormals; 以上两句获取一张深度纹理 + 一张深度+法线纹理

采样深度纹理,非线性变线性推导。。。以后补上。。

当摄像机远裁剪平面的距离过大,会导致距离摄像机较近的物体被映射的深度值非常小,生成的深度纹理变"黑"。相反,远裁剪平面过大,会导致距离摄像机较近的物体被映射的深度值偏大。生成的深度纹理变"白"。


实践:

1.运动模糊

速度映射图模拟运动模糊。速度映射图中存储了每个像素的速度,然后使用这个速度决定模糊的方向和大小。

生成速度映射纹理有两种方式:

  1. 把场景中所有物体都渲染到一张纹理中,不过这种方法需要修改场景所有的Shader。
  2. 使用深度纹理在片元着色器中为每个像素计算世界空间下的位置(使用当前帧的视角*投影矩阵的逆矩阵计算)。当得到世界空间下的顶点坐标后,然后使用前一帧视角*投影矩阵对其进行变换,得到该顶点位置在前一帧的NDC坐标。当前帧顶点的NDC坐标 - 前一帧的NDC位置得到该像素的速度。优点是屏幕后统一处理计算,缺点是两次矩阵变换,效率低。

 

Shader "Chan/Chapter 13/Motion Blur With Depth Texture" {
	Properties
	{
		_MainTex("Base Tex",2D) = "white"{}
		_BlurSize("Blur Size",float) = 1.0
	}
		SubShader
		{
			CGINCLUDE

			#include "UnityCG.cginc"

			sampler2D _MainTex;
			half4 _MainTex_TexelSize;
			//通过_CameraDepthTexture 直接就能访问到当前摄像机的深度纹理
			sampler2D _CameraDepthTexture;
			float4x4 _CurrentViewProjectionInverseMatrix;
			float4x4 _PreviousViewProjectionMatrix;
			half _BlurSize;

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

			v2f vert(appdata_img v)
			{
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);
				o.uv = v.texcoord;
				o.uv_depth = v.texcoord;

				//多张渲染纹理,可能uv采样坐标会出问题 需要根据平台判断
				# if UNITY_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
			{
				//不同平台要通过uv采样深度纹理得到深度值,可能有差异。使用宏定义 内部处理了。SAMPLE_DEPTH_TEXTURE 
				float d = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv_depth);
				//归一化设备坐标系下的坐标[-1,1],纹理通道值[0,1] 所以存储到深度纹理时,进行了映射。此处为反映射
				//归一化设备坐标系下的坐标坐标(x,y值由uv映射而来,z值由深度值映射而来)
				float4 h = float4(i.uv.x * 2 - 1,i.uv.y * 2 - 1,d * 2 - 1,1);
				//归一化设备坐标系下的坐标 乘以当前摄像机 * 投影矩阵的逆矩阵 得到世界空间坐标系的坐标
				float4 D = mul(_CurrentViewProjectionInverseMatrix,h);
				//世界空间坐标系的坐标
				float4 worldPos = D / D.w;

				//当前世界坐标乘以 当前摄像机视角 * 投影矩阵 得到上一帧时候在归一化设备坐标系下的坐标
				float4 currentPos = h;
				float4 previousPos = mul(_PreviousViewProjectionMatrix,worldPos);
				previousPos /= previousPos.w;

				//得到速度
				float2 velocity = (currentPos.xy - previousPos.xy) / 2.0f;

				float2 uv = i.uv;
				float4 c = tex2D(_MainTex,uv);
				uv += velocity * _BlurSize;//控制纹理采样距离
				//采样三地方,取
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值