Merender kerangka kerja I: Pengandalan penyajian

Catatan

Topik ini adalah bagian dari buat game Platform Windows Universal sederhana (UWP) dengan seri tutorial DirectX. Topik di tautan tersebut mengatur konteks untuk seri.

Sejauh ini kami telah membahas cara menyusun game Platform Windows Universal (UWP), dan cara mendefinisikan mesin status untuk menangani alur permainan. Sekarang saatnya untuk mempelajari cara mengembangkan kerangka kerja penyajian. Mari kita lihat bagaimana contoh permainan merender adegan game menggunakan Direct3D 11.

Direct3D 11 berisi sekumpulan API yang menyediakan akses ke fitur canggih perangkat keras grafis berkinerja tinggi yang dapat digunakan untuk membuat grafik 3D untuk aplikasi intensif grafis seperti game.

Merender grafik game di layar berarti pada dasarnya merender urutan bingkai di layar. Di setiap bingkai, Anda harus merender objek yang terlihat di adegan, berdasarkan tampilan.

Untuk merender bingkai, Anda harus meneruskan informasi adegan yang diperlukan ke perangkat keras sehingga dapat ditampilkan di layar. Jika Anda ingin menampilkan sesuatu di layar, Anda harus mulai merender segera setelah permainan mulai berjalan.

Tujuan

Untuk menyiapkan kerangka kerja penyajian dasar guna menampilkan output grafis untuk game UWP DirectX. Anda dapat secara longgar memecahnya menjadi tiga langkah ini.

  1. Buat koneksi ke antarmuka grafis.
  2. Buat sumber daya yang diperlukan untuk menggambar grafik.
  3. Tampilkan grafik dengan merender bingkai.

Topik ini menjelaskan bagaimana grafik dirender, mencakup langkah 1 dan 3.

Merender kerangka kerja II: Rendering game mencakup langkah 2—cara menyiapkan kerangka kerja penyajian, dan bagaimana data disiapkan sebelum penyajian dapat terjadi.

Mulai

Ada baiknya untuk membiasakan diri dengan grafis dasar dan konsep penyajian. Jika Anda baru menggunakan Direct3D dan penyajian, lihat Istilah dan konsep untuk deskripsi singkat tentang grafik dan istilah penyajian yang digunakan dalam topik ini.

Untuk game ini, kelas GameRenderer mewakili perender untuk permainan sampel ini. Ini bertanggung jawab untuk membuat dan memelihara semua objek Direct3D 11 dan Direct2D yang digunakan untuk menghasilkan visual game. Ini juga mempertahankan referensi ke objek Simple3DGame yang digunakan untuk mengambil daftar objek yang akan dirender, serta status permainan untuk layar heads-up (HUD).

Di bagian tutorial ini, kita akan fokus pada penyajian objek 3D dalam game.

Membuat koneksi ke antarmuka grafis

Untuk informasi tentang mengakses perangkat keras untuk penyajian, lihat topik Menentukan kerangka kerja aplikasi UWP game .

Metode App::Initialize

Fungsi std::make_shared , seperti yang ditunjukkan di bawah ini, digunakan untuk membuat shared_ptr ke DX::D eviceResources, yang juga menyediakan akses ke perangkat.

Di Direct3D 11, perangkat digunakan untuk mengalokasikan dan menghancurkan objek, merender primitif, dan berkomunikasi dengan kartu grafis melalui driver grafis.

void Initialize(CoreApplicationView const& applicationView)
{
    ...

    // At this point we have access to the device. 
    // We can create the device-dependent resources.
    m_deviceResources = std::make_shared<DX::DeviceResources>();
}

Tampilkan grafik dengan merender bingkai

Adegan game perlu dirender ketika game diluncurkan. Instruksi untuk penyajian dimulai dalam metode GameMain::Run , seperti yang ditunjukkan di bawah ini.

Alur sederhananya adalah ini.

  1. Pembaruan
  2. Render
  3. Hadir

Metode GameMain::Run

void GameMain::Run()
{
    while (!m_windowClosed)
    {
        if (m_visible) // if the window is visible
        {
            switch (m_updateState)
            {
            ...
            default:
                CoreWindow::GetForCurrentThread().Dispatcher().ProcessEvents(CoreProcessEventsOption::ProcessAllIfPresent);
                Update();
                m_renderer->Render();
                m_deviceResources->Present();
                m_renderNeeded = false;
            }
        }
        else
        {
            CoreWindow::GetForCurrentThread().Dispatcher().ProcessEvents(CoreProcessEventsOption::ProcessOneAndAllPending);
        }
    }
    m_game->OnSuspending();  // Exiting due to window close, so save state.
}

Pembaruan

Lihat topik Manajemen alur game untuk informasi selengkapnya tentang bagaimana status game diperbarui dalam metode GameMain::Update .

Render

Rendering diimplementasikan dengan memanggil metode GameRenderer::Render dari GameMain::Run.

Jika penyajian stereo diaktifkan, maka ada dua perenderan lewat—satu untuk mata kiri dan satu untuk kanan. Di setiap kode penyajian, kami mengikat target render dan tampilan stensil kedalaman ke perangkat. Kami juga menghapus tampilan stensil kedalaman setelahnya.

Catatan

Penyajian stereo dapat dicapai menggunakan metode lain seperti stereo single pass menggunakan instancing vertex atau shader geometri. Metode two-rendering-passes adalah cara yang lebih lambat tetapi lebih nyaman untuk mencapai penyajian stereo.

Setelah game berjalan, dan sumber daya dimuat, kami memperbarui matriks proyeksi, sekali per rendering pass. Objek sedikit berbeda dari setiap tampilan. Selanjutnya, kami menyiapkan alur penyajian grafis.

Catatan

Lihat Membuat dan memuat sumber daya grafis DirectX untuk informasi selengkapnya tentang cara sumber daya dimuat.

