Bagikan melalui


Mengonversi kerangka kerja penyajian

Ringkasan

Menunjukkan cara mengonversi kerangka kerja penyajian sederhana dari Direct3D 9 ke Direct3D 11, termasuk cara mem-port buffer geometri, cara mengkompilasi dan memuat program shader HLSL, dan cara mengimplementasikan rantai penyajian di Direct3D 11. Bagian 2 dari Port, aplikasi Direct3D 9 sederhana ke panduan DirectX 11 dan Platform Windows Universal (UWP).

Mengonversi efek ke shader HLSL

Contoh berikut adalah teknik D3DX sederhana, yang ditulis untuk EFFECTS API warisan, untuk transformasi vertex perangkat keras dan data warna pass-through.

Kode shader Direct3D 9

// Global variables
matrix g_mWorld;        // world matrix for object
matrix g_View;          // view matrix
matrix g_Projection;    // projection matrix

// Shader pipeline structures
struct VS_OUTPUT
{
    float4 Position   : POSITION;   // vertex position
    float4 Color      : COLOR0;     // vertex diffuse color
};

struct PS_OUTPUT
{
    float4 RGBColor : COLOR0;  // Pixel color    
};

// Vertex shader
VS_OUTPUT RenderSceneVS(float3 vPos : POSITION, 
                        float3 vColor : COLOR0)
{
    VS_OUTPUT Output;
    
    float4 pos = float4(vPos, 1.0f);

    // Transform the position from object space to homogeneous projection space
    pos = mul(pos, g_mWorld);
    pos = mul(pos, g_View);
    pos = mul(pos, g_Projection);

    Output.Position = pos;
    
    // Just pass through the color data
    Output.Color = float4(vColor, 1.0f);
    
    return Output;
}

// Pixel shader
PS_OUTPUT RenderScenePS(VS_OUTPUT In) 
{ 
    PS_OUTPUT Output;

    Output.RGBColor = In.Color;

    return Output;
}

// Technique
technique RenderSceneSimple
{
    pass P0
    {          
        VertexShader = compile vs_2_0 RenderSceneVS();
        PixelShader  = compile ps_2_0 RenderScenePS(); 
    }
}

Di Direct3D 11, kita masih dapat menggunakan shader HLSL kita. Kami menempatkan setiap shader dalam file HLSL sendiri sehingga Visual Studio mengkompilasinya ke dalam file terpisah dan nanti, kami akan memuatnya sebagai sumber daya Direct3D terpisah. Kami mengatur tingkat target ke Shader Model 4 Level 9_1 (/4_0_level_9_1) karena shader ini ditulis untuk GPU DirectX 9.1.

Ketika kami mendefinisikan tata letak input, kami memastikannya mewakili struktur data yang sama dengan yang kami gunakan untuk menyimpan data per vertex dalam memori sistem dan dalam memori GPU. Demikian pula, output shader vertex harus cocok dengan struktur yang digunakan sebagai input ke shader piksel. Aturan tidak sama dengan meneruskan data dari satu fungsi ke fungsi lain di C++; Anda dapat menghilangkan variabel yang tidak digunakan di akhir struktur. Tetapi urutan tidak dapat diatur ulang dan Anda tidak dapat melewati konten di tengah struktur data.

Catatan Aturan di Direct3D 9 untuk mengikat shader vertex ke shader piksel lebih santai daripada aturan di Direct3D 11. Pengaturan Direct3D 9 fleksibel, tetapi tidak efisien.

 

Ada kemungkinan bahwa file HLSL Anda menggunakan sintaks yang lebih lama untuk semantik shader - misalnya, COLOR alih-alih SV_TARGET. Jika demikian, Anda harus mengaktifkan mode kompatibilitas HLSL (/opsi pengkompilasi Gec) atau memperbarui semantik shader ke sintaks saat ini. Shader puncak dalam contoh ini telah diperbarui dengan sintaks saat ini.

Berikut adalah shader vertex transformasi perangkat keras kami, kali ini didefinisikan dalam filenya sendiri.

Catatan Shader puncak diperlukan untuk menghasilkan semantik nilai sistem SV_POSITION. Semantik ini menyelesaikan data posisi puncak untuk mengoordinasikan nilai di mana x antara -1 dan 1, y adalah antara -1 dan 1, z dibagi dengan nilai koordinat homogen asli dengan nilai w (z/w), dan w dibagi 1 dengan nilai w asli (1/w).

 

Shader vertex HLSL (tingkat fitur 9.1)

cbuffer ModelViewProjectionConstantBuffer : register(b0)
{
    matrix mWorld;       // world matrix for object
    matrix View;        // view matrix
    matrix Projection;  // projection matrix
};

struct VS_INPUT
{
    float3 vPos   : POSITION;
    float3 vColor : COLOR0;
};

