본문 바로가기

그래픽스

[그래픽스] Geometry Shader

 

Geometry Shader는 기하 정보를 더 자세하게 만드는 쉐이더이다.

글로만 보면 잘 이해가 안되니 코드를 살펴보자.

 

 

    m_constantData.width = 2.4f;
    D3D11Utils::CreateConstantBuffer(device, m_constantData, m_constantBuffer);

    // Geometry shader 초기화하기
    D3D11Utils::CreateGeometryShader(
        device, L"BillboardPointsGeometryShader.hlsl", m_geometryShader);

 

Geometry Shader 역시 ConstantBuffer을 사용할 수 있기 때문에 GeometryShader을 위한 ConstantBuffer을 만들고

VS, PS와 같은 방식으로 CreateGeometryShader을 한다. gs_5_0를 버전으로 쓰는 것 제외하고 내부 코드도 비슷하다.

 

 

    context->GSSetConstantBuffers(0, 1, m_constantBuffer.GetAddressOf());
    context->GSSetShader(m_geometryShader.Get(), 0, 0);
    
    //.. drawIndexed or draw 이후
    
    context->GSSetShader(nullptr, 0, 0);

 

Render에서 GSSetConstantBuffers와 GSSetShader을 하는 것도 동일하다. 한번 Geomtry Shader을 Set하면

다른 GeometryShader을 사용하지 않는 물체에도 적용될 수 있기 때문에 draw 이후에 nullptr로 세팅해줘야 한다.

 

 

struct PixelShaderInput
{
    float4 pos : SV_POSITION; // not POSITION
    float2 texCoord : TEXCOORD; // 텍스춰 좌표 추가
    uint primID : SV_PrimitiveID;
};


쉐이더를 보면, GeometryShader.hlsl에서 SV_PrimitiveID라는 Semantics가 새로 등장했다.

SV_PrimitiveID는 래스터라이즈를 할 때 가장 기본이 되는 최소 단위이다. 

최소 단위가 Point라면 몇번째 Point인지 삼각형이라면 몇번째 삼각형인지 지정해줄 수 있다.

 

 

[maxvertexcount(4)]
void main(point GeometryShaderInput input[1], uint primID : SV_PrimitiveID,
                              inout PointStream<PixelShaderInput> outputStream)

 

쉐이더의 메인 함수도 VS, PS와는 조금 다르다. 먼저 [maxvertexcount]는 GS가 만들어낼 수 있는 정점의 최대 개수이다.

GeomtryShaderInput은 GS에 입력하는 정보, PointStream<PixelShaderInput>은 GS에서 출력하는 정보이다.

현재는 PrimitiveTopology가 POINTLIST로 정점 1개씩 받고 있으므로 input 안의 대괄호가 [1]이고 출력 형태 역시

PointStream이다. 삼각형을 출력할 경우 TriangleStream이 된다.

 

 

 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);
    }

 

main 내부는 output에 정보를 추가해서 ouputStream에 출력될 정점들을 하나씩 추가하는 형식이다.

출력형식을 PointStream에서 TriangleStream으로 바꾸면 GS에서 삼각형을 그릴 수 있는데, GS는

TriangleStrip 방식으로 삼각형을 그리기 때문에 정점 3개로 삼각형 1개, 정점 4개로 삼각형 2개를 그릴 수 있다.

기존대로 정점 6개로 삼각형 2개를 그리는 TriangleList 방식을 사용하고 싶다면, 정점 3개로 삼각형을 하나 그리고,

outputStream.RestartStrip() 함수를 이용해 다시 정점3개로 삼각형을 그릴 수 있다.