Dalam permainan sampel ini, perender dirancang untuk menggunakan tata letak vertex standar di semua objek. Ini menyederhanakan desain shader, dan memungkinkan perubahan yang mudah antara shader, terlepas dari geometri objek.

Metode GameRenderer::Render

Kami mengatur konteks Direct3D untuk menggunakan tata letak verteks input. Objek tata letak input menjelaskan bagaimana data buffer vertex dialirkan ke alur penyajian.

Selanjutnya, kita mengatur konteks Direct3D untuk menggunakan buffer konstanta yang ditentukan sebelumnya, yang digunakan oleh tahap alur shader vertex dan tahap alur piksel shader .

Catatan

Lihat Merender kerangka kerja II: Penyajian game untuk informasi selengkapnya tentang definisi buffer konstanta.

Karena tata letak input dan set buffer konstan yang sama digunakan untuk semua shader yang ada di alur, tata letak tersebut disiapkan sekali per bingkai.

void GameRenderer::Render()
{
    bool stereoEnabled{ m_deviceResources->GetStereoState() };

    auto d3dContext{ m_deviceResources->GetD3DDeviceContext() };
    auto d2dContext{ m_deviceResources->GetD2DDeviceContext() };

    int renderingPasses = 1;
    if (stereoEnabled)
    {
        renderingPasses = 2;
    }

    for (int i = 0; i < renderingPasses; i++)
    {
        // Iterate through the number of rendering passes to be completed.
        // 2 rendering passes if stereo is enabled.
        if (i > 0)
        {
            // Doing the Right Eye View.
            ID3D11RenderTargetView* const targets[1] = { m_deviceResources->GetBackBufferRenderTargetViewRight() };

            // Resets render targets to the screen.
            // OMSetRenderTargets binds 2 things to the device.
            // 1. Binds one render target atomically to the device.
            // 2. Binds the depth-stencil view, as returned by the GetDepthStencilView method, to the device.
            // For more info, see
            // https://learn.microsoft.com/windows/win32/api/d3d11/nf-d3d11-id3d11devicecontext-omsetrendertargets

            d3dContext->OMSetRenderTargets(1, targets, m_deviceResources->GetDepthStencilView());

            // Clears the depth stencil view.
            // A depth stencil view contains the format and buffer to hold depth and stencil info.
            // For more info about depth stencil view, go to: 
            // https://learn.microsoft.com/windows/uwp/graphics-concepts/depth-stencil-view--dsv-
            // A depth buffer is used to store depth information to control which areas of 
            // polygons are rendered rather than hidden from view. To learn more about a depth buffer,
            // go to: https://learn.microsoft.com/windows/uwp/graphics-concepts/depth-buffers
            // A stencil buffer is used to mask pixels in an image, to produce special effects. 
            // The mask determines whether a pixel is drawn or not,
            // by setting the bit to a 1 or 0. To learn more about a stencil buffer,
            // go to: https://learn.microsoft.com/windows/uwp/graphics-concepts/stencil-buffers

            d3dContext->ClearDepthStencilView(m_deviceResources->GetDepthStencilView(), D3D11_CLEAR_DEPTH, 1.0f, 0);

            // Direct2D -- discussed later
            d2dContext->SetTarget(m_deviceResources->GetD2DTargetBitmapRight());
        }
        else
        {
            // Doing the Mono or Left Eye View.
            // As compared to the right eye:
            // m_deviceResources->GetBackBufferRenderTargetView instead of GetBackBufferRenderTargetViewRight
            ID3D11RenderTargetView* const targets[1] = { m_deviceResources->GetBackBufferRenderTargetView() };

            // Same as the Right Eye View.
            d3dContext->OMSetRenderTargets(1, targets, m_deviceResources->GetDepthStencilView());
            d3dContext->ClearDepthStencilView(m_deviceResources->GetDepthStencilView(), D3D11_CLEAR_DEPTH, 1.0f, 0);

            // d2d -- Discussed later under Adding UI
            d2dContext->SetTarget(m_deviceResources->GetD2DTargetBitmap());
        }

        const float clearColor[4] = { 0.5f, 0.5f, 0.8f, 1.0f };

        // Only need to clear the background when not rendering the full 3D scene since
        // the 3D world is a fully enclosed box and the dynamics prevents the camera from
        // moving outside this space.
        if (i > 0)
        {
            // Doing the Right Eye View.
            d3dContext->ClearRenderTargetView(m_deviceResources->GetBackBufferRenderTargetViewRight(), clearColor);
        }
        else
        {
            // Doing the Mono or Left Eye View.
            d3dContext->ClearRenderTargetView(m_deviceResources->GetBackBufferRenderTargetView(), clearColor);
        }

        // Render the scene objects
        if (m_game != nullptr && m_gameResourcesLoaded && m_levelResourcesLoaded)
        {
            // This section is only used after the game state has been initialized and all device
            // resources needed for the game have been created and associated with the game objects.
            if (stereoEnabled)
            {
                // When doing stereo, it is necessary to update the projection matrix once per rendering pass.

                auto orientation = m_deviceResources->GetOrientationTransform3D();

                ConstantBufferChangeOnResize changesOnResize;
                // Apply either a left or right eye projection, which is an offset from the middle
                XMStoreFloat4x4(
                    &changesOnResize.projection,
                    XMMatrixMultiply(
                        XMMatrixTranspose(
                            i == 0 ?
                            m_game->GameCamera().LeftEyeProjection() :
                            m_game->GameCamera().RightEyeProjection()
                            ),
                        XMMatrixTranspose(XMLoadFloat4x4(&orientation))
                        )
                    );

                d3dContext->UpdateSubresource(
                    m_constantBufferChangeOnResize.get(),
                    0,
                    nullptr,
                    &changesOnResize,
                    0,
                    0
                    );
            }

            // Update variables that change once per frame.
            ConstantBufferChangesEveryFrame constantBufferChangesEveryFrameValue;
            XMStoreFloat4x4(
                &constantBufferChangesEveryFrameValue.view,
                XMMatrixTranspose(m_game->GameCamera().View())
                );
            d3dContext->UpdateSubresource(
                m_constantBufferChangesEveryFrame.get(),
                0,
                nullptr,
                &constantBufferChangesEveryFrameValue,
                0,
                0
                );

            // Set up the graphics pipeline. This sample uses the same InputLayout and set of
            // constant buffers for all shaders, so they only need to be set once per frame.
            // For more info about the graphics or rendering pipeline, see
            // https://learn.microsoft.com/windows/win32/direct3d11/overviews-direct3d-11-graphics-pipeline

            // IASetInputLayout binds an input-layout object to the input-assembler (IA) stage. 
            // Input-layout objects describe how vertex buffer data is streamed into the IA pipeline stage.
            // Set up the Direct3D context to use this vertex layout. For more info, see
            // https://learn.microsoft.com/windows/win32/api/d3d11/nf-d3d11-id3d11devicecontext-iasetinputlayout
            d3dContext->IASetInputLayout(m_vertexLayout.get());

            // VSSetConstantBuffers sets the constant buffers used by the vertex shader pipeline stage.
            // Set up the Direct3D context to use these constant buffers. For more info, see
            // https://learn.microsoft.com/windows/win32/api/d3d11/nf-d3d11-id3d11devicecontext-vssetconstantbuffers

            ID3D11Buffer* constantBufferNeverChanges{ m_constantBufferNeverChanges.get() };
            d3dContext->VSSetConstantBuffers(0, 1, &constantBufferNeverChanges);
            ID3D11Buffer* constantBufferChangeOnResize{ m_constantBufferChangeOnResize.get() };
            d3dContext->VSSetConstantBuffers(1, 1, &constantBufferChangeOnResize);
            ID3D11Buffer* constantBufferChangesEveryFrame{ m_constantBufferChangesEveryFrame.get() };
            d3dContext->VSSetConstantBuffers(2, 1, &constantBufferChangesEveryFrame);
            ID3D11Buffer* constantBufferChangesEveryPrim{ m_constantBufferChangesEveryPrim.get() };
            d3dContext->VSSetConstantBuffers(3, 1, &constantBufferChangesEveryPrim);

            // Sets the constant buffers used by the pixel shader pipeline stage. 
            // For more info, see
            // https://learn.microsoft.com/windows/win32/api/d3d11/nf-d3d11-id3d11devicecontext-pssetconstantbuffers

            d3dContext->PSSetConstantBuffers(2, 1, &constantBufferChangesEveryFrame);
            d3dContext->PSSetConstantBuffers(3, 1, &constantBufferChangesEveryPrim);
            ID3D11SamplerState* samplerLinear{ m_samplerLinear.get() };
            d3dContext->PSSetSamplers(0, 1, &samplerLinear);

            for (auto&& object : m_game->RenderObjects())
            {
                // The 3D object render method handles the rendering.
                // For more info, see Primitive rendering below.
                object->Render(d3dContext, m_constantBufferChangesEveryPrim.get());
            }
        }

        // Start of 2D rendering
        ...
    }
}