struct VS_OUTPUT
{
    float4 Position : SV_POSITION; // Vertex shaders must output SV_POSITION
    float4 Color    : COLOR0;
};

VS_OUTPUT main(VS_INPUT input) // main is the default function name
{
    VS_OUTPUT Output;

    float4 pos = float4(input.vPos, 1.0f);

    // Transform the position from object space to homogeneous projection space
    pos = mul(pos, mWorld);
    pos = mul(pos, View);
    pos = mul(pos, Projection);
    Output.Position = pos;

    // Just pass through the color data
    Output.Color = float4(input.vColor, 1.0f);

    return Output;
}

Ini semua yang kita butuhkan untuk pemecah piksel pass-through kita. Meskipun kami menyebutnya pass-through, itu benar-benar mendapatkan data warna interpolasi yang benar perspektif untuk setiap piksel. Perhatikan bahwa semantik nilai sistem SV_TARGET diterapkan ke output nilai warna oleh shader piksel kami seperti yang diperlukan oleh API.

Catatan Tingkat shader 9_x shader piksel tidak dapat dibaca dari semantik nilai sistem SV_POSITION. Shader piksel model 4.0 (dan lebih tinggi) dapat menggunakan SV_POSITION untuk mengambil lokasi piksel di layar, di mana x antara 0 dan lebar target render dan y antara 0 dan tinggi target render (masing-masing offset sebesar 0,5).

 

Sebagian besar shader piksel jauh lebih kompleks daripada pass through; perhatikan bahwa tingkat fitur Direct3D yang lebih tinggi memungkinkan jumlah perhitungan yang jauh lebih besar per program shader.

Shader piksel HLSL (tingkat fitur 9.1)

struct PS_INPUT
{
    float4 Position : SV_POSITION;  // interpolated vertex position (system value)
    float4 Color    : COLOR0;       // interpolated diffuse color
};

struct PS_OUTPUT
{
    float4 RGBColor : SV_TARGET;  // pixel color (your PS computes this system value)
};

PS_OUTPUT main(PS_INPUT In)
{
    PS_OUTPUT Output;

    Output.RGBColor = In.Color;

    return Output;
}

Mengkompilasi dan memuat shader

Game Direct3D 9 sering menggunakan pustaka Efek sebagai cara mudah untuk mengimplementasikan alur yang dapat diprogram. Efek dapat dikompilasi pada run-time menggunakan metode fungsi D3DXCreateEffectFromFile .

Memuat efek di Direct3D 9

// Turn off preshader optimization to keep calculations on the GPU
DWORD dwShaderFlags = D3DXSHADER_NO_PRESHADER;

// Only enable debug info when compiling for a debug target
#if defined (DEBUG) || defined (_DEBUG)
dwShaderFlags |= D3DXSHADER_DEBUG;
#endif

D3DXCreateEffectFromFile(
    m_pd3dDevice,
    L"CubeShaders.fx",
    NULL,
    NULL,
    dwShaderFlags,
    NULL,
    &m_pEffect,
    NULL
    );

Direct3D 11 bekerja dengan program shader sebagai sumber daya biner. Shader dikompilasi ketika proyek dibangun dan kemudian diperlakukan sebagai sumber daya. Jadi contoh kami akan memuat bytecode shader ke dalam memori sistem, menggunakan antarmuka perangkat Direct3D untuk membuat sumber daya Direct3D untuk setiap shader, dan menunjuk ke sumber daya shader Direct3D saat kami menyiapkan setiap bingkai.

Memuat sumber daya shader di Direct3D 11

// BasicReaderWriter is a tested file loader used in SDK samples.
BasicReaderWriter^ readerWriter = ref new BasicReaderWriter();


// Load vertex shader:
Platform::Array<byte>^ vertexShaderData =
    readerWriter->ReadData("CubeVertexShader.cso");

// This call allocates a device resource, validates the vertex shader 
// with the device feature level, and stores the vertex shader bits in 
// graphics memory.
m_d3dDevice->CreateVertexShader(
    vertexShaderData->Data,
    vertexShaderData->Length,
    nullptr,
    &m_vertexShader
    );

Untuk menyertakan bytecode shader dalam paket aplikasi yang dikompilasi, cukup tambahkan file HLSL ke proyek Visual Studio. Visual Studio akan menggunakan Effect-Compiler Tool (FXC) untuk mengkompilasi file HLSL menjadi objek shader yang dikompilasi (. File CSO) dan sertakan dalam paket aplikasi.

Catatan Pastikan untuk mengatur tingkat fitur target yang benar untuk pengkompilasi HLSL: klik kanan file sumber HLSL di Visual Studio, pilih Properti, dan ubah pengaturan Model Shader di bawah Pengkompilasi HLSL -> Umum. Direct3D memeriksa properti ini terhadap kemampuan perangkat keras saat aplikasi Anda membuat sumber daya shader Direct3D.

 

