스트림 출력 단계 시작
이 섹션에서는 스트림 출력 단계에서 기하 도형 셰이더를 사용하는 방법을 설명합니다.
기하 도형 셰이더 컴파일
이 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();
}
이 코드를 염두에 두고 기하 도형 셰이더는 꼭짓점 또는 픽셀 셰이더와 비슷하지만 함수, 입력 매개 변수 선언 및 내장 함수에서 반환되는 형식은 예외입니다.
항목 | 설명 | |
---|---|---|
함수 반환 형식 |
함수 반환 형식은 한 가지 작업을 수행하며 셰이더에서 출력할 수 있는 최대 꼭짓점 수를 선언합니다. 이 경우
는 출력을 최대 12개의 꼭짓점으로 정의합니다. |
|
입력 매개 변수 선언 |
이 함수는 다음 두 개의 입력 매개 변수를 사용합니다.
첫 번째 매개 변수는 꼭짓점당 데이터를 위치, 법선 및 텍스처 좌표로 정의하는 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비트 값입니다.