Penyajian primitif

Saat merender adegan, Anda mengulangi semua objek yang perlu dirender. Langkah-langkah di bawah ini diulang untuk setiap objek (primitif).

  • Perbarui buffer konstanta (m_constantBufferChangesEveryPrim) dengan matriks transformasi dunia model dan informasi material.
  • m_constantBufferChangesEveryPrim berisi parameter untuk setiap objek. Ini termasuk matriks transformasi objek-ke-dunia serta properti material seperti warna dan eksponen spekular untuk perhitungan pencahayaan.
  • Atur konteks Direct3D untuk menggunakan tata letak puncak input agar data objek jala dialirkan ke tahap perakitan input (IA) dari alur penyajian.
  • Atur konteks Direct3D untuk menggunakan buffer indeks di tahap IA. Berikan info primitif: jenis, pesanan data.
  • Kirim panggilan gambar untuk menggambar primitif terindeks dan non-instans. Metode GameObject::Render memperbarui buffer konstan primitif dengan data khusus untuk primitif tertentu. Ini menghasilkan panggilan DrawIndexed pada konteks untuk menggambar geometri setiap primitif. Secara khusus, panggilan gambar ini mengantrekan perintah dan data ke unit pemrosesan grafis (GPU), seperti yang diparameterkan oleh data buffer konstanta. Setiap panggilan gambar menjalankan shader vertex satu kali per puncak, lalu shader piksel satu kali untuk setiap piksel setiap segitiga dalam primitif. Tekstur adalah bagian dari status yang digunakan shader piksel untuk melakukan penyajian.

Berikut adalah alasan untuk menggunakan beberapa buffer konstanta.

  • Gim ini menggunakan beberapa buffer konstan, tetapi hanya perlu memperbarui buffer ini satu kali per primitif. Seperti disebutkan sebelumnya, buffer konstan seperti input ke shader yang berjalan untuk setiap primitif. Beberapa data bersifat statis (m_constantBufferNeverChanges); beberapa data konstan di atas bingkai (m_constantBufferChangesEveryFrame), seperti posisi kamera; dan beberapa data khusus untuk primitif, seperti warna dan teksturnya (m_constantBufferChangesEveryPrim).
  • Perender game memisahkan input ini menjadi buffer konstan yang berbeda untuk mengoptimalkan bandwidth memori yang digunakan CPU dan GPU. Pendekatan ini juga membantu meminimalkan jumlah data yang perlu dilacak oleh GPU. GPU memiliki antrean perintah yang besar, dan setiap kali game memanggil Draw, perintah itu diantrekan bersama dengan data yang terkait dengannya. Ketika game memperbarui buffer konstanta primitif dan mengeluarkan perintah Draw berikutnya, driver grafis menambahkan perintah berikutnya ini dan data terkait ke antrean. Jika permainan menarik 100 primitif, itu berpotensi memiliki 100 salinan data buffer konstan dalam antrean. Untuk meminimalkan jumlah data yang dikirim game ke GPU, game ini menggunakan buffer konstanta primitif terpisah yang hanya berisi pembaruan untuk setiap primitif.