hlsl shader propertihlsl shader type

Ini adalah tempat yang baik untuk membuat tata letak input, yang sesuai dengan deklarasi aliran puncak di Direct3D 9. Struktur data per vertex perlu mencocokkan apa yang digunakan shader vertex; di Direct3D 11 kami memiliki kontrol lebih atas tata letak input; kita dapat menentukan ukuran array dan panjang bit vektor floating-point dan menentukan semantik untuk shader vertex. Kami membuat struktur D3D11_INPUT_ELEMENT_DESC dan menggunakannya untuk memberi tahu Direct3D seperti apa data per vertex akan terlihat. Kami menunggu sampai setelah kami memuat shader vertex untuk menentukan tata letak input karena API memvalidasi tata letak input terhadap sumber daya shader vertex. Jika tata letak input tidak kompatibel, Direct3D akan menampilkan pengecualian.

Data per vertex harus disimpan dalam jenis yang kompatibel dalam memori sistem. Jenis data DirectXMath dapat membantu; misalnya, DXGI_FORMAT_R32G32B32_FLOAT sesuai dengan XMFLOAT3.

Catatan Buffer konstan menggunakan tata letak input tetap yang selaras dengan empat angka floating-point pada satu waktu. XMFLOAT4 (dan turunannya) direkomendasikan untuk data buffer konstan.

 

Mengatur tata letak input di Direct3D 11

