【Unity Shader学习笔记】13.使用深度和法线纹理

本文详细介绍了如何在Unity中利用深度纹理和法线纹理实现全局雾效。首先,通过延迟渲染或特殊Pass获取深度纹理,然后根据深度信息重建世界坐标。接着,计算摄像机到近裁剪平面的4个角的向量,用于雾的计算。在顶点和片元着色器中,利用纹理坐标和深度信息重建世界坐标,并计算雾密度,最终实现基于高度的雾效。示例代码展示了从设置相机参数到渲染后处理的完整流程。
摘要由CSDN通过智能技术生成

学习资料:《Unity Shader入门精要》第13章

源代码:Github

获取深度和法线纹理

深度纹理:

  • 延迟渲染:渲染到G-buffer中
  • 通过单独的Pass渲染得到:使用着色器替换技术,选择Render Type为Opaque的物体,判断渲染队列是否<=2500,满足条件的就渲染到深度和法线纹理中

深度纹理的获取:

camera.depthTextureMode = DepthTextureMode.depth;

在Shader中访问深度纹理:_CameraDepthTexture

全局雾效

重建世界坐标

根据深度纹理重建每个像素在世界空间下的位置:对图像空间下的视锥体射线(摄像机指向图像上某点的射线)进行插值,射线存储了该像素在世界空间下到摄像机的方向信息。把射线与线性化后的视角空间下的深度值相乘,加上摄像机的世界位置,得到该像素在世界空间中的位置。

float4 worldPos = _WorldSpaceCameraPos + linearDepth * interpolatedRay;

计算摄像机到近裁剪平面的4个角的向量,屏幕后处理就是使用特定的材质渲染一个刚好填充整个屏幕的四边形面片,这个四边形面片的4个顶点就对应了近裁剪平面的4个角

雾的计算

float3 afterFog = f * fogColor + (1-f) * origColor;

基于高度的雾效:

f=\frac{H_{end}-y}{H_{end}-H_{start}}

获取摄像机的深度纹理

void OnEnable() {
	camera.depthTextureMode |= DepthTextureMode.Depth;
}

在摄像机的FogWithDepthTexture脚本中计算interpolatedRay,传递给Shader:

void OnRenderImage (RenderTexture src, RenderTexture dest) {
	if (material != null) {
		Matrix4x4 frustumCorners = Matrix4x4.identity;

		float fov = camera.fieldOfView;
		float near = camera.nearClipPlane;
		float aspect = camera.aspect;

		float halfHeight = near * Mathf.Tan(fov * 0.5f * Mathf.Deg2Rad);
		Vector3 toRight = cameraTransform.right * halfHeight * aspect;
		Vector3 toTop = cameraTransform.up * halfHeight;

		Vector3 topLeft = cameraTransform.forward * near + toTop - toRight;
		float scale = topLeft.magnitude / near;

		topLeft.Normalize();
		topLeft *= scale;

		Vector3 topRight = cameraTransform.forward * near + toRight + toTop;
		topRight.Normalize();
		topRight *= scale;

		Vector3 bottomLeft = cameraTransform.forward * near - toTop - toRight;
		bottomLeft.Normalize();
		bottomLeft *= scale;

		Vector3 bottomRight = cameraTransform.forward * near + toRight - toTop;
		bottomRight.Normalize();
		bottomRight *= scale;

		frustumCorners.SetRow(0, bottomLeft);
		frustumCorners.SetRow(1, bottomRight);
		frustumCorners.SetRow(2, topRight);
		frustumCorners.SetRow(3, topLeft);

		material.SetMatrix("_FrustumCornersRay", frustumCorners);

		material.SetFloat("_FogDensity", fogDensity);
		material.SetColor("_FogColor", fogColor);
		material.SetFloat("_FogStart", fogStart);
		material.SetFloat("_FogEnd", fogEnd);

		Graphics.Blit (src, dest, material);
	} else {
		Graphics.Blit(src, dest);
	}
}

在顶点着色器中用纹理坐标确定4个顶点对应的interpolatedRay的索引:

o.uv = v.texcoord;
o.uv_depth = v.texcoord;

// 如果是DirectX开启抗锯齿,纹理坐标需要进行差异化处理
#if UNITY_UV_STARTS_AT_TOP
if (_MainTex_TexelSize.y < 0)
	o.uv_depth.y = 1 - o.uv_depth.y;
#endif

int index = 0;
if (v.texcoord.x < 0.5 && v.texcoord.y < 0.5) {
	index = 0;
} else if (v.texcoord.x > 0.5 && v.texcoord.y < 0.5) {
	index = 1;
} else if (v.texcoord.x > 0.5 && v.texcoord.y > 0.5) {
	index = 2;
} else {
	index = 3;
}

#if UNITY_UV_STARTS_AT_TOP
if (_MainTex_TexelSize.y < 0)
	index = 3 - index;
#endif

o.interpolatedRay = _FrustumCornersRay[index];
	 	 
return o;

在片元着色器中重建世界坐标,产生雾效:

// SAMPLLE_DEPTH_TEXTURE:对深度纹理采样
// LinearEyeDepth:得到视角空间下的线性深度值
float linearDepth = LinearEyeDepth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv_depth));

// 计算该像素的世界坐标
float3 worldPos = _WorldSpaceCameraPos + linearDepth * i.interpolatedRay.xyz;
			
float fogDensity = (_FogEnd - worldPos.y) / (_FogEnd - _FogStart); 
fogDensity = saturate(fogDensity * _FogDensity);

fixed4 finalColor = tex2D(_MainTex, i.uv);
finalColor.rgb = lerp(finalColor.rgb, _FogColor.rgb, fogDensity);

return finalColor;



效果展示:
fogDensity = 0.5
在这里插入图片描述
fogDensity = 1
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值