기하 쉐이더(GS)
기하 쉐이더(Geometry Shader,GS)
기하 쉐이더는 VertexShaer와 PixelShader 사이에 존재하는 하나의 스테이지
- 입력 받는 것 :
Primitive
(도형) - 출력 하는 것 :
수정된 Primitive
- 해당 과정을 통해,
동적인 수정 or 일부를 제거
하는 쉐이딩 단계이다
기하 정보와 관련된
VS ~ GS
를 별도로
Geometry Pipeline이라 퉁치기도 함
요점은 대략적인 Mesh를 받은 후
- 고퀄리티 Mesh를 GPU에서 만듦(CPU -> GPU를 가볍게)
- SubDivision 같은 기술을 이용하여 실시간 메시를 변형 가능
IA에서 이미 삼각형이 정점 3개가 필요하다는 사실을 알고 있기에
GS에 들어오는 것은 여러 정점 데이터가 한번에 들어올 수 있음
(Primitive)
구체적으로 하는 일? 사례?
-
- 추가적인 생성
- Vertex 하나를 ‘사각형’으로 만들어줄 수 있음
(추가적인 폴리곤 생성으로 더 부드럽거나 독특한 도형으로 변형)
- 추가적인 생성
-
- LOD (Level of Detail)
- 카메라와의 거리를 통해 삼각형을 늘리거나 줄임
- LOD (Level of Detail)
유의할 점들
-
동적인 Primitive 수정이 가능하지만
종종 병목 현상의 원인이 되기도 함
(최신 그래픽스의 경우, Compute Shader, Mesh Shader 등을 고려하는 편) -
한번의 GS 호출에서 출력하는 Vertex에 제한 존재
(D3D11 : 약 1024) -
쉐이더를 GSSetShader로 등록 시,
한번의 Draw 콜에서 ‘계속’ 쉐이더로 남아있기에
다른 물체들이 GS를 사용하지 않는다면
GSSetShader(nullptr,0,0) 같은 방식으로
GS쉐이더의 사용을 해제해야 함
Render()
{
context->GSSetConstantBuffers(0, 1, m_constantBuffer.GetAddressOf());
context->GSSetShader(m_geometryShader.Get(), 0, 0);
// draw Call...
context->GSSetShader(nullptr, 0, 0);
}
이전 세부 단계는 나중에 다룰 예정
(Hull,Domain, Tessellator)
예제 - GS를 이용하여 정점을 삼각형으로 변환
// Geometry-Shader Object
// https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-geometry-shader
// Stream-Output Object
// https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-so-type
cbuffer BillboardPointsConstantData : register(b0)
{
float3 eyeWorld;
float width;
Matrix model; // For vertex shader
Matrix view; // For vertex shader
Matrix proj; // For vertex shader
};
struct GeometryShaderInput
{
float4 pos : SV_POSITION;
};
struct PixelShaderInput
{
float4 pos : SV_POSITION; // not POSITION
uint primID : SV_PrimitiveID;
};
//TODO: PointStream -> TriangleStream
[maxvertexcount(100)] // 최대 출력 Vertex 갯수
void main(point GeometryShaderInput input[1], uint primID : SV_PrimitiveID,
inout PointStream<PixelShaderInput> outputStream)
{
PixelShaderInput output;
output.pos = input[0].pos;
for (int i = 0; i < 100; i ++)
{
output.pos = input[0].pos + float4(0.0, 0.003, 0.0, 0.0) * float(i);
output.pos = mul(output.pos, view);
output.pos = mul(output.pos, proj);
output.primID = primID;
outputStream.Append(output);
}
}
-
VS -> GS -> PS 이기에
VS에서 입력을 받고
GS에서는 PS의 결과물로 return 한다
(inout) -
예제 목표는 저 PointStream을 Triangle로 바꾸는 것
- point GeometryShaderInput input[1]
- point : 입력 Primitive를 지정 (Point : 점 1개, line : 선 1개 등)
- GeometryShaderInput : 우리가 정의한 입력 구조체 타입
-
[1] : 도형을 구성하는 정점 개수(Point라서 점 1개)
-
- SV_PrimitiveID?
- 현재 쉐이더에서 처리 중인 도형의 고유 ID
(삼각형의 단위를 표현)
(PS 의 Output이며, ps에서 사용 가능)
(ex : 3번째 삼각형마다 색 변경 등)
(저 primID는 GPU가 넣어주는 값이기에 VS에서 생각할 필요 없음)
- SV_PrimitiveID?
-
- [maxvertexcount(100)]
- 최대 출력 Vertex (아까 말했듯 32비트 환경에선 1024로 제한됨)
- [maxvertexcount(100)]
- 어차피 POINTLIST로 IA에서 설정하였고
정점 5개를 받아 각각 물체를 만들어주는 것이 목표
예제 수정 코드
[maxvertexcount(100)] // 최대 출력 Vertex 갯수
void main(point GeometryShaderInput input[1], uint primID : SV_PrimitiveID,
inout TriangleStream<PixelShaderInput> outputStream)
{
float hw = 0.5 * width;
PixelShaderInput o1;
o1.pos = input[0].pos + float4(-hw, -hw, 0.0, 0.0);
o1.pos = mul(o1.pos, view);
o1.pos = mul(o1.pos, proj);
o1.primID = primID;
outputStream.Append(o1);
o1.pos = input[0].pos + float4(-hw, hw, 0.0, 0.0);
o1.pos = mul(o1.pos, view);
o1.pos = mul(o1.pos, proj);
outputStream.Append(o1);
o1.pos = input[0].pos + float4(hw, hw, 0.0, 0.0);
o1.pos = mul(o1.pos, view);
o1.pos = mul(o1.pos, proj);
outputStream.Append(o1);
outputStream.RestartStrip();
o1.pos = input[0].pos + float4(-hw, -hw, 0.0, 0.0);
o1.pos = mul(o1.pos, view);
o1.pos = mul(o1.pos, proj);
outputStream.Append(o1);
o1.pos = input[0].pos + float4(hw, hw, 0.0, 0.0);
o1.pos = mul(o1.pos, view);
o1.pos = mul(o1.pos, proj);
outputStream.Append(o1);
o1.pos = input[0].pos + float4(hw, -hw, 0.0, 0.0);
o1.pos = mul(o1.pos, view);
o1.pos = mul(o1.pos, proj);
outputStream.Append(o1);
}
- 삼각형으로 출력할 때는
output에 맞게 각 요소에
수정
- 시계 방향으로 위치를 조작
- 이후 outputStream에 Append
- 삼각형을 그린 후, RestartStrip을 통하여 끊어주기
- 시계 방향으로 위치를 조작
- 내부적으로 그릴때 TriangleStrip을 사용하기에
2가지 방식으로 구현이 가능하다
- POINTLIST 처럼 각각의 독립적인 3각형 2개를 합침 - 현재 방식
- Strip을 이용하여 정점 4개만을 이용하여 그리는 방식
- POINTLIST 처럼 각각의 독립적인 3각형 2개를 합침 - 현재 방식
만약 4개로 그린다면?
// 좌 하
o1.pos = input[0].pos + float4(-hw, -hw, 0.0, 0.0);
o1.pos = mul(o1.pos, view);
o1.pos = mul(o1.pos, proj);
o1.primID = primID;
outputStream.Append(o1);
// 좌 상
o1.pos = input[0].pos + float4(-hw, hw, 0.0, 0.0);
o1.pos = mul(o1.pos, view);
o1.pos = mul(o1.pos, proj);
outputStream.Append(o1);
// 우 하
o1.pos = input[0].pos + float4(hw, -hw, 0.0, 0.0);
o1.pos = mul(o1.pos, view);
o1.pos = mul(o1.pos, proj);
outputStream.Append(o1);
// 우 상
o1.pos = input[0].pos + float4(hw, hw, 0.0, 0.0);
o1.pos = mul(o1.pos, view);
o1.pos = mul(o1.pos, proj);
outputStream.Append(o1);
결과
- 첫 예제라 비교적 가벼운 내용이었다
댓글남기기