1. tess.fx 에서 Hull Shader
// ===========
// Hull Shader
// ===========
// Patch Constant Function
// 패치의 분할 횟수를 지정하는 함수
struct PatchOutput
{
float Edges[3] : SV_TessFactor;
float Inside : SV_InsideTessFactor;
};
PatchOutput PatchConstFunc(InputPatch<VS_INOUT, 3> _input
, uint PatchID : SV_PrimitiveID)
{
PatchOutput output = (PatchOutput) 0.f;
output.Edges[0] = g_float_0;
output.Edges[1] = g_float_0;
output.Edges[2] = g_float_0;
output.Inside = g_float_0;
return output;
}
struct HS_OUT
{
float3 vPos : POSITION;
float2 vUV : TEXCOORD;
};
[domain("tri")]
[partitioning("integer")]
[outputtopology("triangle_cw")]
[outputcontrolpoints(3)]
[patchconstantfunc("PatchConstFunc")]
[maxtessfactor(64.f)]
HS_OUT HS_Tess(InputPatch<VS_INOUT, 3> _input
, uint i : SV_OutputControlPointID
, uint PatchID : SV_PrimitiveID)
{
HS_OUT output = (HS_OUT) 0.f;
output.vPos = _input[i].vPos;
output.vUV = _input[i].vUV;
return output;
}
[domain] -> 팻치 하나가 어떻게 구성되어있는지, 우리는 정점3개가 팻치1개라 tri
[partitioning("integer")] -> 분할하는 방식.. 정수와 실수가 있다.
=> integer 정수단위 분할
=> fractional_odd 실수단위 분할
[outputtopology("triangle_cw")] -> 삼각형 단위로 하나의 면으로 보는 방식
[outputcontrolpoints(3)] -> 우린 트라이앵글 사용해서 3개
[patchconstantfunc("PatchConstFunc")] -> 팻치 상수함수가 누구인지 알려주기.
=> 홀 쉐이더가 호출될 때 정점마다 홀 쉐이더가 호출되고 3개씩 팻치를 묶어서 팻치당 실행될 함수는 누군지 알려줘야 컴파일러가 컴파일할 때 적혀진 함수보고 함수명 (PatchConstFunc)을 찾아서 팻치마다 실행해줄 함수인지 구분해서 컴파일 할 때 같이 묶어서 진행한다.
[maxtessfactor(64.f)] -> 분할할 수 있는 최대 개수 64개로 제한.
=> 나중에는 고정된 숫자로 하지 않고.. 카메라에서 가까워지면 분할 개수 늘려주고, 멀어지면 분할 개수를 줄여줄 수 있게 변수화 시킬 것이다. 수식으로 하면 프로그램이 너무 늦어질 수 있기 때문에..
버텍스 쉐이더에서 리턴시킨 값이 VS_Pos가 아니라 로컬 Pos이다.
why? 버텍스 쉐이더는 이제 더이상 최종 마지막 단계가 아니기 때문이다.
홀 쉐이더도 이걸 그대로 받아서 그대로 넘기면서 분할 레벨을 2로 잡았다.
그대로 넘긴 이유 ? 모든 분할을 로컬 안에서 다 해놓고 일괄적으로 좌표계 변환 진행하기 위해서.
2. tess.fx 에서 Domain Shader
struct DS_OUT
{
float4 vPosition : SV_Position;
float2 vUV : TEXCOORD;
};
[domain("tri")]
DS_OUT DS_Tess(const OutputPatch<HS_OUT, 3> _origin
, float3 _vWeight : SV_DomainLocation
, PatchOutput _patchtess)
{
DS_OUT output = (DS_OUT) 0.f;
//float3 vLocalPos = (_origin[0].vPos * _vWeight[0]) + (_origin[1].vPos * _vWeight[1]) + (_origin[2].vPos * _vWeight[2]);
//float2 vUV = (_origin[0].vUV * _vWeight[0]) + (_origin[1].vUV * _vWeight[1]) + (_origin[2].vUV * _vWeight[2]);
float3 vLocalPos = (float3) 0.f;
float2 vUV = (float2) 0.f;
for (int i = 0; i < 3; ++i)
{
vLocalPos += _origin[i].vPos * _vWeight[i];
vUV += _origin[i].vUV * _vWeight[i];
}
output.vPosition = mul(float4(vLocalPos, 1.f), g_matWVP);
output.vUV = vUV;
return output;
}
도메인 쉐이더에선 이제 최종 마지막 단계이기 때문에 SV_Pos를 준다.
why 마지막..? 지오메트리 쉐이더까지 있지 않아서 도메인 쉐이더 다음이 레스터라이저이기 때문이다.
만약 지오메트리 쉐이더가 있다면 도메인 쉐이더 다음이 되어서 SV_Pos 를 지오메트리가 사용하게 될 것이다.
[domain("tri")] => 도메인 쉐이더는 디스크립션을 이거 하나만 작성하면 된다.
(삼각형이라서 tri)
DS_OUT DS_Tess(const OutputPatch<HS_OUT, 3> _origin
, float3 _vWeight : SV_DomainLocation
, PatchOutput _patchtess)
_origin => 홀쉐이더에서 반환한 값 3개(const OutputPatch<hs_out, 3><HS_OUT, 3>)가 들어온다.
_vWeight => 최종좌표를 각각 원본 정점으로부터 가중치를 곱해줘야한다..
(가중치를 다 더하면 무조건 1이 되어야한다..)
output.vPosition = mul(float4(vLocalPos, 1.f), g_matWVP) => 최종위치를 구하기 위해 월드뷰프로젝션작업을 해주는 것.
(원래는 로컬스페이스니까)
output. vUV => UV는 그냥 넘겨도 된다.
* ResMgr에서 pShader->SetTopology(D3D11_PRIMITIVE_TOPOLOGY_3_CONTROL_POINT_PATCHLIST); 를 꼭 지정해주어야한다.. 삼각형이라 제어점이 3개여서 3이들어간다.
현재 edge(제어점)이 2개이기 때문에 왼쪽과 같은 결과가 나왔다.. 오른쪽은 16개로 늘렸을 때의 결과이다.
ResMgr 에서 pShader->AddScalarParam(FLOAT_0, "Divide"); 를 지정해줘서 float값을 공개해서 edge(제어점)의 값을 제어할 수 있게 해준다.
Imgui 에서 내가 tessmtrl 에서 컨트롤 할 수 있게 만들어준다
'DirectX 11 3d' 카테고리의 다른 글
230601 절두체 컬링 이어서 (0) | 2023.06.04 |
---|---|
230531 Frustum Culling, 평면의 방정식 (1) | 2023.06.04 |
230526 Tesselation - Patch Constant Function (0) | 2023.05.26 |
230525 LandScape, Tesselation (0) | 2023.05.26 |
230524 Decal에 Light 입히기 (0) | 2023.05.25 |