// Create input layout:
const D3D11_INPUT_ELEMENT_DESC vertexDesc[] = 
{
    { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT,
        0, 0,  D3D11_INPUT_PER_VERTEX_DATA, 0 },

    { "COLOR",    0, DXGI_FORMAT_R32G32B32_FLOAT, 
        0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
};

Membuat sumber daya geometri

Di Direct3D 9 kami menyimpan sumber daya geometri dengan membuat buffer pada perangkat Direct3D, mengunci memori, dan menyalin data dari memori CPU ke memori GPU.

Direct3D 9

// Create vertex buffer:
VOID* pVertices;

// In Direct3D 9 we create the buffer, lock it, and copy the data from 
// system memory to graphics memory.
m_pd3dDevice->CreateVertexBuffer(
    sizeof(CubeVertices),
    0,
    D3DFVF_XYZ | D3DFVF_DIFFUSE,
    D3DPOOL_MANAGED,
    &pVertexBuffer,
    NULL);

pVertexBuffer->Lock(
    0,
    sizeof(CubeVertices),
    &pVertices,
    0);

memcpy(pVertices, CubeVertices, sizeof(CubeVertices));
pVertexBuffer->Unlock();

DirectX 11 mengikuti proses yang lebih sederhana. API secara otomatis menyalin data dari memori sistem ke GPU. Kita dapat menggunakan penunjuk cerdas COM untuk membantu mempermudah pemrograman.

DirectX 11

D3D11_SUBRESOURCE_DATA vertexBufferData = {0};
vertexBufferData.pSysMem = CubeVertices;
vertexBufferData.SysMemPitch = 0;
vertexBufferData.SysMemSlicePitch = 0;
CD3D11_BUFFER_DESC vertexBufferDesc(
    sizeof(CubeVertices),
    D3D11_BIND_VERTEX_BUFFER);
  
// This call allocates a device resource for the vertex buffer and copies
// in the data.
m_d3dDevice->CreateBuffer(
    &vertexBufferDesc,
    &vertexBufferData,
    &m_vertexBuffer
    );

Menerapkan rantai penyajian

Game Direct3D 9 sering menggunakan rantai penyajian berbasis efek. Jenis rantai penyajian ini menyiapkan objek efek, menyediakannya dengan sumber daya yang dibutuhkan, dan memungkinkannya merender setiap lulus.

Rantai penyajian Direct3D 9

// Clear the render target and the z-buffer.
m_pd3dDevice->Clear(
    0, NULL,
    D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,
    D3DCOLOR_ARGB(0, 45, 50, 170),
    1.0f, 0
    );

// Set the effect technique
m_pEffect->SetTechnique("RenderSceneSimple");

// Rotate the cube 1 degree per frame.
D3DXMATRIX world;
D3DXMatrixRotationY(&world, D3DXToRadian(m_frameCount++));


// Set the matrices up using traditional functions.
m_pEffect->SetMatrix("g_mWorld", &world);
m_pEffect->SetMatrix("g_View", &m_view);
m_pEffect->SetMatrix("g_Projection", &m_projection);

// Render the scene using the Effects library.
if(SUCCEEDED(m_pd3dDevice->BeginScene()))
{
    // Begin rendering effect passes.
    UINT passes = 0;
    m_pEffect->Begin(&passes, 0);
    
    for (UINT i = 0; i < passes; i++)
    {
        m_pEffect->BeginPass(i);
        
        // Send vertex data to the pipeline.
        m_pd3dDevice->SetFVF(D3DFVF_XYZ | D3DFVF_DIFFUSE);
        m_pd3dDevice->SetStreamSource(
            0, pVertexBuffer,
            0, sizeof(VertexPositionColor)
            );
        m_pd3dDevice->SetIndices(pIndexBuffer);
        
        // Draw the cube.
        m_pd3dDevice->DrawIndexedPrimitive(
            D3DPT_TRIANGLELIST,
            0, 0, 8, 0, 12
            );
        m_pEffect->EndPass();
    }
    m_pEffect->End();
    
    // End drawing.
    m_pd3dDevice->EndScene();
}

// Present frame:
// Show the frame on the primary surface.
m_pd3dDevice->Present(NULL, NULL, NULL, NULL);

Rantai penyajian DirectX 11 masih akan melakukan tugas yang sama, tetapi pass penyajian perlu diimplementasikan secara berbeda. Alih-alih menempatkan spesifikasi dalam file FX dan membiarkan teknik penyajian lebih atau kurang buram ke kode C++ kami, kami akan menyiapkan semua penyajian kami di C++.

Berikut adalah bagaimana rantai penyajian kami akan terlihat. Kita perlu menyediakan tata letak input yang kita buat setelah memuat shader vertex, menyediakan setiap objek shader, dan menentukan buffer konstanta untuk setiap shader untuk digunakan. Contoh ini tidak menyertakan beberapa pass penyajian, tetapi jika berhasil, kami akan melakukan rantai penyajian serupa untuk setiap pass, mengubah penyiapan sesuai kebutuhan.

Rantai penyajian Direct3D 11

// Clear the back buffer.
const float midnightBlue[] = { 0.098f, 0.098f, 0.439f, 1.000f };
m_d3dContext->ClearRenderTargetView(
    m_renderTargetView.Get(),
    midnightBlue
    );

// Set the render target. This starts the drawing operation.
m_d3dContext->OMSetRenderTargets(
    1,  // number of render target views for this drawing operation.
    m_renderTargetView.GetAddressOf(),
    nullptr
    );


// Rotate the cube 1 degree per frame.
XMStoreFloat4x4(
    &m_constantBufferData.model, 
    XMMatrixTranspose(XMMatrixRotationY(m_frameCount++ * XM_PI / 180.f))
    );

// Copy the updated constant buffer from system memory to video memory.
m_d3dContext->UpdateSubresource(
    m_constantBuffer.Get(),
    0,      // update the 0th subresource
    NULL,   // use the whole destination
    &m_constantBufferData,
    0,      // default pitch
    0       // default pitch
    );


// Send vertex data to the Input Assembler stage.
UINT stride = sizeof(VertexPositionColor);
UINT offset = 0;

m_d3dContext->IASetVertexBuffers(
    0,  // start with the first vertex buffer
    1,  // one vertex buffer
    m_vertexBuffer.GetAddressOf(),
    &stride,
    &offset
    );

m_d3dContext->IASetIndexBuffer(
    m_indexBuffer.Get(),
    DXGI_FORMAT_R16_UINT,
    0   // no offset
    );

m_d3dContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
m_d3dContext->IASetInputLayout(m_inputLayout.Get());


// Set the vertex shader.
m_d3dContext->VSSetShader(
    m_vertexShader.Get(),
    nullptr,
    0
    );

// Set the vertex shader constant buffer data.
m_d3dContext->VSSetConstantBuffers(
    0,  // register 0
    1,  // one constant buffer
    m_constantBuffer.GetAddressOf()
    );


// Set the pixel shader.
m_d3dContext->PSSetShader(
    m_pixelShader.Get(),
    nullptr,
    0
    );


// Draw the cube.
m_d3dContext->DrawIndexed(
    m_indexCount,
    0,  // start with index 0
    0   // start with vertex 0
    );

Rantai pertukaran adalah bagian dari infrastruktur grafis, jadi kami menggunakan rantai pertukaran DXGI kami untuk menyajikan bingkai yang telah selesai. DXGI memblokir panggilan hingga vsync berikutnya; kemudian kembali, dan perulangan game kami dapat berlanjut ke iterasi berikutnya.

Menyajikan bingkai ke layar menggunakan DirectX 11

m_swapChain->Present(1, 0);

Rantai penyajian yang baru saja kita buat akan dipanggil dari perulangan game yang diimplementasikan dalam metode IFrameworkView::Run . Ini ditunjukkan di Bagian 3: Viewport dan perulangan game.