다음을 통해 공유


스트림 출력 단계 시작

이 섹션에서는 스트림 출력 단계에서 기하 도형 셰이더를 사용하는 방법을 설명합니다.

기하 도형 셰이더 컴파일

이 GS(기하 도형 셰이더)는 각 삼각형의 얼굴 노멀을 계산하고 위치, 표준 및 텍스처 좌표 데이터를 출력합니다.

struct GSPS_INPUT
{
    float4 Pos : SV_POSITION;
    float3 Norm : TEXCOORD0;
    float2 Tex : TEXCOORD1;
};

[maxvertexcount(12)]
void GS( triangle GSPS_INPUT input[3], inout TriangleStream<GSPS_INPUT> TriStream )
{
    GSPS_INPUT output;
    
    //
    // Calculate the face normal
    //
    float3 faceEdgeA = input[1].Pos - input[0].Pos;
    float3 faceEdgeB = input[2].Pos - input[0].Pos;
    float3 faceNormal = normalize( cross(faceEdgeA, faceEdgeB) );
    float3 ExplodeAmt = faceNormal*Explode;
    
    //
    // Calculate the face center
    //
    float3 centerPos = (input[0].Pos.xyz + input[1].Pos.xyz + input[2].Pos.xyz)/3.0;
    float2 centerTex = (input[0].Tex + input[1].Tex + input[2].Tex)/3.0;
    centerPos += faceNormal*Explode;
    
    //
    // Output the pyramid
    //
    for( int i=0; i<3; i++ )
    {
        output.Pos = input[i].Pos + float4(ExplodeAmt,0);
        output.Pos = mul( output.Pos, View );
        output.Pos = mul( output.Pos, Projection );
        output.Norm = input[i].Norm;
        output.Tex = input[i].Tex;
        TriStream.Append( output );
        
        int iNext = (i+1)%3;
        output.Pos = input[iNext].Pos + float4(ExplodeAmt,0);
        output.Pos = mul( output.Pos, View );
        output.Pos = mul( output.Pos, Projection );
        output.Norm = input[iNext].Norm;
        output.Tex = input[iNext].Tex;
        TriStream.Append( output );
        
        output.Pos = float4(centerPos,1) + float4(ExplodeAmt,0);
        output.Pos = mul( output.Pos, View );
        output.Pos = mul( output.Pos, Projection );
        output.Norm = faceNormal;
        output.Tex = centerTex;
        TriStream.Append( output );
        
        TriStream.RestartStrip();
    }
    
    for( int i=2; i>=0; i-- )
    {
        output.Pos = input[i].Pos + float4(ExplodeAmt,0);
        output.Pos = mul( output.Pos, View );
        output.Pos = mul( output.Pos, Projection );
        output.Norm = -input[i].Norm;
        output.Tex = input[i].Tex;
        TriStream.Append( output );
    }
    TriStream.RestartStrip();
}

이 코드를 염두에 두고 기하 도형 셰이더는 꼭짓점 또는 픽셀 셰이더와 비슷하지만 함수, 입력 매개 변수 선언 및 내장 함수에서 반환되는 형식은 예외입니다.

항목 설명
함수 반환 형식
함수 반환 형식은 한 가지 작업을 수행하며 셰이더에서 출력할 수 있는 최대 꼭짓점 수를 선언합니다. 이 경우
maxvertexcount(12)

는 출력을 최대 12개의 꼭짓점으로 정의합니다.

입력 매개 변수 선언

이 함수는 다음 두 개의 입력 매개 변수를 사용합니다.

triangle GSPS_INPUT input[3] , inout TriangleStream<GSPS_INPUT> TriStream

첫 번째 매개 변수는 꼭짓점당 데이터를 위치, 법선 및 텍스처 좌표로 정의하는 GSPS_INPUT 구조로 정의된 꼭짓점 배열(이 경우 3)입니다. 또한 첫 번째 매개 변수는 삼각형 키워드(keyword) 사용합니다. 즉, 입력 어셈블러 단계에서 삼각형 기본 형식(삼각형 목록 또는 삼각형 스트립) 중 하나로 기하 도형 셰이더에 데이터를 출력해야 합니다.

