相机深度图渲染 Shader+C# 脚本教程(带代码详解)
一、效果预览
深度图通过获取观察视角中,物体由近到远的深度信息,来实现与其相关的特殊效果。
深度值是在像素信息中保存的[0,1]范围的非线性值,这些深度值来自裁剪坐标。Unity会自动利用Shader Replacement将RenderType为Opaque、渲染队列小于等于2500并且有ShadowCaster Pass的物体的深度值渲染到深度图中。
该方案通过Shader+C# 脚本协作,让物体显示相机视角下的深度图效果:场景中物体像素颜色随其与相机的距离变化(近距偏白,远距偏黑),可直观呈现场景深度信息,适合深度可视化、后期特效(如景深)、地形高度检测等开发场景。

二、Shader 代码解析(Custom/Depth)
1. SubShader 基础配置
SubShader {
Tags { "RenderType"="Opaque" } // 不透明渲染类型(深度图无需透明,避免混合异常)
Pass{
CGPROGRAM
#pragma vertex vert // 声明顶点着色器
#pragma fragment frag // 声明片元着色器
#include "UnityCG.cginc" // 引入坐标转换、深度纹理工具库
sampler2D _CameraDepthTexture; // 声明Unity内置相机深度纹理(存储场景深度信息)
2. 数据结构定义
// 片元输入结构(v2f:vertex to fragment)
struct v2f {
float4 pos : SV_POSITION; // 裁剪空间顶点坐标(屏幕渲染用)
float3 uv:TEXCOORD0; // 模型UV(本Shader暂未使用,预留扩展)
float4 screenPos:TEXCOORD1; // 屏幕空间坐标(用于采样深度纹理)
};
3. 顶点着色器(vert 函数)
v2f vert (appdata_base v){
v2f o;
o.pos = UnityObjectToClipPos (v.vertex); // 模型空间→裁剪空间(屏幕渲染必备)
// 计算屏幕空间坐标(齐次坐标,含深度信息,用于后续深度纹理采样)
o.screenPos = ComputeScreenPos(o.pos);
o.uv = v.texcoord; // 传递UV(预留)
return o;
}
- 核心操作:获取屏幕空间坐标,为片元着色器采样深度纹理提供坐标基础。
4. 片元着色器(frag 函数)—— 深度图核心逻辑
half4 frag (v2f i) : COLOR{
// 步骤1:转换屏幕坐标为投影纹理坐标(适配不同平台深度纹理采样)
half4 proj = UNITY_PROJ_COORD(i.screenPos);
// 步骤2:采样相机深度纹理(tex2Dproj自动处理投影坐标,获取深度值)
half4 _tex2Dproj = tex2Dproj(_CameraDepthTexture, proj);
// 步骤3:将深度纹理值转为[0,1]线性范围(Linear01Depth确保近距=1、远距=0,颜色直观)
half depthValue = Linear01Depth(_tex2Dproj.r);
// 步骤4:输出深度颜色(RGB通道均为depthValue,呈现黑白深度图,Alpha=1不透明)
return half4(depthValue, depthValue, depthValue, 1);
}
- 核心原理:通过采样 Unity 内置
_CameraDepthTexture获取场景深度,再转为线性范围,最终以黑白颜色映射深度(近白远黑)。
三、C# 脚本解析(cameraDepth)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof (Camera))] // 强制挂载Camera组件,避免脚本无相机时报错
public class cameraDepth : MonoBehaviour
{
public Material cameraMaterial; // 关联使用“Custom/Depth”Shader的材质
void Start () {
Camera camera = gameObject.GetComponent<Camera> ();
// 关键:开启相机深度纹理生成(否则_CameraDepthTexture无数据,深度图失效)
camera.depthTextureMode = DepthTextureMode.Depth;
}
// 渲染完成后调用(处理相机最终图像,输出深度图)
void OnRenderImage (RenderTexture source, RenderTexture destination){
if (cameraMaterial != null)
{
// 将相机渲染图像通过指定材质(深度Shader)处理后输出
Graphics.Blit(source, destination, cameraMaterial);
}
else
{
// 无材质时直接输出原图像(避免黑屏)
Graphics.Blit(source, destination);
}
}
}
- 核心作用:强制相机生成深度纹理,并通过
OnRenderImage将相机画面用深度 Shader 处理,最终显示深度图。
四、完整使用步骤(4 步完成)
- 创建 Shader 与材质:Unity 中右键→Create→Shader→Unlit Shader,替换为上述 “Custom/Depth” 代码;创建 Material(命名 DepthMat),将 Shader 设为 “Custom/Depth”。
- 创建 C# 脚本:右键→Create→C# Script,命名 “cameraDepth”,替换为上述脚本代码。
- 配置相机:将 “cameraDepth” 脚本挂载到场景主相机;在脚本
cameraMaterial字段拖入步骤 1 创建的 “DepthMat” 材质。 - 运行查看效果:点击运行,场景中物体将以黑白深度图显示(靠近相机的物体偏白,远离相机的物体偏黑)。
完整c#代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//需要Camera组件
[RequireComponent(typeof (Camera))]
public class cameraDepth : MonoBehaviour
{
public Material cameraMaterial;
void Start () {
Camera camera = gameObject.GetComponent<Camera> ();
//设置Camera的depthTextureMode,生成深度图
camera.depthTextureMode = DepthTextureMode.Depth;
}
//OnRenderImage在所有的渲染完成后调用
//该函数允许我们处理渲染后的图像,输入原图像source,输出的图像desitination
void OnRenderImage (RenderTexture source, RenderTexture destination){
//如果指定了cameraMaterial 就用cameraMaterial处理输出的图像,否则就用输入的原图像用作输出
if (cameraMaterial != null)
{
//该方法用于将输入的图像指定material和shader pass 后输出
Graphics.Blit(source, destination, cameraMaterial);
}
else
{
Graphics.Blit(source, destination);
}
}
}
完整Shader代码
Shader "Custom/Depth" {
SubShader {
Tags { "RenderType"="Opaque" }
Pass{
CGPROGRAM
// 定义顶点、片片渲染器,并引入shader相关的宏
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
// 声明贴图变量,用于接受摄像机的贴图
sampler2D _CameraDepthTexture;
// 片元渲染器的输入结构
struct v2f {
// 像素的坐标
float4 pos : SV_POSITION;
float3 uv:TEXCOORD0;
// 屏幕坐标
float4 screenPos:TEXCOORD1;
};
//顶点渲染器
v2f vert (appdata_base v){
v2f o;
// 将顶点坐标模型空间转为裁剪空间
o.pos = UnityObjectToClipPos (v.vertex);
// 使用ComputeScreenPos计算出屏幕坐标
// 输入裁剪空间的位置,获取齐次空间的屏幕坐标。(不是最终输出的屏幕坐标)
o.screenPos = ComputeScreenPos(o.pos);
o.uv = v.texcoord;
return o;
}
//片元渲染器
half4 frag (v2f i) : COLOR{
// 计算出投影纹理坐标
half4 proj =UNITY_PROJ_COORD(i.screenPos);
//tex2D和tex2Dproj的区别在于:tex2Dproj会先将第二个参数除以它的最后一个分量。
//要想进行深度值对比,只有先存储深度值。在生成ShadowMap时存储深度值,然后无论用tex2D还是tex2Dproj都可以得到深度值。
half4 _tex2Dproj = tex2Dproj(_CameraDepthTexture, proj);
//LinearEyeDepth负责把深度纹理的采样结果转换到视角空间下的深度值
//Linear01Depth则返回一个范围在[0,1]的线性深度值
half depthValue = Linear01Depth(_tex2Dproj.r);
// float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv);
// float depthValue = Linear01Depth(depth);
return half4(depthValue, depthValue, depthValue, 1);
}
ENDCG
}
}
// 这个必须要有。失败的时候尝试调用Diffuse subshader
FallBack "Diffuse"
}
就完成了
6012

被折叠的 条评论
为什么被折叠?



