이번 포스팅은 저번 포스팅에 이어 Tessellator Stage에 대한 이야기를 해볼까 합니다.
이전에 Hull Shader Stage에서 폴리곤을 어떤 기준으로 그리고 얼마나 분할 할 것인가를 정했습니다.
Tessellator Stage에서는 이 결과를 Input으로 받아 Domain Shader에서 사용할 '무게중심'을 계산합니다.
하지만 이 스테이지는 프로그래머가 제어 할 수 없는 영역이기 때문에, 이 계산과정에 대한 설명만을 설명 한 뒤 이번 포스팅은 넘어가도록 하겠습니다.
1. 무게중심좌표
무게중심좌표를 구하는 방법은 다양합니다. Vertex Averaging을 통해 나온 결과도 무게 중심좌표이지만 지금 설명하려는 방법으로도 구할 수 있습니다.
설명에 앞서 다음과 같은 외적의 기하학적 의미를 알고 있어야 합니다.
- A X B 의 결과는 두 벡터에 수직인 벡터 C 다.
- C 의 길이는 A와 B로 이루어진 평행사변형의 넓이다.
이 성질은 다음과 같은 중심좌표 표현법에서 유용하게 사용됩니다.
위 그림에선 중심좌표 P를 각 꼭지점의 비율로 구성합니다.
다만 w (가중치)는 어떻게 구할 수 있을까요?
방법은 각 꼭지점의 맞은편에 있는 삼각형의 넓이를 전체 삼각형의 넓이로 나누는 것 입니다.
- w1 = 삼각형 BCP의 넓이 / 삼각형 ABC의 넓이
- w2 = 삼각형 ACP의 넓이 / 삼각형 ABC의 넓이
- w3 = 삼각형 ABP의 넓이 / 삼각형 ABC의 넓이
- w1 + w2 + w3 = 1 만족
이 방법은 비율을 따져서 중심좌표를 구하기 때문에, Triangle에서도 Quad에서도 사용가능하며 도형의 면적을 이용한 좌표라해서
'면적좌표'라고도 불립니다.
이 과정을 CG 언어로 작성하면 다음과 같이 작성 할 수 있습니다.
float3 CalculateBC(float3 vecP,
float3 vecA,
float3 vecB,
float3 vecC)
{
float3 vecResult;
float3 vecCross;
float3 a2b = vecB - vecA;
float3 a2c = vecC - vecA;
float3 a2p = vecP - vecA;
vecCross = cross(a2b, a2c);
float totalArea = length (vecCross);
vecCross = cross(a2b, a2p);
float subArea = length(vecCross);
vecResult.z = subArea/totalArea;
vecCross = cross(a2c, a2p);
subArea = length(vecCross);
vecResult.y = subArea/totalArea;
vecResult.x = 1 - (vecResult.z + vecResult.y);
return vecResult;
}
테셀레이터 내부에선 위와 비슷한 방법으로 무게중심의 가중치를 구합니다. 이 과정은 앞서 설명드렸듯 프로그래머가 관여 할 수 없는 고정 기능입니다.
이후 이 가중치들은 Domain-Shader로 전달되어 사용됩니다.
(MSDN에서 정의한 Domain-Shader의 가장 기초적인 형태)
[domain("tri")]
DS_OUTPUT DS( HS_CONSTANT_DATA_OUTPUT input,
float3 UVW : SV_DomainLocation,
const OutputPatch patches
{
DS_OUTPUT Output;
...
return Output;
}
이제 기본적인 Hull Shader와 Tessellator, Domain Shader의 흐름과 개념을 익히는것이 끝났습니다.
다음 포스팅에서는 기존 Wave Deformer을 개량하는 예제와 풀이를 올리도록 하겠습니다.