Metode GameObject::Render

void GameObject::Render(
    _In_ ID3D11DeviceContext* context,
    _In_ ID3D11Buffer* primitiveConstantBuffer
    )
{
    if (!m_active || (m_mesh == nullptr) || (m_normalMaterial == nullptr))
    {
        return;
    }

    ConstantBufferChangesEveryPrim constantBuffer;

    // Put the model matrix info into a constant buffer, in world matrix.
    XMStoreFloat4x4(
        &constantBuffer.worldMatrix,
        XMMatrixTranspose(ModelMatrix())
        );

    // Check to see which material to use on the object.
    // If a collision (a hit) is detected, GameObject::Render checks the current context, which 
    // indicates whether the target has been hit by an ammo sphere. If the target has been hit, 
    // this method applies a hit material, which reverses the colors of the rings of the target to 
    // indicate a successful hit to the player. Otherwise, it applies the default material 
    // with the same method. In both cases, it sets the material by calling Material::RenderSetup, 
    // which sets the appropriate constants into the constant buffer. Then, it calls 
    // ID3D11DeviceContext::PSSetShaderResources to set the corresponding texture resource for the 
    // pixel shader, and ID3D11DeviceContext::VSSetShader and ID3D11DeviceContext::PSSetShader 
    // to set the vertex shader and pixel shader objects themselves, respectively.

    if (m_hit && m_hitMaterial != nullptr)
    {
        m_hitMaterial->RenderSetup(context, &constantBuffer);
    }
    else
    {
        m_normalMaterial->RenderSetup(context, &constantBuffer);
    }

    // Update the primitive constant buffer with the object model's info.
    context->UpdateSubresource(primitiveConstantBuffer, 0, nullptr, &constantBuffer, 0, 0);

    // Render the mesh.
    // See MeshObject::Render method below.
    m_mesh->Render(context);
}

Metode MeshObject::Render

void MeshObject::Render(_In_ ID3D11DeviceContext* context)
{
    // PNTVertex is a struct. stride provides us the size required for all the mesh data
    // struct PNTVertex
    //{
    //  DirectX::XMFLOAT3 position;
    //  DirectX::XMFLOAT3 normal;
    //  DirectX::XMFLOAT2 textureCoordinate;
    //};
    uint32_t stride{ sizeof(PNTVertex) };
    uint32_t offset{ 0 };

    // Similar to the main render loop.
    // Input-layout objects describe how vertex buffer data is streamed into the IA pipeline stage.
    ID3D11Buffer* vertexBuffer{ m_vertexBuffer.get() };
    context->IASetVertexBuffers(0, 1, &vertexBuffer, &stride, &offset);

    // IASetIndexBuffer binds an index buffer to the input-assembler stage.
    // For more info, see
    // https://learn.microsoft.com/windows/win32/api/d3d11/nf-d3d11-id3d11devicecontext-iasetindexbuffer.
    context->IASetIndexBuffer(m_indexBuffer.get(), DXGI_FORMAT_R16_UINT, 0);

    // Binds information about the primitive type, and data order that describes input data for the input assembler stage.
    // For more info, see
    // https://learn.microsoft.com/windows/win32/api/d3d11/nf-d3d11-id3d11devicecontext-iasetprimitivetopology.
    context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

    // Draw indexed, non-instanced primitives. A draw API submits work to the rendering pipeline.
    // For more info, see
    // https://learn.microsoft.com/windows/win32/api/d3d11/nf-d3d11-id3d11devicecontext-drawindexed.
    context->DrawIndexed(m_indexCount, 0, 0);
}

Metode DeviceResources::P resent

Kami memanggil metode DeviceResources::P resent untuk menampilkan konten yang telah kami tempatkan di buffer.

Kami menggunakan istilah swap chain untuk kumpulan buffer yang digunakan untuk menampilkan bingkai kepada pengguna. Setiap kali aplikasi menyajikan bingkai baru untuk tampilan, buffer pertama dalam rantai pertukaran menggantikan buffer yang ditampilkan. Proses ini disebut pertukaran atau membalik. Untuk informasi selengkapnya, lihat Rantai pertukaran.

  • Metode Present antarmuka IDXGISwapChain1 menginstruksikanDXGI untuk memblokir hingga sinkronisasi vertikal (VSync) terjadi, membuat aplikasi tertidur hingga VSync berikutnya. Ini memastikan bahwa Anda tidak membuang-buang siklus bingkai penyajian yang tidak akan pernah ditampilkan ke layar.
  • Metode DiscardView antarmuka ID3D11DeviceContext3 membuang konten target render. Ini adalah operasi yang valid hanya ketika konten yang ada akan sepenuhnya ditimpa. Jika kotor atau menggulir rekam jeda digunakan, maka panggilan ini harus dihapus.

Tip

Untuk mencapai kecepatan bingkai yang halus, Anda harus memastikan bahwa jumlah pekerjaan untuk merender bingkai cocok dalam waktu antara VSyncs.

// Present the contents of the swap chain to the screen.
void DX::DeviceResources::Present()
{
    // The first argument instructs DXGI to block until VSync, putting the application
    // to sleep until the next VSync. This ensures we don't waste any cycles rendering
    // frames that will never be displayed to the screen.
    HRESULT hr = m_swapChain->Present(1, 0);

    // Discard the contents of the render target.
    // This is a valid operation only when the existing contents will be entirely
    // overwritten. If dirty or scroll rects are used, this call should be removed.
    m_d3dContext->DiscardView(m_d3dRenderTargetView.get());

    // Discard the contents of the depth stencil.
    m_d3dContext->DiscardView(m_d3dDepthStencilView.get());

    // If the device was removed either by a disconnection or a driver upgrade, we 
    // must recreate all device resources.
    if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET)
    {
        HandleDeviceLost();
    }
    else
    {
        winrt::check_hresult(hr);
    }
}