두 번째 매개 변수는 TriangleStream<GSPS_INPUT> 형식으로 정의된 삼각형 스트림입니다. 즉, 매개 변수는 각각 세 개의 꼭짓점(GSPS_INPUT 멤버의 데이터를 포함)으로 구성된 삼각형 배열입니다.

삼각형 및 삼각형 스트림 키워드(keyword) 사용하여 GS에서 개별 삼각형 또는 삼각형 스트림을 식별합니다.

내장 함수

셰이더 함수의 코드 줄은 Append 및 RestartStrip를 호출하는 마지막 두 줄을 제외하고 공통 셰이더 코어 HLSL 내장 함수를 사용합니다. 이러한 함수는 기하 도형 셰이더에서만 사용할 수 있습니다. 추가는 현재 스트립에 출력을 추가하도록 기하 도형 셰이더에 알릴 수 있습니다. RestartStrip은 새 기본 스트립을 만듭니다. 새 스트립은 GS 단계의 모든 호출에서 암시적으로 만들어집니다.

나머지 셰이더는 꼭짓점 또는 픽셀 셰이더와 매우 유사합니다. 기하 도형 셰이더는 구조를 사용하여 입력 매개 변수를 선언하고 위치 멤버를 SV_POSITION 의미 체계로 표시하여 하드웨어에 위치 데이터임을 알릴 수 있습니다. 입력 구조는 다른 두 입력 매개 변수를 텍스처 좌표로 식별합니다(그 중 하나에 얼굴 표준이 포함되더라도). 원하는 경우 얼굴 표준에 고유한 사용자 지정 의미 체계를 사용할 수 있습니다.

기하 도형 셰이더를 디자인한 후 다음 코드 예제와 같이 D3DCompile을 호출하여 컴파일합니다.

DWORD dwShaderFlags = D3DCOMPILE_ENABLE_STRICTNESS;
ID3DBlob** ppShader;

D3DCompile( pSrcData, sizeof( pSrcData ), 
  "Tutorial13.fx", NULL, NULL, "GS", "gs_4_0", 
  dwShaderFlags, 0, &ppShader, NULL );

꼭짓점 및 픽셀 셰이더와 마찬가지로 셰이더를 컴파일하는 방법(디버깅, 속도 최적화 등), 진입점 함수 및 셰이더 모델의 유효성을 검사할 방법을 컴파일러에 알려야 합니다. 이 예제에서는 GS 함수를 사용하여 Tutorial13.fx 파일에서 빌드된 기하 도형 셰이더를 만듭니다. 셰이더는 셰이더 모델 4.0용으로 컴파일됩니다.

스트림 출력을 사용하여 Geometry-Shader 개체 만들기

기하 도형에서 데이터를 스트리밍하고 셰이더를 성공적으로 컴파일한 경우 다음 단계는 ID3D11Device::CreateGeometryShaderWithStreamOutput을 호출하여 기하 도형 셰이더 개체를 만드는 것입니다.

하지만 먼저 SO(스트림 출력) 단계 입력 서명을 선언해야 합니다. 이 서명은 개체를 만들 때 GS 출력 및 SO 입력과 일치하거나 유효성을 검사합니다. 다음 코드는 SO 선언의 예입니다.

D3D11_SO_DECLARATION_ENTRY pDecl[] =
{
    // semantic name, semantic index, start component, component count, output slot
    { "SV_POSITION", 0, 0, 4, 0 },   // output all components of position
    { "TEXCOORD0", 0, 0, 3, 0 },     // output the first 3 of the normal
    { "TEXCOORD1", 0, 0, 2, 0 },     // output the first 2 texture coordinates
};

D3D11Device->CreateGeometryShaderWithStreamOut( pShaderBytecode, ShaderBytecodesize, pDecl, 
    sizeof(pDecl), NULL, 0, 0, NULL, &pStreamOutGS );

