docs.unity3d.com
    显示 / 隐藏目录

    从深度纹理重建像素的世界空间位置

    此示例中的 Unity 着色器使用深度纹理和屏幕空间 UV 坐标来重建像素的世界空间位置。该着色器在网格上绘制棋盘图案,使位置可视化。

    下图展示了最终结果:

    棋盘图案使重建的世界空间位置可视化。

    本页包含以下几节:

    • 创建示例场景

    • 编辑 ShaderLab 代码

    • 完整的 ShaderLab 代码

    创建示例场景

    要按照本节中的步骤操作,应创建示例场景:

    1. 将 URP 安装到现有 Unity 项目中,或使用通用项目模板创建新项目。

    2. 在示例场景中,创建一个平面游戏对象,并将其放置在适当位置以使其遮挡一些游戏对象。

      创建平面

    3. 创建一个新材质并将其分配给平面。

    4. 创建一个新着色器并将其分配给材质。从 URP 无光照基本着色器页面复制并粘贴 Unity 着色器源代码。

    5. 选择 URP 资源。如果项目是用通用渲染管线模板创建的,则 URP 资源路径为 Assets/Settings/UniversalRP-HighQuality。

    6. 在 URP 资源的 General 部分中,启用 Depth Texture。

      在 URP 资源中,启用 Depth Texture

    7. 打开在步骤 4 中创建的着色器。

    编辑 ShaderLab 代码

    本节假设已从 URP 无光照基本着色器页面复制了相应的源代码。

    对 ShaderLab 代码进行以下更改:

    1. 在 HLSLPROGRAM 代码块中,添加深度纹理着色器头的 include 声明。例如,将该声明放在 Core.hlsl 的现有 include 声明下。

      #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
      
      // DeclareDepthTexture.hlsl 文件包含用于对摄像机深度纹理进行采样的
      // 实用程序。
      #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl"
      

      DeclareDepthTexture.hlsl 文件包含用于对摄像机深度纹理进行采样的函数。此示例使用 SampleSceneDepth 函数对像素的 Z 坐标进行采样。

    2. 在片元着色器定义中,添加 Varyings IN 作为输入。

      half4 frag(Varyings IN) : SV_Target
      

      在此示例中,片元着色器使用 Varyings 结构中的 positionHCS 属性来获取像素的位置。

    3. 在片元着色器中,要计算用于采样深度缓冲区的 UV 坐标,请将像素位置除以渲染目标分辨率 _ScaledScreenParams。_ScaledScreenParams.xy 属性会考虑渲染目标的任何缩放,例如动态分辨率。

      float2 UV = IN.positionHCS.xy / _ScaledScreenParams.xy;
      
    4. 在片元着色器中,使用 SampleSceneDepth 函数对深度缓冲区进行采样。

      #if UNITY_REVERSED_Z
          real depth = SampleSceneDepth(UV);
      #else
          // 调整 z 以匹配 OpenGL 的 NDC
          real depth = lerp(UNITY_NEAR_CLIP_VALUE, 1, SampleSceneDepth(UV));
      #endif
      

      SampleSceneDepth 函数来自 DeclareDepthTexture.hlsl 文件。它返回 [0, 1] 范围内的 Z 值。

      要使重建函数 (ComputeWorldSpacePosition) 正常工作,深度值必须位于归一化设备坐标 (NDC) 空间中。在 D3D 中,Z 处于 [0,1] 范围内,而在 OpenGL 中,Z 处于 [-1, 1] 范围内。

      此示例使用 UNITY_REVERSED_Z 常量来确定平台并调整 Z 值范围。有关更多说明,请参阅此示例中的步骤 6。

      UNITY_NEAR_CLIP_VALUE 变量是裁剪空间的与平台无关的近裁剪面值。

      有关更多信息,请参阅平台特定的渲染差异。

    5. 从像素的 UV 和 Z 坐标重建世界空间位置。

      float3 worldPos = ComputeWorldSpacePosition(UV, depth, UNITY_MATRIX_I_VP);
      

      ComputeWorldSpacePosition 是一个实用函数,根据 UV 和深度 (Z) 值计算世界空间位置。此函数在 SRP Core 包的 Common.hlsl 文件中定义。

      UNITY_MATRIX_I_VP 是一个逆视图投影矩阵,可将点从裁剪空间变换为世界空间。

    6. 要使像素的世界空间位置可视化,请创建棋盘效果。

      uint scale = 10;
      uint3 worldIntPos = uint3(abs(worldPos.xyz * scale));
      bool white = (worldIntPos.x & 1) ^ (worldIntPos.y & 1) ^ (worldIntPos.z & 1);
      half4 color = white ? half4(1,1,1,1) : half4(0,0,0,1);
      

      scale 是棋盘图案大小的相反比例。

      abs 函数将图案镜像到负坐标侧。

      worldIntPos 变量的 uint3 声明将坐标位置贴靠到整数。

      表达式 <integer value> & 1 中的 AND 运算符检查值是偶数 (0) 还是奇数 (1)。该表达式让代码将表面划分为正方形。

      表达式 <integer value> ^ <integer value> 中的 XOR 运算符将翻转正方形颜色。

      对于未渲染几何图形的区域,深度缓冲区可能没有任何有效值。以下代码会在这些区域绘制黑色。

      #if UNITY_REVERSED_Z
          if(depth < 0.0001)
              return half4(0,0,0,1);
      #else
          if(depth > 0.9999)
              return half4(0,0,0,1);
      #endif
      

      不同的平台对远裁剪面使用不同的 Z 值(0 == far,或 1 == far)。UNITY_REVERSED_Z 常量让代码可以正确处理所有平台。

      保存着色器代码,示例即准备就绪。

    下图展示了最终结果:

    3D 棋盘

    完整的 ShaderLab 代码

    以下是此示例的完整 ShaderLab 代码。

    // 此 Unity 着色器使用深度纹理和屏幕空间 UV 坐标来重建
    //像素的世界空间位置。该着色器在网格上绘制棋盘图案,
    //使位置可视化。
    Shader "Example/URPReconstructWorldPos"
    {
        Properties
        { }
    
        // 包含 Shader 代码的 SubShader 代码块。
        SubShader
        {
            // SubShader Tags 定义何时以及在何种条件下执行某个 SubShader 代码块
            // 或某个通道。
            Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" }
    
            Pass
            {
                HLSLPROGRAM
                // 此行定义顶点着色器的名称。
                #pragma vertex vert
                // 此行定义片元着色器的名称。
                #pragma fragment frag
    
                // Core.hlsl 文件包含常用的 HLSL 宏和
                // 函数的定义,还包含对其他 HLSL 文件(例如
                // Common.hlsl、SpaceTransforms.hlsl 等)的#include 引用。
                #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
    
                // DeclareDepthTexture.hlsl 文件包含用于对摄像机深度纹理进行采样的
                // 实用程序。
                #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl"
    
                // 此示例使用 Attributes 结构作为顶点着色器中的
                // 输入结构。
                struct Attributes
                {
                    // positionOS 变量包含对象空间中的顶点
                    // 位置。
                    float4 positionOS   : POSITION;
                };
    
                struct Varyings
                {
                    // 此结构中的位置必须具有 SV_POSITION 语义。
                    float4 positionHCS  : SV_POSITION;
                };
    
                // 顶点着色器定义具有在 Varyings 结构中定义的
                // 属性。vert 函数的类型必须与它返回的类型(结构)
                // 匹配。
                Varyings vert(Attributes IN)
                {
                    // 使用 Varyings 结构声明输出对象 (OUT)。
                    Varyings OUT;
                    // TransformObjectToHClip 函数将顶点位置
                    // 从对象空间变换到齐次裁剪空间。
                    OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz);
                    // 返回输出。
                    return OUT;
                }
    
                // 片元着色器定义。
                // Varyings 输入结构包含来自顶点着色器的
                // 插值。片元着色器使用 `Varyings` 结构中的
                // `positionHCS` 属性来获取像素的位置。
                half4 frag(Varyings IN) : SV_Target
                {
                    // 要计算用于采样深度缓冲区的 UV 坐标,
                    // 请将像素位置除以渲染目标分辨率
                    // _ScaledScreenParams。
                    float2 UV = IN.positionHCS.xy / _ScaledScreenParams.xy;
    
                    // 从摄像机深度纹理中采样深度。
                    #if UNITY_REVERSED_Z
                        real depth = SampleSceneDepth(UV);
                    #else
                        //  调整 Z 以匹配 OpenGL 的 NDC ([-1, 1])
                        real depth = lerp(UNITY_NEAR_CLIP_VALUE, 1, SampleSceneDepth(UV));
                    #endif
    
                    // 重建世界空间位置。
                    float3 worldPos = ComputeWorldSpacePosition(UV, depth, UNITY_MATRIX_I_VP);
    
                    // 以下部分创建棋盘效果。
                    // 比例是平方反比。
                    uint scale = 10;
                    // 缩放、镜像和捕捉坐标。
                    uint3 worldIntPos = uint3(abs(worldPos.xyz * scale));
                    // 将表面划分为正方形。计算颜色 ID 值。
                    bool white = ((worldIntPos.x) & 1) ^ (worldIntPos.y & 1) ^ (worldIntPos.z & 1);
                    // 根据 ID 值(黑色或白色)为正方形着色。
                    half4 color = white ? half4(1,1,1,1) : half4(0,0,0,1);
    
                    // 在远裁剪面附近将颜色设置为
                    // 黑色。
                    #if UNITY_REVERSED_Z
                        // 具有 REVERSED_Z 的平台(如 D3D)的情况。
                        if(depth < 0.0001)
                            return half4(0,0,0,1);
                    #else
                        // 没有 REVERSED_Z 的平台(如 OpenGL)的情况。
                        if(depth > 0.9999)
                            return half4(0,0,0,1);
                    #endif
    
                    return color;
                }
                ENDHLSL
            }
        }
    }
    
    返回到顶部
    Copyright © 2023 Unity Technologies — 商标和使用条款
    • 法律条款
    • 隐私政策
    • Cookie
    • 不要出售或分享我的个人信息
    • Your Privacy Choices (Cookie Settings)