기하 셰이더
실행 시점 : 정점 셰이더와 픽셀 셰이더 단계 사이에서 동작
입력 값 : 온전한 기본 도형을 입력
역할 : 기하 구조를 새로 생성 및 파괴
출력 : 다른 종류의 기본 도형을 기하 셰이더가 출력
출력 하는 기본 도형들은 정점 목록으로 정의 되고, 해당 정점목록은 동차 절단 공간에 존재
기하 셰이더 프로그래밍
기하 셰이더의 일반적인 틀
[maxvertexcount(4)]
void GS(point VertexOut gin[1],
uint primID : SV_PrimitiveID,
inout TriangleStream<GeoOut> triStream)
{
//기하 셰이더 함수 본문
}
[Maxvertexcount(N)] : 한 실행에서 출력할 정점들의 최대 개수, 최대 정점 개수 특성 지정
point VertexOut gin[1] : 입력 기본 도형 점일 때 사용,
점, 띠, 삼각형, 인접성 정보를 가신 선, 인접성을 가진 목록 및 띠 중에 골라서 입력 기본 도형을 정한다
출력 매개 변수
inout : 출력 매개 변수로 기본 도형 정점을 스트림 형식으로 출력
스트림 형식들은 템플릿 형태로 구분, 점, 라인, 삼각형 목록
Pointstream<OutputVertexType>, Linestream<OutputVertexType> , triangleStream <OutputVertexType>
선과 삼각형의 경우 출력 기본 도형은 항상 띠 인데 RestartStrip() 메서드를 사용하면 선 목록이나 삼각형 목록을 표현
void StreamOutputObject< OutputVertexType>::ReStartStrip();
예시)
//최대 4개의 정점 출력
//입력 기본 도형은 선, 출력 기본 도형은 삼각형 띠
[maxvertexcount(4)]
void GS(Line VertexOut gin[2],
uint primID : SV_PrimitiveID,
inout TriangleStream<GeoOut> triStream)
{
//기하 셰이더 함수 본문
}
//최대 32개의 정점 출력
//입력 기본 도형은 삼각형, 출력 기본 도형은 삼각형 띠
[maxvertexcount(32)]
void GS(triangle VertexOut gin[3],
uint primID : SV_PrimitiveID,
inout TriangleStream<GeoOut> triStream)
{
//기하 셰이더 함수 본문
}
//최대 4개의 정점 출력
//입력 기본 도형은 점, 출력 기본 도형은 삼각형 띠
[maxvertexcount(4)]
void GS(point VertexOut gin[1],
uint primID : SV_PrimitiveID,
inout TriangleStream<GeoOut> triStream)
{
//기하 셰이더 함수 본문
}
Append 메서드, RestartStrip 메서드 용법 예시
입력 된 삼각형을 같은 크기의 작은 삼각형 네개로 세분( subdivision) 네개의 삼각형 출력
struct VertexOut
{
float3 PosL : POSITION;
float3 NormalL : NORMAL;
float2 Tex : TEXCOORD;
};
struct GeoOut
{
float4 PosH : SV_POSITION;
float3 PosW : POSITION;
float3 NormalW : NORMAL;
float2 Tex : TEXCOORD;
float FogLerp : FOG;
};
void Subdivide(VectexOut inverts[3], out VertexOut outVerts[6])
{
// v1
// *
// / \
// / \
// m0*-----*m1
// / \ / \
// / \ / \
// *-----*-----*
// v0 m2 v2
VertexOut m[3];
//각 변의 중점 계산
m[0].PosL = 0.5f * (inverts[0].PosL + inVerts[1].PosL);
m[1].PosL = 0.5f * (inverts[1].PosL + inVerts[2].PosL);
m[2].PosL = 0.5f * (inverts[2].PosL + inVerts[0].PosL);
//단위 벡터로 변경
m[0].PosL = normalize(m[0].PosL);
m[1].PosL = normalize(m[1].PosL);
m[2].PosL = normalize(m[2].PosL);
//노말 지정
m[0].NormalL = m[0].PosL;
m[1].NormalL = m[1].PosL;
m[2].NormalL = m[2].PosL;
//텍스처 좌표 보간
m[0].Tex = 0.5f * (inverts[0].Tex + inVerts[1].Tex);
m[1].Tex = 0.5f * (inverts[1].Tex + inVerts[2].Tex);
m[2].Tex = 0.5f * (inverts[2].Tex + inVerts[0].Tex);
OutVerts[0] = inVerts[0];
OutVerts[1] = m[0];
OutVerts[2] = m[2];
OutVerts[3] = m[1];
OutVerts[4] = inVerts[2];
OutVerts[5] = inVerts[1];
}
void OutputSubdivision(VertexOut v[6],
inout TriangleStream<GeoOut> triStream)
{
Geoout gout[6];
[unroll]
for (int i = 0; i < 6; ++i)
{
//세계 공간으로 변환
gout[i].PosW = mul(float4(v[i].PosL, 1.0f), gWorld).xyz;
gout[i].NormalW = mul(v[i].NormlL, (float3x3) gWorldInTranspose);
//동차 절단 공간으로 변환
gout[i].PosH = mul((float4(v[i].PosL, 1.0f), gWorldViewProj);
gout[i].Tex = v[i].Tex;
}
// v1
// *
// / \
// / \
// m0*-----*m1
// / \ / \
// / \ / \
// *-----*-----*
// v0 m2 v2
//세분된 삼각형들을 두개의 띠로 그린다
//띠 1: 아래쪽 삼각형 세 개
//띠 2: 위쪽 삼각형 하나
[unroll]
for (int j = 0; j < 5; ++J)
{
triStream.Append(gout[j]);
}
triStream.RestartStrip();
triStream.Append(gout[1]);
triStream.Append(gout[5]);
triStream.Append(gout[3]);
}
[maxvertexcount(8)]
void GS(triangle VertexOut gin[3], inout TriangleStream<GeoOut>)
{
VertexOut v[6];
Subdivide(gin, v);
OutputSubdivision(v, triStream);
}
triStream 에는 총 8개의 정점이 만들어 지고 동작
GS 함수에서 입력 -> 분할 -> 출력을 거친다
기하 셰이더 컴파일 방법 및 PSO 포함 시키는 방법
void TreeBillboardsApp::BuildShadersAndInputLayouts()
{
//...
mShaders["treeSpriteGS"] = d3dUtil::CompileShader
(L"Shaders\\TreeSprite.hlsl", nullptr, "GS", "gs_5_0");
//...
}
void TreeBillboardsApp::BuildPSOs()
{
//...
D3D12_GRAPHICS_PIPELINE_STATE_DESC treeSpritePsoDesc = opaquePsoDesc;
treeSpritePsoDesc.GS =
{
reinterpret_cast<BYTE*>(mShaders["treeSpriteGS"]->GetBufferPointer()),
mShaders["treeSpriteGS"]->GetBufferSize()
};
//...
}
참고
기하 셰이더에서 아무것도 출력 하지 않으면 파괴, 하나의 기본도형을 완성 하기 충분한 개수의 정점을 출력 하면
미완성된 기본 도형을 폐기
참고 및 내용 인용
프랭크 D 루나 지음, 류광 옮김
한빛 미디어
Directx 12를 이용한 3D 게임 프로그래밍 입문(2017)