Unity 6.3
0 онлайн 54 гостей 3 в системе
Вход

Реконструкция мировых космических позиций в шейдере в URP

В этом примере шейдер Unity восстанавливает позиции пикселей в мировом пространстве, используя текстуру глубины и координаты экранного пространства UV. Шейдер рисует шахматную доску на сетке, чтобы визуализировать позиции.

На следующей иллюстрации показан конечный результат:

Шахматная доска, визуализирующая реконструированные мировые космические позиции.
Шахматная доска, визуализирующая реконструированные мировые космические позиции.

Эта страница содержит следующие разделы:

Создание примерной сцены

Создайте примерную сцену для выполнения действий, описанных в этом разделе:

  1. Установите URP в существующий проект Unity или создайте новый проект с использованием шаблона Universal Project Template.

  2. В примере Scene создайте плоскость GameObject и расположите ее так, чтобы она окклюдировала часть GameObjects.

    Создание плоскости
    Создание плоскости
  3. Создайте новый Материал и присвоите его плоскости.

  4. Создайте новый шейдер и присвоите его материалу. Скопируйте и вставьте исходный код шейдера Unity со страницы Базовый неосвещённый шейдер URP.

  5. Выберите ассет URP.

  6. В ассете URP в разделе Общие включите Depth Texture.

    В URP ассет, включить текстуру глубины
    В URP ассет, включить текстуру глубины
  7. Откройте шейдер, созданный на шаге 4.

Изменить код ShaderLab

В этом разделе предполагается, что вы скопировали исходный код со страницы Базовый неосвещённый шейдер URP.

Внесите следующие изменения в код ShaderLab:

  1. В блоке HLSLPROGRAM добавьте объявление включения для заголовка шейдера текстуры глубины. Например, поместите его под существующее объявление включения для Core.hlsl.

    #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
    
    // The DeclareDepthTexture.hlsl file contains utilities for sampling the Camera
    // depth texture.
    #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl"
    

    Файл DeclareDepthTexture.hlsl содержит функции для выборки текстуры глубины Camera. В этом примере используется функция SampleSceneDepth для выборки координаты Z для пикселей.

  2. В определении шейдера фрагмента добавьте Varyings IN в качестве входа.

    half4 frag(Varyings IN) : SV_Target
    

    В этом примере шейдер фрагмента использует positionHCS свойства от Varyings структуру, чтобы получить положения пикселей.

  3. В шейдере фрагментов, чтобы вычислить координаты UV для выборки буфера глубины, разделите расположение пикселей на разрешение цели рендеринга _ScaledScreenParams. Свойство _ScaledScreenParams.xy учитывает любое масштабирование цели рендеринга, например Динамическое Разрешение.

    float2 UV = IN.positionHCS.xy / _ScaledScreenParams.xy;
    
  4. В шейдере фрагментов используйте функции SampleSceneDepth для семплирования буфера глубины.

    #if UNITY_REVERSED_Z
        real depth = SampleSceneDepth(UV);
    #else
        // Adjust z to match NDC for OpenGL
        real depth = lerp(UNITY_NEAR_CLIP_VALUE, 1, SampleSceneDepth(UV));
    #endif
    

Функция SampleSceneDepth взята из файла DeclareDepthTexture.hlsl. Она возвращает значение Z в диапазоне [0, 1].

Для работы функции реконструкции (ComputeWorldSpacePosition) значение глубины должно находиться в нормализованном координатном пространстве устройства (NDC). В D3D, Z находится в диапазоне [0,1], в OpenGL, Z находится в диапазоне [-1, 1].

В этом примере используется константа UNITY_REVERSED_Z для определения платформы и настройки диапазона значений Z. Дополнительные пояснения см. в шаге 6 этого примера.

Переменная UNITY_NEAR_CLIP_VALUE является независимым от платформы значением вблизи плоскости обрезки для пространства клипа.

Дополнительные сведения см. в Различия в отображении между платформами.

  1. Реконструкция мировых пространственных позиций из координат UV и Z пикселей.

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

    ComputeWorldSpacePosition — это функция, которая вычисляет пространственное положение мира из значений UV и глубины (Z). Эта функция определена в файле Common.hlsl пакета SRP Core.

    UNITY_MATRIX_I_VP является матрицей проекции обратного вида, которая преобразует точки из пространства клипа в мировое пространство.

  2. Чтобы визуализировать пространственные позиции пикселей в мире, создайте эффект квадрата.

    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 зеркалит шаблон на сторону с отрицательными координатами.

    Объявление uint3 для переменной worldIntPos привязывает координатные позиции к целым числам.

    Оператор AND в выражении <integer value> & 1 проверяет, является ли значение чётным (0) или нечетным (1). Выражение позволяет коду разделить поверхность на квадраты.

    Оператор XOR в выражении <integer value> ^ <integer value> переворачивает цвет квадрата.

    Буфер глубины может не иметь действительных значений для областей, где не отображается геометрия. Следующий код рисует черный цвет в таких областях.

    #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 == далеко, или 1 == далеко). Константа UNITY_REVERSED_Z позволяет коду правильно обрабатывать все платформы.

    Сохраните код шейдера, пример готов.