이 함수는 다음을 비롯한 여러 매개 변수를 사용합니다.

  • 컴파일된 기하 도형 셰이더(또는 기하 도형 셰이더가 없고 데이터가 꼭짓점 셰이더에서 직접 스트리밍되는 경우 꼭짓점 셰이더)에 대한 포인터입니다. 이 포인터를 가져오는 방법에 대한 자세한 내용은 컴파일된 셰이더에 대한 포인터 가져오기를 참조 하세요.
  • 스트림 출력 단계의 입력 데이터를 설명하는 선언 배열에 대한 포인터입니다. (참조)D3D11_SO_DECLARATION_ENTRY.) SO 단계에서 출력할 요소의 각 유형에 대해 하나씩 최대 64개의 선언을 제공할 수 있습니다. 선언 항목의 배열은 단일 버퍼 또는 여러 버퍼만 스트림 출력에 바인딩되어야 하는지 여부에 관계없이 데이터 레이아웃을 설명합니다.
  • SO 스테이지에서 작성한 요소 수입니다.
  • 생성된 기하 도형 셰이더 개체에 대한 포인터입니다(ID3D11GeometryShader 참조).

이 경우 버퍼 보폭은 NULL이고, 래스터라이저로 보낼 스트림의 인덱스는 0이고, 클래스 링크 인터페이스는 NULL입니다.

스트림 출력 선언은 데이터가 버퍼 리소스에 기록되는 방식을 정의합니다. 출력 선언에 원하는 만큼 구성 요소를 추가할 수 있습니다. SO 단계를 사용하여 단일 버퍼 리소스 또는 여러 버퍼 리소스에 씁니다. 단일 버퍼의 경우 SO 단계에서는 꼭짓점당 다양한 요소를 작성할 수 있습니다. 여러 버퍼의 경우 SO 단계에서는 꼭짓점당 데이터의 단일 요소만 각 버퍼에 쓸 수 있습니다.

기하 도형 셰이더를 사용하지 않고 SO 단계를 사용하려면 ID3D11Device::CreateGeometryShaderWithStreamOutput을 호출하고 pShaderBytecode 매개 변수에 꼭짓점 셰이더에 대한 포인터를 전달합니다.

출력 대상 설정

마지막 단계는 SO 스테이지 버퍼를 설정하는 것입니다. 나중에 사용하기 위해 메모리에서 하나 이상의 버퍼로 데이터를 스트리밍할 수 있습니다. 다음 코드에서는 꼭짓점 데이터뿐만 아니라 SO 단계에서 데이터를 스트리밍하는 데 사용할 수 있는 단일 버퍼를 만드는 방법을 보여줍니다.

ID3D11Buffer *m_pBuffer;
int m_nBufferSize = 1000000;

D3D11_BUFFER_DESC bufferDesc =
{
    m_nBufferSize,
    D3D11_USAGE_DEFAULT,
    D3D11_BIND_STREAM_OUTPUT,
    0,
    0,
    0
};
D3D11Device->CreateBuffer( &bufferDesc, NULL, &m_pBuffer );

ID3D11Device::CreateBuffer를 호출 하여 버퍼를 만듭니다. 이 예제에서는 CPU에서 상당히 자주 업데이트될 것으로 예상되는 버퍼 리소스에 일반적인 기본 사용량을 보여 줍니다. 바인딩 플래그는 리소스를 바인딩할 수 있는 파이프라인 단계를 식별합니다. SO 단계에서 사용하는 모든 리소스도 바인딩 플래그 D3D10_BIND_STREAM_OUTPUT 사용하여 만들어야 합니다.

버퍼가 성공적으로 만들어지면 ID3D11DeviceContext::SOSetTargets를 호출하여 현재 디바이스로 설정합니다.

UINT offset[1] = 0;
D3D11Device->SOSetTargets( 1, &m_pBuffer, offset );

이 호출은 버퍼 수, 버퍼에 대한 포인터 및 오프셋 배열(데이터 쓰기를 시작할 위치를 나타내는 각 버퍼에 대한 하나의 오프셋)을 사용합니다. 그리기 함수가 호출될 때 데이터는 이러한 스트리밍 출력 버퍼에 기록됩니다. 내부 변수는 스트리밍 출력 버퍼에 데이터를 쓰기 시작할 위치를 추적하며, SOSetTargets가 다시 호출되고 새 오프셋 값이 지정될 때까지 해당 변수는 계속 증가합니다.

대상 버퍼에 기록된 모든 데이터는 32비트 값입니다.

스트림 출력 단계