뎁스 텍스처에서 픽셀의 월드 공간 포지션 재구성
이 예제의 Unity 셰이더는 뎁스 텍스처와 스크린 공간 UV 좌표를 사용하여 픽셀의 월드 공간 포지션을 재구성합니다. 셰이더는 포지션을 시각화하기 위해 메시 위에 체커보드 패턴을 그립니다.
다음 그림에 최종 결과가 나와 있습니다.
이 페이지는 다음 섹션으로 구성됩니다.
샘플 씬 생성
샘플 씬을 생성하려면 이 섹션의 다음 단계를 따르십시오.
기존 Unity 프로젝트에 URP를 설치하거나, 유니버설 프로젝트 템플릿을 사용하여 새 프로젝트를 생성합니다.
샘플 씬에서 평면 게임 오브젝트를 생성하고 일부 게임 오브젝트를 가리도록 배치합니다.
새 머티리얼을 생성하여 평면에 할당합니다.
새 셰이더를 생성하여 머티리얼에 할당합니다. URP 언릿 기본 셰이더 페이지의 Unity 셰이더 소스 코드를 복사하여 붙여넣습니다.
URP 에셋을 선택합니다. 유니버설 렌더 파이프라인 템플릿을 사용하여 프로젝트를 생성한 경우 URP 에셋 경로는
Assets/Settings/UniversalRP-HighQuality
입니다.URP 에셋의 General 섹션에서
Depth Texture
를 활성화합니다.4단계에서 생성한 셰이더를 엽니다.
ShaderLab 코드 편집
이 섹션에서는 URP 언릿 기본 셰이더 페이지의 소스 코드를 붙여넣었다고 가정합니다.
ShaderLab 코드를 다음과 같이 변경하십시오.
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 좌표를 샘플링합니다.프래그먼트 셰이더 정의에
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 // Z를 조정하여 OpenGL용 NDC에 맞춥니다. 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 코드
아래는 이 예제의 완전한 ShaderLab 코드입니다.
// 이 Unity 셰이더는 뎁스 텍스처와 스크린 공간 UV 좌표를 사용하여
// 픽셀의 월드 공간 포지션을 재구성합니다. 셰이더는 메시 위에 체커보드 패턴을 그려서
// 포지션을 시각화합니다.
Shader "Example/URPReconstructWorldPos"
{
Properties
{ }
// 셰이더 코드가 포함된 서브셰이더 블록입니다.
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)
{
// 출력 오브젝트(OUT)를 Varyings 구조체로 선언합니다.
Varyings OUT;
// TransformObjectToHClip 함수는 버텍스 포지션을 오브젝트 공간에서
// 균일한 클립 공간으로 변환합니다.
OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz);
// 출력을 반환합니다.
return OUT;
}
// 프래그먼트 셰이더 정의입니다.
// Varying 입력 구조에는 버텍스 셰이더에서 보간된 값이
// 포함됩니다. 프래그먼트 셰이더는 `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
// D3D와 같이 REVERSED_Z가 있는 플랫폼의 경우입니다.
if(depth < 0.0001)
return half4(0,0,0,1);
#else
// OpenGL과 같이 REVERSED_Z가 없는 플랫폼의 경우입니다.
if(depth > 0.9999)
return half4(0,0,0,1);
#endif
return color;
}
ENDHLSL
}
}
}