Tessellation영역은 파이프라인에서 Hull Shader,Tessellator, Domain Shader를 합친 것이다. 이때 Hull Shader와 Domain Shader는 쉐이더 코드를 통해 사용할 수 있고 Tessellator는 옵션을 통해 사용해줄 수 있다.

 

Tessellatoion은 기하 구조를 더 작은 삼각형들로 분할하고 새로 생긴 정점들의 위치를 적절한 방식으로 조절해주는 것이다. 주로 지형 지물, 터레인에서 많이 사용되고 특히 GPU에서의 LOD(멀수록 퀄리티 떨어져 보임)연산에서 연관이 많다.

 

이 Tessellatoion을 사용할 때는 삼각형을 매개변수로 사용하는 것이 아닌 D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST 이런 변수를 입력으로 받는다. 이것은 4개의 제어점으로 이것을 분할해서 사용하는 것이다. Tessellation은 이 제어점을 기준으로 하는 패치가 기본 단위로 연산이 이루어 진다.

예제코드를 실행하게 되면 멀어질수록 삼각형이 적어지고 가까이 갈수록 삼각형의 개수가 많아져서 세밀하게 보이게 된다.

 

쉐이더 코드를 살펴보면 기존의 VS와 PS단계에서 추가된 코드를 볼 수 있다. 우선 Hull Shader 코드에서  각 Tessellation의 기본 단위인 패치마다 실행되는 ConstantHS 단계를 먼저 살펴보자. 이 단계에서 얼마나 더 세분화해줄 지에 관련된 계수를 출력해준다. 이 계수에 해당하는 변수는 각 변의 세분정도를 나타내는 EdgeTess와 내부의 분할정도를 나타내는 InsideTess가 있다.

struct PatchTess
{
	float EdgeTess[4]   : SV_TessFactor;
	float InsideTess[2] : SV_InsideTessFactor;
};

PatchTess ConstantHS(InputPatch<VertexOut, 4> patch, uint patchID : SV_PrimitiveID)
{
	PatchTess pt;

	//거리계산 - 패치들의 평균값
	float3 centerL = 0.25f * (patch[0].PosL + patch[1].PosL + patch[2].PosL + patch[3].PosL);
	float3 centerW = mul(float4(centerL, 1.0f), gWorld).xyz;

	//패치들과 카메라간의 거리 파악
	float d = distance(centerW, gEyePosW);


	//20(최소)~100(최대)사이
	const float d0 = 20.0f;
	const float d1 = 100.0f;
	float tess = 64.0f * saturate((d1 - d) / (d1 - d0));

	//각 변의 세분정도
	pt.EdgeTess[0] = tess;
	pt.EdgeTess[1] = tess;
	pt.EdgeTess[2] = tess;
	pt.EdgeTess[3] = tess;

	//내부의 분할정도
	pt.InsideTess[0] = tess;
	pt.InsideTess[1] = tess;

	return pt;
}

 

이 다음 단계는 출력하는 제어점마다 한번씩 실행되는 HS 단계로 표면을 어떻게 표현할지를 정해준다. 현재는 그냥 통과 시켜주고 있지만 설정해줄 수 있는 부분이 많다.

struct HullOut
{
	float3 PosL : POSITION;
};

[domain("quad")]
[partitioning("integer")]
[outputtopology("triangle_cw")]
[outputcontrolpoints(4)]
[patchconstantfunc("ConstantHS")]
[maxtessfactor(64.0f)]
//출력하는 제어점마다 한번씩- 표면
HullOut HS(InputPatch<VertexOut, 4> p,
	uint i : SV_OutputControlPointID,
	uint patchId : SV_PrimitiveID)
{
	HullOut hout;

	hout.PosL = p[i].PosL;

	return hout;
}

 

그 다음이 Tessellator 단계이다. 이 단계에서는 쉐이더에서 정해준 값에 따라 분할이 일어나는 단계로 GPU가 해주는 작업이고 Domain shader단계로 넘어가게 된다. 이 단계에서는 동적으로 생성과 기존 정점의 행렬연산을 해주게 된다. Tesselation의 VS단계라고 보면 된다.

struct DomainOut
{
	float4 PosH : SV_POSITION;
};

// The domain shader is called for every vertex created by the tessellator.  
// It is like the vertex shader after tessellation.
[domain("quad")]
DomainOut DS(PatchTess patchTess,
	float2 uv : SV_DomainLocation,
	const OutputPatch<HullOut, 4> quad)
{
	DomainOut dout;

	// Bilinear interpolation.
	float3 v1 = lerp(quad[0].PosL, quad[1].PosL, uv.x);
	float3 v2 = lerp(quad[2].PosL, quad[3].PosL, uv.x);
	float3 p = lerp(v1, v2, uv.y);

	// Displacement mapping
	p.y = 0.3f * (p.z * sin(p.x) + p.x * cos(p.z));

	dout.PosH = mul(float4(p, 1.0f), gWorldViewProj);

	return dout;
}

 

Tesselation은 GS단계와 비슷하지만 하나의 물체가 있는 상태에서 분할하는 느낌이고 GS단계는 별도의 물체를 생성할 수도 있는 느낌이라고 생각하면 된다. GS는 파티클이나 빌보드에 많이 사용되고 Tesselation은 Terrain같은 지형지물에 많이 사용한다.

+ Recent posts