Langkah berikutnya

Topik ini menjelaskan bagaimana grafik dirender pada layar, dan memberikan deskripsi singkat untuk beberapa istilah penyajian yang digunakan (di bawah). Pelajari selengkapnya tentang penyajian dalam topik Rendering framework II: Rendering game , dan pelajari cara menyiapkan data yang diperlukan sebelum penyajian.

Syarat dan konsep

Adegan permainan sederhana

Adegan permainan sederhana terdiri dari beberapa objek dengan beberapa sumber cahaya.

Bentuk objek didefinisikan oleh satu set koordinat X, Y, Z dalam ruang. Lokasi render aktual di dunia game dapat ditentukan dengan menerapkan matriks transformasi ke koordinat X, Y, Z posisi. Ini mungkin juga memiliki sekumpulan koordinat tekstur—Anda dan V—yang menentukan bagaimana materi diterapkan ke objek. Ini mendefinisikan sifat permukaan objek, dan memberi Anda kemampuan untuk melihat apakah objek memiliki permukaan kasar (seperti bola tenis), atau permukaan mengkilap halus (seperti bola bowling).

Info adegan dan objek digunakan oleh kerangka kerja penyajian untuk membuat ulang bingkai adegan menurut bingkai, membuatnya hidup di monitor tampilan Anda.

Penyajian alur

Alur penyajian adalah proses di mana info adegan 3D diterjemahkan ke gambar yang ditampilkan di layar. Di Direct3D 11, alur ini dapat diprogram. Anda dapat menyesuaikan tahapan untuk mendukung kebutuhan penyajian Anda. Tahapan yang menampilkan inti shader umum dapat diprogram dengan menggunakan bahasa pemrograman HLSL. Ini juga dikenal sebagai alur penyajian grafis, atau hanya alur.

Untuk membantu Anda membuat alur ini, Anda harus terbiasa dengan detail ini.

Untuk informasi selengkapnya, lihat Memahami alur penyajian Direct3D 11 dan alur Grafis.

HLSL

HLSL adalah bahasa shader tingkat tinggi untuk DirectX. Dengan menggunakan HLSL, Anda dapat membuat shader yang dapat diprogram seperti C untuk alur Direct3D. Untuk informasi selengkapnya, lihat HLSL.

Efek gambar

