unity shaderlab 相机深度图渲染 Shader+C# 脚本教程(带代码详解)

相机深度图渲染 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 步完成)

  1. 创建 Shader 与材质:Unity 中右键→Create→Shader→Unlit Shader,替换为上述 “Custom/Depth” 代码;创建 Material(命名 DepthMat),将 Shader 设为 “Custom/Depth”。
  2. 创建 C# 脚本:右键→Create→C# Script,命名 “cameraDepth”,替换为上述脚本代码。
  3. 配置相机:将 “cameraDepth” 脚本挂载到场景主相机;在脚本cameraMaterial字段拖入步骤 1 创建的 “DepthMat” 材质。
  4. 运行查看效果:点击运行,场景中物体将以黑白深度图显示(靠近相机的物体偏白,远离相机的物体偏黑)。

完整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" 
}  

就完成了

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

千年奇葩

从来没受过打赏,这玩意好吃吗?

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值