TECHARTNOMAD | TECHARTFLOWIO.COM

(출간예정) 모두의 셰이더 프로그래밍

동차 클립 공간(Homogeneous Clip Space) 동차 좌표 클리핑 공간

jplee 2025. 2. 2. 17:58
2025년 4월경(계획대로라면~)에 신간으로 출간 예정인 모두의 셰이더 프로그래밍(가제인데 매달 바뀌네요 생각이) 학습에 보충으로 필요할수도 있는 동차 클립 공간에 대한 간략한 내용입니다.

선형 대수학에서 동차 좌표가 도입된 주된 이유는 평행 이동 변환의 문제를 해결하기 위해서입니다. 원점 불변성으로 인해 선형 변환에서 평행 이동을 처리하기 위해서는 차원을 하나 더 추가해야 합니다.

이 공간이 정확히 무엇인지 많은 사람들이 혼란스러워하는데, 실제로는 카메라의 절두체 공간으로, 근평면(Near Plane)과 원평면(Far Plane) 사이의 영역을 의미합니다.

이것은 Frustum(절두체)이라고도 합니다.

이 절두체 공간에서 주의해야 할 몇 가지 사항이 있습니다:

  1. 버텍스 셰이더의 출력은 HCS 공간의 정점 좌표입니다
  2. 절두체 외부의 객체는 제거되며, 이것이 Clip의 유래입니다
  3. 이 절두체 내부의 좌표는 동차 좌표계입니다
  4. 이 절두체 공간은 MVP 행렬 변환 후에 얻어집니다.

V는 벡터, M은 행렬입니다

  1. 【매우 중요】HCS 공간 내의 xy 좌표 범위는 [-w, w] 사이입니다!!!
  2. 서로 다른 그래픽 API에서의 차이점

클립 공간 좌표(투영 후 공간 좌표라고도 함)는 Direct3D 계열과 OpenGL 계열 플랫폼에서 차이가 있습니다.

Direct3D 계열: 클립 공간의 깊이는 근평면에서 0.0, 원평면에서 +1.0의 범위를 가집니다. 여기에는 Direct3D, Metal, 그리고 콘솔이 포함됩니다.

OpenGL 계열: 클립 공간의 깊이는 근평면에서 -1.0, 원평면에서 +1.0의 범위를 가집니다. 여기에는 OpenGL과 OpenGL ES가 포함됩니다.

셰이더 코드에서는~

UNITY_NEAR_CLIP_VALUE

내장 매크로를 사용하여 플랫폼에 맞는 근평면 값을 얻을 수 있습니다.

즉, 카메라와 가까운 절단면을 얻기 위해 UNITY_NEAR_CLIP_VALUE 매크로를 크로스 플랫폼으로 사용합니다.

NDC(정규화된 장치 좌표계):

가장 오해가 많은 공간으로, 표준 NDC의 정의는 다음과 같습니다:

NDC 공간은 화면에 독립적인 디스플레이 좌표계로, x, y, z 구성요소가 -1에서 1 사이의 범위를 가지는 큐브를 포함합니다.

스크린 공간으로 변환하기 전에, 절두체 공간을 [-1, 1] 범위의 xyz 좌표를 가진 박스에 넣어야 합니다.

하지만 Unity의 NDC는 약간 다릅니다. Unity의 NDC는 실제로 동차 NDC입니다!

Unity의 HCS에서 NDC로의 변환

이 코드는 단 세 줄로 구성되어 있으며, 각 줄은 정교하게 설계된 변환 과정을 수행합니다. 이제 각 줄의 기능을 상세하게 분석하며 그 수학적 의미와 실제 적용 효과를 살펴보도록 하겠습니다.

이 변환 과정은 놀랍도록 효율적이면서도 정밀하게 설계되어 있습니다. 각 단계는 최소한의 연산으로 최대의 효과를 달성하도록 최적화되어 있으며, 이제 각 단계를 자세히 분석해보도록 하겠습니다.

첫 번째 단계에서는 절두체 공간의 모든 동차 좌표에 0.5를 곱하여 스케일링을 수행합니다. 이는 좌표계의 범위를 정확히 절반으로 줄이는 과정으로, 이후 변환 과정을 위한 중요한 준비 단계입니다.

두 번째 단계에서는 정교한 좌표 변환을 수행하는데, x 좌표에는 w 동차항을 가산하여 투영 효과를 조정하고, y 좌표는 방향 계수를 적용한 후 w 동차항을 가산하여 화면 공간에서의 올바른 위치를 계산합니다. 이러한 과정을 통해 3D 공간의 좌표가 2D 화면 공간으로 정확하게 매핑됩니다.

_ProjectionParams.x는 1.0입니다 (또는 뒤집힌 투영 행렬로 렌더링할 때는 -1.0)

세 번째 줄은 zw 값을 클립 공간의 값과 동일하게 유지합니다!

결론: 이 변환 이후, Unity NDC 공간의 xy 좌표 범위는 [-w, w]에서 [0, w]로 변경됩니다. 다시 한 번 강조하자면, Unity NDC 공간은 여전히 동차 좌표계입니다!!!!

스크린 공간:

스크린 공간의 좌표 범위는 [0, 1]이며, 일반적으로 스크린 공간의 좌표를 직접 제어할 수는 없습니다. 하지만 위의 NDC 공간에서 [0, 1] 범위의 xy 스크린 좌표를 얻을 수 있습니다.

NDCSpace.xy / NDCSpace.w //[0, 1] 사이의 xy 화면 좌표를 가져옵니다.

공간 변환(Unity 소스 코드 포함)

소스 코드 스크린샷이 보기에 좋지 않아서, 아래에 직접 작성하겠습니다.

// VS --> HCS의 버텍스

// 균일한 클립 공간으로 변환
float4x4 GetWorldToHClipMatrix()
{
    return UNITY_MATRIX_VP;
}
// 월드 공간에서 동차 공간으로 위치 변환
float4 TransformWorldToHClip(float3 positionWS)
{
    return mul(GetWorldToHClipMatrix(), float4(positionWS, 1.0));
}

positionCS = TransformWorldToHClip(input.positionWS);

// VS 노말 --> HCS

// 균일한 클립 공간으로 변환
float4x4 GetWorldToHClipMatrix()
{
    return UNITY_MATRIX_VP;
}

// 월드 스페이스에서 동질 공간으로 벡터를 변환합니다.
real3 TransformWorldToHClipDir(real3 directionWS, bool doNormalize = false)
{
    float3 dirHCS = mul((real3x3)GetWorldToHClipMatrix(), directionWS).xyz;
    if (doNormalize)
        return normalize(dirHCS);

    return dirHCS;
}

normalCS = TransformWorldToHClipDir(input.normalWS);