Mulai menggunakan Tahap Stream-Output

Bagian ini menjelaskan cara menggunakan shader geometri dengan tahap output aliran.

Mengkompilasi Geometry Shader

Shader geometri (GS) ini menghitung wajah normal untuk setiap segitiga, dan posisi output, data koordinat normal dan tekstur.

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

Mengingat kode tersebut, pertimbangkan bahwa shader geometri terlihat seperti vertex atau piksel shader, tetapi dengan pengecualian berikut: jenis yang dikembalikan oleh fungsi, deklarasi parameter input, dan fungsi intrinsik.

Item Deskripsi
Jenis pengembalian fungsi
Jenis pengembalian fungsi melakukan satu hal, menyatakan jumlah maksimum simpul yang dapat dihasilkan oleh shader. Dalam hal ini,
maxvertexcount(12)

mendefinisikan output menjadi maksimum 12 simpul.

Deklarasi parameter input

Fungsi ini mengambil dua parameter input:

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

Parameter pertama adalah array simpul (3 dalam hal ini) yang ditentukan oleh struktur GSPS_INPUT (yang mendefinisikan data per-vertex sebagai posisi, koordinat normal dan tekstur). Parameter pertama juga menggunakan kata kunci segitiga, yang berarti tahap perakitan input harus menghasilkan data ke shader geometri sebagai salah satu jenis primitif segitiga (daftar segitiga atau strip segitiga).

Parameter kedua adalah aliran segitiga yang ditentukan oleh jenis TriangleStream<GSPS_INPUT>. Ini berarti parameter adalah array segitiga, yang masing-masing terdiri dari tiga simpul (yang berisi data dari anggota GSPS_INPUT).

Gunakan kata kunci segitiga dan segitiga untuk mengidentifikasi segitiga individual atau aliran segitiga dalam GS.

Fungsi intrinsik

Baris kode dalam fungsi shader menggunakan fungsi intrinsik HLSL common-shader-core kecuali dua baris terakhir, yang memanggil Tambahkan dan RestartStrip. Fungsi-fungsi ini hanya tersedia untuk shader geometri. Tambahkan menginformasikan shader geometri untuk menambahkan output ke strip saat ini; RestartStrip membuat strip primitif baru. Strip baru dibuat secara implisit di setiap pemanggilan tahap GS.

Sisa shader terlihat sangat mirip dengan vertex atau pixel shader. Shader geometri menggunakan struktur untuk mendeklarasikan parameter input dan menandai anggota posisi dengan semantik SV_POSITION untuk memberi tahu perangkat keras bahwa ini adalah data posisional. Struktur input mengidentifikasi dua parameter input lainnya sebagai koordinat tekstur (meskipun salah satunya akan berisi wajah normal). Anda dapat menggunakan semantik kustom Anda sendiri untuk wajah normal jika Anda mau.

Setelah merancang shader geometri, panggil D3DCompile untuk mengkompilasi seperti yang ditunjukkan dalam contoh kode berikut.

DWORD dwShaderFlags = D3DCOMPILE_ENABLE_STRICTNESS;
ID3DBlob** ppShader;

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

Sama seperti shader vertex dan piksel, Anda memerlukan bendera shader untuk memberi tahu pengkompilasi bagaimana Anda ingin shader dikompilasi (untuk penelusuran kesalahan, dioptimalkan untuk kecepatan, dan sebagainya), fungsi titik masuk, dan model shader untuk divalidasi. Contoh ini membuat shader geometri yang dibangun dari file Tutorial13.fx, dengan menggunakan fungsi GS. Shader dikompilasi untuk model shader 4.0.

Membuat Objek Geometri-Shader dengan Output Aliran

Setelah Anda tahu bahwa Anda akan mengalirkan data dari geometri, dan Anda telah berhasil mengompilasi shader, langkah selanjutnya adalah memanggil ID3D11Device::CreateGeometryShaderWithStreamOutput untuk membuat objek shader geometri.

Tetapi pertama-tama, Anda perlu mendeklarasikan tanda tangan input tahap output aliran (SO). Tanda tangan ini cocok atau memvalidasi output GS dan input SO pada saat pembuatan objek. Kode berikut adalah contoh deklarasi 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 );

Fungsi ini mengambil beberapa parameter termasuk:

  • Penunjuk ke shader geometri yang dikompilasi (atau shader vertex jika tidak ada shader geometri yang akan ada dan data akan dialirkan langsung dari shader vertex). Untuk informasi tentang cara mendapatkan pointer ini, lihat Mendapatkan Pointer ke Shader yang Dikompilasi.
  • Penunjuk ke array deklarasi yang menjelaskan data input untuk tahap output aliran. (Lihat D3D11_SO_DECLARATION_ENTRY.) Anda dapat menyediakan hingga 64 deklarasi, satu untuk setiap jenis elemen yang berbeda untuk menjadi output dari tahap SO. Array entri deklarasi menjelaskan tata letak data terlepas dari apakah hanya satu buffer atau beberapa buffer yang akan terikat untuk output aliran.
  • Jumlah elemen yang ditulis oleh tahap SO.
  • Penunjuk ke objek shader geometri yang dibuat (lihat ID3D11GeometryShader).

Dalam situasi ini, langkah buffer adalah NULL, indeks aliran yang akan dikirim ke rasterizer adalah 0, dan antarmuka tautan kelas adalah NULL.

Deklarasi output aliran menentukan cara data ditulis ke sumber daya buffer. Anda dapat menambahkan komponen sebanyak yang Anda inginkan ke deklarasi output. Gunakan tahap SO untuk menulis ke satu sumber daya buffer atau banyak sumber daya buffer. Untuk satu buffer, tahap SO dapat menulis banyak elemen yang berbeda per vertex. Untuk beberapa buffer, tahap SO hanya dapat menulis satu elemen data per vertex ke setiap buffer.

Untuk menggunakan tahap SO tanpa menggunakan shader geometri, panggil ID3D11Device::CreateGeometryShaderWithStreamOutput dan teruskan penunjuk ke shader vertex ke parameter pShaderBytecode.

Atur Target Output

Langkah terakhir adalah mengatur buffer tahap SO. Data dapat dialirkan ke satu atau beberapa buffer dalam memori untuk digunakan nanti. Kode berikut menunjukkan cara membuat buffer tunggal yang dapat digunakan untuk data vertex, serta untuk tahap SO untuk mengalirkan data.

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

Buat buffer dengan memanggil ID3D11Device::CreateBuffer. Contoh ini menggambarkan penggunaan default, yang khas untuk sumber daya buffer yang diharapkan cukup sering diperbarui oleh CPU. Bendera pengikatan mengidentifikasi tahap alur yang dapat diikat sumber daya. Sumber daya apa pun yang digunakan oleh tahap SO juga harus dibuat dengan bendera ikatan D3D10_BIND_STREAM_OUTPUT.

Setelah buffer berhasil dibuat, atur ke perangkat saat ini dengan memanggil ID3D11DeviceContext::SOSetTargets:

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

Panggilan ini mengambil jumlah buffer, pointer ke buffer, dan array offset (satu offset ke masing-masing buffer yang menunjukkan tempat untuk mulai menulis data). Data akan ditulis ke buffer streaming-output ini ketika fungsi gambar dipanggil. Variabel internal melacak posisi tempat mulai menulis data ke buffer streaming-output, dan variabel tersebut akan terus meningkat sampai SOSetTargets dipanggil lagi dan nilai offset baru ditentukan.

Semua data yang ditulis ke buffer target akan menjadi nilai 32-bit.

Tahap Stream-Output