이 예제의 Unity 셰이더는 뎁스 텍스처와 스크린 공간 UV 좌표를 사용하여 픽셀의 월드 공간 위치를 재구성합니다. 셰이더는 위치를 시각화하기 위해 메시 위에 체커보드 패턴을 그립니다.
다음 그림에 최종 결과가 나와 있습니다.
이 페이지는 다음 섹션으로 구성됩니다.
샘플 씬을 생성하려면 이 섹션의 다음 단계를 따르십시오.
기존 Unity 프로젝트에 URP를 설치하거나, 유니버설 프로젝트 템플릿을 사용하여 새 프로젝트를 생성합니다.
샘플 씬에서 평면 게임 오브젝트를 생성하고 일부 게임 오브젝트를 가리도록 배치합니다.
새 머티리얼을 생성하여 평면에 할당합니다.
새 셰이더를 생성하여 머티리얼에 할당합니다. URP 언릿 기본 셰이더 페이지의 Unity 셰이더 소스 코드를 복사하여 붙여넣습니다.
URP 에셋을 선택합니다.
URP 에셋의 General 섹션에서 Depth Texture를 활성화합니다.
4단계에서 생성한 셰이더를 엽니다.
이 섹션에서는 URP 언릿 기본 셰이더 페이지의 소스 코드를 붙여넣었다고 가정합니다.
ShaderLab 코드를 다음과 같이 변경하십시오.
HLSLPROGRAM 블록에서 뎁스 텍스처 셰이더 헤더에 대한 include 선언을 추가합니다. 예를 들어 Core.hlsl에 대한 기존 include 선언 아래에 배치할 수 있습니다.
#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 파일에는 카메라 뎁스 텍스처를 샘플링하기 위한 함수가 포함되어 있습니다. 이 예제에서는 SampleSceneDepth 함수를 사용하여 픽셀의 Z 좌표를 샘플링합니다.
프래그먼트 셰이더 정의에 Varyings IN을 입력으로 추가합니다.
half4 frag(Varyings IN) : SV_Target
이 예제에서 프래그먼트 셰이더는 Varyings 구조체의 positionHCS 프로퍼티를 사용하여 픽셀의 위치를 가져옵니다.
프래그먼트 셰이더에서 뎁스 버퍼를 샘플링하기 위한 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 파일에서 가져옵니다. 이 함수는 [0, 1] 범위의 Z값을 반환합니다.
재구성 함수(ComputeWorldSpacePosition)가 작동하려면 뎁스값이 NDC(Normalized Device Coordinate) 공간에 있어야 합니다. 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) 값으로부터 월드 공간 위치를 계산하는 유틸리티 함수입니다. 이 함수는 SRP 코어 패키지의 Common.hlsl 파일에 정의되어 있습니다.
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 함수는 패턴을 음의 좌표 쪽으로 미러링합니다.
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 상수를 사용하면 코드가 모든 플랫폼을 올바르게 처리할 수 있습니다.
셰이더 코드를 저장하면 예제가 준비됩니다.
다음 그림에 최종 결과가 나와 있습니다.
아래는 이 예제의 완전한__ ShaderLab__셰이더 오브젝트의 구조를 정의하기 위한 Unity 언어입니다. 자세한 정보
See in Glossary 코드입니다.
// 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
}
}
}