Реконструкция мировых космических позиций в шейдере в URP
В этом примере шейдер Unity восстанавливает позиции пикселей в мировом пространстве, используя текстуру глубины и координаты экранного пространства UV. Шейдер рисует шахматную доску на сетке, чтобы визуализировать позиции.
На следующей иллюстрации показан конечный результат:
Эта страница содержит следующие разделы:
Создание примерной сцены
Создайте примерную сцену для выполнения действий, описанных в этом разделе:
Установите URP в существующий проект Unity или создайте новый проект с использованием шаблона Universal Project Template.
-
В примере Scene создайте плоскость GameObject и расположите ее так, чтобы она окклюдировала часть GameObjects.
Создание плоскости Создайте новый Материал и присвоите его плоскости.
Создайте новый шейдер и присвоите его материалу. Скопируйте и вставьте исходный код шейдера Unity со страницы Базовый неосвещённый шейдер URP.
Выберите ассет URP.
-
В ассете URP в разделе Общие включите
Depth Texture.
В URP ассет, включить текстуру глубины Откройте шейдер, созданный на шаге 4.
Изменить код ShaderLab
В этом разделе предполагается, что вы скопировали исходный код со страницы Базовый неосвещённый шейдер URP.
Внесите следующие изменения в код ShaderLab:
-
В блоке
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 для пикселей. -
В определении шейдера фрагмента добавьте
Varyings INв качестве входа.half4 frag(Varyings IN) : SV_TargetВ этом примере шейдер фрагмента использует
positionHCSсвойства отVaryingsструктуру, чтобы получить положения пикселей. -
В шейдере фрагментов, чтобы вычислить координаты UV для выборки буфера глубины, разделите расположение пикселей на разрешение цели рендеринга
_ScaledScreenParams. Свойство_ScaledScreenParams.xyучитывает любое масштабирование цели рендеринга, например Динамическое Разрешение.float2 UV = IN.positionHCS.xy / _ScaledScreenParams.xy; -
В шейдере фрагментов используйте функции
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 является независимым от платформы значением вблизи плоскости обрезки для пространства клипа.
Дополнительные сведения см. в Различия в отображении между платформами.
-
Реконструкция мировых пространственных позиций из координат UV и Z пикселей.
float3 worldPos = ComputeWorldSpacePosition(UV, depth, UNITY_MATRIX_I_VP);ComputeWorldSpacePosition— это функция, которая вычисляет пространственное положение мира из значений UV и глубины (Z). Эта функция определена в файлеCommon.hlslпакета SRP Core.UNITY_MATRIX_I_VPявляется матрицей проекции обратного вида, которая преобразует точки из пространства клипа в мировое пространство. -
Чтобы визуализировать пространственные позиции пикселей в мире, создайте эффект квадрата.
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позволяет коду правильно обрабатывать все платформы.Сохраните код шейдера, пример готов.
На следующей иллюстрации показан конечный результат:
Полный код 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
}
}
}