Shader dapat dianggap sebagai serangkaian instruksi yang menentukan bagaimana permukaan objek muncul saat dirender. Yang diprogram menggunakan HLSL dikenal sebagai shader HLSL. File kode sumber untuk shader [HLSL])(#hlsl) memiliki .hlsl ekstensi file. Shader ini dapat dikompilasi pada build-time atau pada runtime, dan diatur pada runtime ke tahap alur yang sesuai. Objek shader yang dikompilasi memiliki .cso ekstensi file.

Shader Direct3D 9 dapat dirancang menggunakan model shader 1, model shader 2 dan model shader 3; Shader Direct3D 10 hanya dapat dirancang pada model shader 4. Shader Direct3D 11 dapat dirancang pada model shader 5. Direct3D 11.3 dan Direct3D 12 dapat dirancang pada model shader 5.1, dan Direct3D 12 juga dapat dirancang pada model shader 6.

Shader puncak dan shader piksel

Data memasuki alur grafik sebagai aliran primitif, dan diproses oleh berbagai shader seperti shader vertex dan shader piksel.

Vertex shader memproses simpul, biasanya melakukan operasi seperti transformasi, menguliti, dan pencahayaan. Pemecah piksel memungkinkan teknik bayangan yang kaya seperti pencahayaan per piksel dan pasca-pemrosesan. Ini menggabungkan variabel konstan, data tekstur, nilai per vertex terinterpolasi, dan data lain untuk menghasilkan output per piksel.

Tahapan shader

Urutan berbagai shader ini didefinisikan untuk memproses aliran primitif ini dikenal sebagai tahap shader dalam alur penyajian. Tahap aktual tergantung pada versi Direct3D, tetapi biasanya mencakup tahap puncak, geometri, dan piksel. Ada juga tahapan lain, seperti lambung dan shader domain untuk tesselulasi, dan shader komputasi. Semua tahapan ini sepenuhnya dapat diprogram menggunakan HLSL. Untuk informasi selengkapnya, lihat Alur grafis.

Berbagai format file shader

Berikut adalah ekstensi file kode shader.

  • File dengan .hlsl ekstensi menyimpan kode sumber [HLSL])(#hlsl).
  • File dengan .cso ekstensi menyimpan objek shader yang dikompilasi.
  • File dengan .h ekstensi adalah file header, tetapi dalam konteks kode shader, file header ini menentukan array byte yang menyimpan data shader.
  • File dengan .hlsli ekstensi berisi format buffer konstanta. Dalam permainan sampel, filenya adalah Shaders>ConstantBuffers.hlsli.

Catatan

Anda menyematkan shader baik dengan memuat .cso file saat runtime, atau dengan menambahkan .h file dalam kode yang dapat dieksekusi. Tapi anda tidak akan menggunakan keduanya untuk shader yang sama.

Pemahaman yang lebih mendalam tentang DirectX

Direct3D 11 adalah sekumpulan API yang dapat membantu kami membuat grafik untuk aplikasi intensif grafis seperti game, di mana kami ingin memiliki kartu grafis yang baik untuk memproses komputasi intensif. Bagian ini menjelaskan secara singkat konsep pemrograman grafis Direct3D 11: sumber daya, sub sumber daya, perangkat, dan konteks perangkat.

Sumber daya

Anda dapat menganggap sumber daya (juga dikenal sebagai sumber daya perangkat) sebagai info tentang cara merender objek, seperti tekstur, posisi, atau warna. Sumber daya menyediakan data ke alur, dan menentukan apa yang dirender selama adegan Anda. Sumber daya dapat dimuat dari media game Anda, atau dibuat secara dinamis pada durasi.

Sumber daya sebenarnya adalah area dalam memori yang dapat diakses oleh alur Direct3D. Agar alur dapat mengakses memori secara efisien, data yang disediakan ke alur (seperti geometri input, sumber daya shader, dan tekstur) harus disimpan dalam sumber daya. Ada dua jenis sumber daya dari mana semua sumber daya Direct3D berasal: buffer atau tekstur. Hingga 128 sumber daya dapat aktif untuk setiap tahap alur. Untuk mengetahui informasi selengkapnya, lihat Sumber daya.

Subsumber

Istilah subsumber daya mengacu pada subset sumber daya. Direct3D dapat mereferensikan seluruh sumber daya, atau dapat mereferensikan subset sumber daya. Untuk informasi selengkapnya, lihat Subsumber daya.

Stensil kedalaman

Sumber daya stensil kedalaman berisi format dan buffer untuk menyimpan informasi kedalaman dan stensil. Ini dibuat menggunakan sumber daya tekstur. Untuk informasi selengkapnya tentang cara membuat sumber daya stensil kedalaman, lihat Mengonfigurasi fungsionalitas Depth-Stencil. Kami mengakses sumber daya stensil kedalaman melalui tampilan stensil kedalaman yang diterapkan menggunakan antarmuka ID3D11DepthStencilView .

Info kedalaman memberi tahu kita area poligon mana yang berada di belakang orang lain, sehingga kita dapat menentukan mana yang tersembunyi. Info stensil memberi tahu kami piksel mana yang ditutupi. Ini dapat digunakan untuk menghasilkan efek khusus karena menentukan apakah piksel digambar atau tidak; mengatur bit ke 1 atau 0.

Untuk informasi selengkapnya, lihat Tampilan stensil kedalaman, buffer kedalaman, dan buffer stensil.

Render target

Target render adalah sumber daya yang dapat kita tulis di akhir render pass. Biasanya dibuat menggunakan metode ID3D11Device::CreateRenderTargetView menggunakan buffer swap chain back (yang juga merupakan sumber daya) sebagai parameter input.

Setiap target render juga harus memiliki tampilan stensil kedalaman yang sesuai karena ketika kita menggunakan OMSetRenderTargets untuk mengatur target render sebelum menggunakannya, diperlukan juga tampilan stensil kedalaman. Kami mengakses sumber daya target render melalui tampilan target render yang diterapkan menggunakan antarmuka ID3D11RenderTargetView .

Perangkat

Anda dapat membayangkan perangkat sebagai cara untuk mengalokasikan dan menghancurkan objek, merender primitif, dan berkomunikasi dengan kartu grafis melalui driver grafis.

Untuk penjelasan yang lebih tepat, perangkat Direct3D adalah komponen penyajian Direct3D. Perangkat merangkum dan menyimpan status penyajian, melakukan transformasi dan operasi pencahayaan, dan mem-rasterisasi gambar ke permukaan. Untuk informasi selengkapnya, lihat Perangkat

Perangkat diwakili oleh antarmuka ID3D11Device . Dengan kata lain, antarmuka ID3D11Device mewakili adaptor tampilan virtual, dan digunakan untuk membuat sumber daya yang dimiliki oleh perangkat.

Ada berbagai versi ID3D11Device. ID3D11Device5 adalah versi terbaru, dan menambahkan metode baru ke metode di ID3D11Device4. Untuk informasi selengkapnya tentang cara Direct3D berkomunikasi dengan perangkat keras yang mendasar, lihat arsitektur Windows Device Driver Model (WDDM).

Setiap aplikasi harus memiliki setidaknya satu perangkat; sebagian besar aplikasi hanya membuat satu. Buat perangkat untuk salah satu driver perangkat keras yang diinstal pada komputer Anda dengan memanggil D3D11CreateDevice atau D3D11CreateDeviceAndSwapChain dan tentukan jenis driver dengan bendera D3D_DRIVER_TYPE . Setiap perangkat dapat menggunakan satu atau beberapa konteks perangkat, tergantung pada fungsionalitas yang diinginkan. Untuk informasi selengkapnya, lihat fungsi D3D11CreateDevice.

Konteks perangkat

Konteks perangkat digunakan untuk mengatur status alur , dan menghasilkan perintah penyajian menggunakan sumber daya yang dimiliki oleh perangkat.

Direct3D 11 mengimplementasikan dua jenis konteks perangkat, satu untuk penyajian langsung dan yang lainnya untuk penyajian yang ditangguhkan; kedua konteks diwakili dengan antarmuka ID3D11DeviceContext .

Antarmuka ID3D11DeviceContext memiliki versi yang berbeda; ID3D11DeviceContext4 menambahkan metode baru ke metode baru di ID3D11DeviceContext3.

ID3D11DeviceContext4 diperkenalkan dalam Windows 10 Creators Update, dan merupakan versi terbaru dari antarmuka ID3D11DeviceContext. Aplikasi yang menargetkan Windows 10 Creators Update dan yang lebih baru harus menggunakan antarmuka ini alih-alih versi sebelumnya. Untuk informasi selengkapnya, lihat ID3D11DeviceContext4.

DX::D eviceResources

Kelas DX::D eviceResources ada di file DeviceResources.cpp.h/, dan mengontrol semua sumber daya perangkat DirectX.

Buffer

Sumber daya buffer adalah kumpulan data yang sepenuhnya diketik yang dikelompokkan ke dalam elemen. Anda dapat menggunakan buffer untuk menyimpan berbagai data, termasuk vektor posisi, vektor normal, koordinat tekstur dalam buffer vertex, indeks dalam buffer indeks, atau status perangkat. Elemen buffer dapat menyertakan nilai data yang dikemas (seperti nilai permukaan R8G8B8A8 ), bilangan bulat 8-bit tunggal, atau empat nilai titik mengambang 32-bit.

Ada tiga jenis buffer yang tersedia: buffer vertex, buffer indeks, dan buffer konstanta.

Buffer puncak

Berisi data puncak yang digunakan untuk menentukan geometri Anda. Data vertex mencakup koordinat posisi, data warna, data koordinat tekstur, data normal, dan sebagainya.

Buffer indeks

Berisi offset bilangan bulat ke dalam buffer vertex dan digunakan untuk merender primitif secara lebih efisien. Buffer indeks berisi set berurutan indeks 16-bit atau 32-bit; setiap indeks digunakan untuk mengidentifikasi vertex dalam buffer vertex.

Buffer konstanta, atau penyangga konstanta shader

Memungkinkan Anda untuk secara efisien menyediakan data shader ke alur. Anda dapat menggunakan buffer konstan sebagai input ke shader yang berjalan untuk setiap primitif dan menyimpan hasil tahap stream-output dari alur penyajian. Secara konseptual, buffer konstan terlihat seperti buffer vertex elemen tunggal.

Desain dan implementasi buffer

Anda dapat merancang buffer berdasarkan jenis data, misalnya, seperti dalam permainan sampel kami, satu buffer dibuat untuk data statis, yang lain untuk data yang konstan di atas bingkai, dan satu lagi untuk data yang khusus untuk primitif.

Semua jenis buffer dienkapsulasi oleh antarmuka ID3D11Buffer dan Anda dapat membuat sumber daya buffer dengan memanggil ID3D11Device::CreateBuffer. Tetapi buffer harus terikat ke alur sebelum dapat diakses. Buffer dapat terikat ke beberapa tahap alur secara bersamaan untuk dibaca. Buffer juga dapat terikat ke satu tahap alur untuk menulis; namun, buffer yang sama tidak dapat terikat untuk membaca dan menulis secara bersamaan.

Anda dapat mengikat buffer dengan cara ini.

  • Ke tahap input-assembler dengan memanggil metode ID3D11DeviceContext seperti ID3D11DeviceContext::IASetVertexBuffers dan ID3D11DeviceContext::IASetIndexBuffer.
  • Ke tahap stream-output dengan memanggil ID3D11DeviceContext::SOSetTargets.
  • Ke tahap shader dengan memanggil metode shader, seperti ID3D11DeviceContext::VSSetConstantBuffers.

Untuk informasi selengkapnya, lihat Pengantar buffer di Direct3D 11.

DXGI

Microsoft DirectX Graphics Infrastructure (DXGI) adalah subsistem yang merangkum beberapa tugas tingkat rendah yang diperlukan oleh Direct3D. Perawatan khusus harus dilakukan saat menggunakan DXGI dalam aplikasi multithreaded untuk memastikan bahwa kebuntuan tidak terjadi. Untuk informasi selengkapnya, lihat Multithreading dan DXGI

Tingkat fitur

Tingkat fitur adalah konsep yang diperkenalkan di Direct3D 11 untuk menangani keragaman kartu video di mesin baru dan yang sudah ada. Tingkat fitur adalah serangkaian fungsionalitas unit pemrosesan grafis (GPU) yang terdefinisi dengan baik.

Setiap kartu video mengimplementasikan tingkat fungsiOnalitas DirectX tertentu tergantung pada GPU yang diinstal. Dalam versi Microsoft Direct3D sebelumnya, Anda dapat mengetahui versi Direct3D yang diterapkan kartu video, lalu memprogram aplikasi Anda.

Dengan tingkat fitur, saat membuat perangkat, Anda dapat mencoba membuat perangkat untuk tingkat fitur yang ingin Anda minta. Jika pembuatan perangkat berfungsi, tingkat fitur tersebut ada, jika tidak, perangkat keras tidak mendukung tingkat fitur tersebut. Anda dapat mencoba membuat ulang perangkat pada tingkat fitur yang lebih rendah, atau Anda dapat memilih untuk keluar dari aplikasi. Misalnya, tingkat fitur 12_0 memerlukan Direct3D 11.3 atau Direct3D 12, dan model shader 5.1. Untuk informasi selengkapnya, lihat Tingkat fitur Direct3D: Gambaran Umum untuk setiap tingkat fitur.

Dengan menggunakan tingkat fitur, Anda dapat mengembangkan aplikasi untuk Direct3D 9, Microsoft Direct3D 10, atau Direct3D 11, lalu menjalankannya pada perangkat keras 9, 10, atau 11 (dengan beberapa pengecualian). Untuk informasi selengkapnya, lihat Tingkat fitur Direct3D.

Penyajian stereo

Penyajian stereo digunakan untuk meningkatkan ilusi kedalaman. Ini menggunakan dua gambar, satu dari mata kiri dan yang lain dari mata kanan untuk menampilkan adegan di layar tampilan.

Secara matematis, kami menerapkan matriks proyeksi stereo, yang merupakan offset horizontal sedikit ke kanan dan ke kiri, dari matriks proyeksi mono biasa untuk mencapai hal ini.

Kami melakukan dua lolos penyajian untuk mencapai penyajian stereo dalam permainan sampel ini.

  • Ikat ke target render kanan, terapkan proyeksi yang tepat, lalu gambar objek primitif.
  • Ikat ke target render kiri, terapkan proyeksi kiri, lalu gambar objek primitif.

Kamera dan ruang koordinat

Gim ini memiliki kode untuk memperbarui dunia dalam sistem koordinatnya sendiri (kadang-kadang disebut ruang dunia atau ruang adegan). Semua objek, termasuk kamera, diposisikan dan berorientasi pada ruang ini. Untuk informasi selengkapnya, lihat Sistem koordinat.

Shader puncak melakukan pengangkatan berat konversi dari koordinat model ke perangkat berkoordinasi dengan algoritma berikut (di mana V adalah vektor dan M adalah matriks).

V(device) = V(model) x M(model-to-world) x M(world-to-view) x M(view-to-device)

  • M(model-to-world) adalah matriks transformasi untuk koordinat model ke koordinat dunia, juga dikenal sebagai matriks transformasi Dunia. Ini disediakan oleh primitif.
  • M(world-to-view) adalah matriks transformasi untuk koordinat dunia untuk melihat koordinat, juga dikenal sebagai matriks transformasi Tampilan.
    • Ini disediakan oleh matriks tampilan kamera. Ini didefinisikan oleh posisi kamera bersama dengan vektor tampilan ( tampilan vektor yang menunjuk langsung ke tempat kejadian dari kamera, dan vektor pencarian yang ke atas tegak lurus).
    • Dalam permainan sampel, m_viewMatrix adalah matriks transformasi tampilan, dan dihitung menggunakan Camera::SetViewParams.
  • M(view-to-device) adalah matriks transformasi untuk melihat koordinat ke koordinat perangkat, juga dikenal sebagai matriks transformasi Proyeksi.
    • Ini disediakan oleh proyeksi kamera. Ini memberikan info tentang berapa banyak ruang itu yang benar-benar terlihat di adegan akhir. Bidang pandang (FoV), rasio aspek, dan bidang kliping menentukan matriks transformasi proyeksi.
    • Dalam permainan sampel, m_projectionMatrix mendefinisikan transformasi ke koordinat proyeksi, dihitung menggunakan Camera::SetProjParams (Untuk proyeksi stereo, Anda menggunakan dua matriks proyeksi—satu untuk tampilan setiap mata).

Kode shader di VertexShader.hlsl dimuat dengan vektor dan matriks ini dari buffer konstanta, dan melakukan transformasi ini untuk setiap puncak.

Transformasi koordinat

Direct3D menggunakan tiga transformasi untuk mengubah koordinat model 3D Anda menjadi koordinat piksel (ruang layar). Transformasi ini adalah transformasi dunia, transformasi tampilan, dan transformasi proyeksi. Untuk informasi selengkapnya, lihat Gambaran umum transformasi.

Matriks transformasi dunia

Perubahan dunia mengubah koordinat dari ruang model, di mana simpul didefinisikan relatif terhadap asal lokal model, ke ruang dunia, di mana simpul didefinisikan relatif terhadap asal yang umum untuk semua objek dalam adegan. Intinya, transformasi dunia menempatkan model ke dunia; oleh karena itu namanya. Untuk informasi selengkapnya, lihat Transformasi dunia.

Lihat matriks transformasi

Transformasi tampilan menemukan penampil di ruang dunia, mengubah simpul menjadi ruang kamera. Di ruang kamera, kamera, atau penampil, berada di asalnya, melihat ke arah z positif. Untuk informasi selengkapnya, buka Menampilkan transformasi.

Matriks transformasi proyeksi

Transformasi proyeksi mengubah frustum tampilan menjadi bentuk cuboid. Frustum tampilan adalah volume 3D dalam adegan yang diposisikan relatif terhadap kamera viewport. Viewport adalah persegi panjang 2D tempat adegan 3D diproyeksikan. Untuk informasi selengkapnya, lihat Viewports dan kliping

Karena ujung dekat frustum tampilan lebih kecil dari ujung jauh, ini memiliki efek memperluas objek yang dekat dengan kamera; ini adalah bagaimana perspektif diterapkan ke adegan. Jadi objek yang lebih dekat dengan pemutar tampak lebih besar; objek yang lebih jauh tampak lebih kecil.

Secara matematis, transformasi proyeksi adalah matriks yang biasanya merupakan skala dan proyeksi perspektif. Fungsinya seperti lensa kamera. Untuk informasi selengkapnya, lihat Transformasi proyeksi.

Status sampler

Status sampler menentukan bagaimana data tekstur diambil sampelnya menggunakan mode pengalamatan tekstur, pemfilteran, dan tingkat detail. Pengambilan sampel dilakukan setiap kali piksel tekstur (atau texel) dibaca dari tekstur.

Tekstur berisi array texel. Posisi setiap texel ditandai dengan (u,v), di mana u adalah lebar dan v merupakan tinggi, dan dipetakan antara 0 dan 1 berdasarkan lebar dan tinggi tekstur. Koordinat tekstur yang dihasilkan digunakan untuk mengatasi texel saat mengambil sampel tekstur.

Saat koordinat tekstur di bawah 0 atau di atas 1, mode alamat tekstur menentukan bagaimana koordinat tekstur membahas lokasi texel. Misalnya, saat menggunakan TextureAddressMode.Clamp, koordinat apa pun di luar rentang 0-1 dijepit ke nilai maksimum 1, dan nilai minimum 0 sebelum pengambilan sampel.

Jika tekstur terlalu besar atau terlalu kecil untuk poligon, maka tekstur difilter agar pas dengan ruang. Filter pembesaran memperbesar tekstur, filter minifikasi mengurangi tekstur agar pas dengan area yang lebih kecil. Pembesaran tekstur mengulangi texel sampel untuk satu atau beberapa alamat yang menghasilkan gambar kabur. Minifikasi tekstur lebih rumit karena perlu menggabungkan lebih dari satu nilai texel ke dalam satu nilai. Ini dapat menyebabkan alias atau tepi berjatuhan tergantung pada data tekstur. Pendekatan paling populer untuk minifikasi adalah menggunakan mipmap. Mipmap adalah tekstur multi-tingkat. Ukuran setiap tingkat adalah kekuatan 2 lebih kecil dari tingkat sebelumnya hingga tekstur 1x1. Ketika minifikasi digunakan, game memilih tingkat mipmap paling dekat dengan ukuran yang diperlukan pada waktu render.

Kelas BasicLoader

BasicLoader adalah kelas loader sederhana yang menyediakan dukungan untuk memuat shader, tekstur, dan jala dari file pada disk. Ini menyediakan metode sinkron dan asinkron. Dalam permainan sampel ini, BasicLoader.h/.cpp file ditemukan di folder Utilitas .

Untuk informasi selengkapnya, lihat Pemuat Dasar.