На следующей иллюстрации показан конечный результат:

3D Шахматы
3D Шахматы

Полный код ShaderLab

Ниже приведен полный код ShaderLab для этого примера.

// This Unity shader reconstructs the world space positions for pixels using a depth
// texture and screen space UV coordinates. The shader draws a checkerboard pattern
// on a mesh to visualize the positions.
Shader "Example/URPReconstructWorldPos"
{
    Properties
    { }

    // The SubShader block containing the Shader code.
    SubShader
    {
        // SubShader Tags define when and under which conditions a SubShader block or
        // a pass is executed.
        Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" }

        Pass
        {
            HLSLPROGRAM
            // This line defines the name of the vertex shader.
            #pragma vertex vert
            // This line defines the name of the fragment shader.
            #pragma fragment frag

            // The Core.hlsl file contains definitions of frequently used HLSL
            // macros and functions, and also contains #include references to other
            // HLSL files (for example, Common.hlsl, SpaceTransforms.hlsl, etc.).
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

            // The DeclareDepthTexture.hlsl file contains utilities for sampling the
            // Camera depth texture.
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl"

            // This example uses the Attributes structure as an input structure in
            // the vertex shader.
            struct Attributes
            {
                // The positionOS variable contains the vertex positions in object
                // space.
                float4 positionOS   : POSITION;
            };

            struct Varyings
            {
                // The positions in this struct must have the SV_POSITION semantic.
                float4 positionHCS  : SV_POSITION;
            };

            // The vertex shader definition with properties defined in the Varyings
            // structure. The type of the vert function must match the type (struct)
            // that it returns.
            Varyings vert(Attributes IN)
            {
                // Declaring the output object (OUT) with the Varyings struct.
                Varyings OUT;
                // The TransformObjectToHClip function transforms vertex positions
                // from object space to homogenous clip space.
                OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz);
                // Returning the output.
                return OUT;
            }

            // The fragment shader definition.
            // The Varyings input structure contains interpolated values from the
            // vertex shader. The fragment shader uses the `positionHCS` property
            // from the `Varyings` struct to get locations of pixels.
            half4 frag(Varyings IN) : SV_Target
            {
                // To calculate the UV coordinates for sampling the depth buffer,
                // divide the pixel location by the render target resolution
                // _ScaledScreenParams.
                float2 UV = IN.positionHCS.xy / _ScaledScreenParams.xy;

                // Sample the depth from the Camera depth texture.
                #if UNITY_REVERSED_Z
                    real depth = SampleSceneDepth(UV);
                #else
                    // Adjust Z to match NDC for OpenGL ([-1, 1])
                    real depth = lerp(UNITY_NEAR_CLIP_VALUE, 1, SampleSceneDepth(UV));
                #endif

                // Reconstruct the world space positions.
                float3 worldPos = ComputeWorldSpacePosition(UV, depth, UNITY_MATRIX_I_VP);

                // The following part creates the checkerboard effect.
                // Scale is the inverse size of the squares.
                uint scale = 10;
                // Scale, mirror and snap the coordinates.
                uint3 worldIntPos = uint3(abs(worldPos.xyz * scale));
                // Divide the surface into squares. Calculate the color ID value.
                bool white = ((worldIntPos.x) & 1) ^ (worldIntPos.y & 1) ^ (worldIntPos.z & 1);
                // Color the square based on the ID value (black or white).
                half4 color = white ? half4(1,1,1,1) : half4(0,0,0,1);

                // Set the color to black in the proximity to the far clipping
                // plane.
                #if UNITY_REVERSED_Z
                    // Case for platforms with REVERSED_Z, such as D3D.
                    if(depth < 0.0001)
                        return half4(0,0,0,1);
                #else
                    // Case for platforms without REVERSED_Z, such as OpenGL.
                    if(depth > 0.9999)
                        return half4(0,0,0,1);
                #endif

                return color;
            }
            ENDHLSL
        }
    }
}