Sdílet prostřednictvím


Architektura vykreslování I: Úvod do vykreslování

Poznámka:

Toto téma je součástí Vytvoření jednoduché hry pro Univerzální platformu Windows (UPW) pomocí série kurzů DirectX. Téma na tomto odkazu nastaví kontext pro řadu.

Zatím jsme probrali, jak strukturovat hru univerzální platformy Windows (UPW) a jak definovat stavový počítač pro zpracování toku hry. Teď je čas se naučit vyvíjet vykreslovací architekturu. Podívejme se, jak ukázková hra vykresluje herní scénu pomocí Direct3D 11.

Direct3D 11 obsahuje sadu rozhraní API, která poskytují přístup k pokročilým funkcím vysoce výkonného grafického hardwaru, který lze použít k vytvoření 3D grafiky pro aplikace náročné na grafiku, jako jsou hry.

Vykreslování herní grafiky na obrazovce znamená v podstatě vykreslení posloupnosti snímků na obrazovce. V každém snímku musíte vykreslit objekty, které jsou viditelné ve scéně na základě pohledu.

Aby bylo možné vykreslit rámeček, musíte hardwaru předat požadované informace o scéně, aby bylo možné ho zobrazit na obrazovce. Pokud chcete, aby se na obrazovce něco zobrazovalo, musíte začít vykreslovat hned, jak hra začne běžet.

Cíle

Chcete-li nastavit základní vykreslovací rámec pro zobrazení grafického výstupu pro hru UWP DirectX. Můžete to volně rozdělit do těchto tří kroků.

  1. Vytvořte připojení k grafickému rozhraní.
  2. Vytvořte prostředky potřebné k vykreslení grafiky.
  3. Zobrazte grafiku vykreslením rámečku.

Toto téma vysvětluje, jak se vykreslují grafické objekty, které pokrývají kroky 1 a 3.

Vykreslovací architektura II: Vykreslování her popisuje krok 2 – jak nastavit vykreslovací architekturu a jak jsou data připravená před vykreslováním.

Začínáme

Je vhodné se seznámit se základními grafickými a vykreslovacími koncepty. Pokud s Direct3D a vykreslováním začínáte, podívejte se na podmínky a koncepty , kde najdete stručný popis grafických a vykreslovacích termínů použitých v tomto tématu.

Pro tuto hru třída GameRenderer představuje vykreslování pro tuto ukázkovou hru. Zodpovídá za vytváření a údržbu všech objektů Direct3D 11 a Direct2D používaných k vygenerování herních vizuálů. Také udržuje odkaz na objekt Simple3DGame, který je použitý k načtení seznamu objektů k vykreslení, a současně sleduje stav hry pro zobrazení informací (HUD).

V této části kurzu se zaměříme na vykreslení 3D objektů ve hře.

Navázání připojení k grafickému rozhraní

Informace o přístupu k hardwaru pro vykreslování najdete v tématu Definice architektury aplikace UWP pro hru.

Metoda App::Initialize

Funkce std::make_shared, jak je znázorněno níže, se používá k vytvoření shared_ptr na DX::DeviceResources, která také poskytuje přístup k zařízení.

V Direct3D 11 se zařízení používá k přidělování a zničení objektů, vykreslení primitiv a komunikaci s grafickou kartou prostřednictvím grafického ovladače.

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

Zobrazení grafiky vykreslením rámečku

Herní scéna se musí vykreslit při spuštění hry. Pokyny pro vykreslení se spustí v metodě GameMain::Run , jak je znázorněno níže.

Tento jednoduchý průběh/proces je následující.

  1. Aktualizace
  2. Vykreslení
  3. Přítomnost

Metoda 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.
}

Aktualizace

Další informace o tom, jak se aktualizují stavy her v metodě GameMain::Update, najdete v tématu Správa průběhu hry.

Vykreslit

Vykreslování je implementováno voláním GameRenderer::Render metoda z GameMain::Run.

Pokud je povoleno stereo vykreslování, pak existují dva vykreslovací průchody; jeden pro levé oko a jeden pro pravé. V každém průchodu vykreslování vytvoříme vazbu cíle vykreslení a zobrazení podrobného vzorníku se zařízením. Později také vymažeme zobrazení vzorníku hloubky.

Poznámka:

Stereo vykreslování lze provést pomocí jiných metod, jako je jednorázové stereo pomocí instancování vrcholů nebo geometrických shaderů. Metoda dvou průchodů vykreslováním je pomalejší, ale pohodlnější způsob, jak dosáhnout stereoskopického vykreslování.

Po spuštění hry a načtení prostředků aktualizujeme matici projekce, jednou za vykreslovací průchod. Objekty se mírně liší od každého zobrazení. Dále nastavíme řetězec vykreslování grafiky .

Poznámka:

Další informace o tom, jak se načítají prostředky, najdete v tématu Vytvoření a načtení grafických prostředků DirectX .

V této ukázkové hře je renderer navržen tak, aby používal standardní rozložení vrcholů napříč všemi objekty. To zjednodušuje návrh shaderu a umožňuje snadné změny mezi shadery nezávisle na geometrii objektů.

Metoda GameRenderer::Render

Kontext Direct3D nastavíme tak, aby používal rozvržení vstupního vrcholu. Objekty rozložení vstupu popisují, jak se data vyrovnávací paměti vrcholů streamují do kanálu vykreslování .

Dále nastavíme kontext Direct3D tak, aby používal dříve definované konstantní vyrovnávací paměti, které používají fáze shaderu vrcholů fázi kanálu a pixel shader fázi kanálu.

Poznámka:

Podrobnosti o definování konstantních bufferů najdete v tématu Rendering Framework II: Vykreslování her.

Vzhledem k tomu, že stejné rozložení vstupu a sada konstantních vyrovnávacích pamětí se používají pro všechny shadery, které jsou v potrubí, je nastaveno jednou za snímek.

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
        ...
    }
}

Primitivní vykreslování

Při vykreslování scény procházíte všechny objekty, které je potřeba vykreslit. Níže uvedené kroky se opakují pro každý objekt (primitivní).

  • Aktualizujte konstantní vyrovnávací paměť (m_constantBufferChangesEveryPrim) o světovou transformační matici modelu a materiálové informace.
  • M_constantBufferChangesEveryPrim obsahuje parametry pro každý objekt. Zahrnuje transformační matici objektu vůči světu a také materiálové vlastnosti, jako je barva a speculární exponent pro výpočty osvětlení.
  • Nastavte kontext Direct3D tak, aby se použilo rozložení vstupního vrcholu pro data síťového objektu, která mají být streamována do fáze vstupního assembleru (IA) řetězce vykreslování .
  • Nastavte kontext Direct3D tak, aby používal indexový buffer ve fázi vstupního assembleru IA. Zadejte primitivní informace: typ, pořadí dat.
  • Odeslat příkaz k vykreslení indexovaného, neinstancovaného prvku. Metoda GameObject::Render aktualizuje konstantní vyrovnávací paměť primitiva s daty specifickými pro dané primitivum. Výsledkem je DrawIndexed volání kontextu k vykreslení geometrie každého primitiva. Konkrétně toto vykreslovací volání řadí příkazy a data do grafické procesorové jednotky (GPU), jak jsou parametrizovány daty konstantní vyrovnávací paměti. Každé volání kreslení provede vrcholový shader jednou na vrchol a poté pixelový shader jednou pro každý pixel každého trojúhelníku v primitivu. Textury jsou součástí stavu, který pixelový shader používá k vykreslování.

Tady jsou důvody použití více konstantních vyrovnávacích pamětí.

  • Hra používá více konstantních vyrovnávacích pamětí, ale tyto vyrovnávací paměti musí aktualizovat pouze jednou za primitivní prvek. Jak už bylo zmíněno dříve, konstantní vyrovnávací paměti jsou jako vstupy do shaderů, které se spouští pro každé primitivum. Některá data jsou statická (m_constantBufferNeverChanges); některá data jsou konstantní přes rámec (m_constantBufferChangesEveryFrame), například umístění kamery; a některá data jsou specifická pro primitivní, například jejich barvu a textury (m_constantBufferChangesEveryPrim).
  • Herní renderer tyto vstupy odděluje do různých konstantních vyrovnávacích pamětí za účelem optimalizace šířky pásma paměti, kterou procesor a GPU používají. Tento přístup také pomáhá minimalizovat množství dat, která GPU potřebuje ke sledování. GPU má velkou frontu příkazů a pokaždé, když hra volá Draw, tento příkaz je zařazen do fronty spolu s daty přidruženými k němu. Když hra aktualizuje základní konstantní vyrovnávací paměť a vydá následující příkaz Draw, ovladač grafiky přidá tento příkaz a přidružená data do fronty. Pokud hra kreslí 100 primitiv, může mít potenciálně 100 kopií konstantních dat vyrovnávací paměti ve frontě. Aby hra minimalizovala množství dat odesílaných do GPU, používá samostatný primitivní konstantní buffer, který obsahuje pouze aktualizace jednotlivých primitiv.

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

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

DeviceResources::Present – metoda

Zavoláme metodu DeviceResources::Present, abychom zobrazili obsah, který jsme umístili do vyrovnávacích pamětí.

Termín swap chain používáme pro kolekci vyrovnávacích pamětí, které se používají k zobrazení snímků uživateli. Pokaždé, když aplikace uvede nový snímek k zobrazení, první vyrovnávací paměť v rámci výměnného řetězce nahradí tu, která je právě zobrazena. Tento proces se nazývá přepínání nebo otočení. Další informace naleznete v swap chainy.

  • Metoda IDXGISwapChain1 rozhraní Present instruuje DXGI, aby blokovalo, dokud nedojde k vertikální synchronizaci (VSync), čímž uvede aplikaci do režimu spánku, dokud nenastane další VSync. Tím zajistíte, že nebudete ztrácet žádné cykly vykreslování snímků, které se nikdy nezobrazí na obrazovce.
  • rozhraní ID3D11DeviceContext3 metoda DiscardView zahodí obsah cílového vykreslení. Toto je platná operace pouze v případě, že existující obsah bude zcela přepsán. Pokud se použijí zašpiněné nebo posuvné obdélníky, mělo by být toto volání odstraněno.

Návod

Chcete-li dosáhnout plynulé frekvence snímků, je nutné zajistit, aby množství práce na vykreslení snímku odpovídalo času mezi synchronizacemi VSync.

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

Další kroky

Toto téma vysvětluje, jak se grafika vykresluje na displeji, a obsahuje stručný popis některých použitých výrazů vykreslování (níže). Přečtěte si další informace o vykreslování v vykreslovací platformě II: Téma vykreslování her a zjistěte, jak připravit data potřebná před vykreslováním.

Pojmy a koncepty

Jednoduchá herní scéna

Jednoduchá herní scéna se skládá z několika objektů s několika zdroji světla.

Obrazec objektu je definován sadou souřadnic X, Y a Z v prostoru. Skutečné umístění vykreslení ve herním světě lze určit použitím transformační matice na souřadnice X, Y a Z. Může mít také sadu souřadnic textury – vy a V – určující způsob použití materiálu na objekt. Tím se definují vlastnosti povrchu objektu a získáte možnost zjistit, jestli má objekt hrubý povrch (například tenisový míč) nebo hladký lesklý povrch (například bowlingový míč).

Informace o scéně a objektech používá vykreslovací rámec k opětovnému vytvoření scény po snímku, aby scéna ožila na vašem monitoru.

Vykreslovací potrubí

Kanál vykreslování je proces, podle kterého se informace o 3D scéně překládají na obrázek zobrazený na obrazovce. V Direct3D 11 je toto potrubí programovatelné. Fáze můžete přizpůsobit tak, aby podporovaly vaše potřeby vykreslování. Fáze, které obsahují běžná jádra shaderu, jsou programovatelné pomocí programovacího jazyka HLSL. Označuje se také jako pipelina grafického vykreslování nebo jednoduše pipelina.

Abyste mohli tento datový tok vytvořit, musíte být seznámeni s těmito podrobnostmi.

Další informace najdete v tématu Vysvětlení kanálu vykreslování Direct3D 11 a grafický kanál.

HLSL

HLSL je jazyk shaderu vysoké úrovně pro DirectX. Pomocí HLSL můžete vytvořit programovatelné shadery podobné jazyku C pro kanál Direct3D. Další informace naleznete v HLSL.

Stínovače

Shader si lze představit jako sadu instrukcí, které určují, jak se povrch objektu zobrazuje při vykreslení. Ty, které jsou naprogramované pomocí HLSL, se označují jako shadery HLSL. Soubory zdrojového kódu pro shadery [HLSL](#hlsl) mají příponu souboru .hlsl. Tyto shadery lze zkompilovat v době sestavení nebo během běhu a nastavit během běhu do příslušné fáze zpracování. Kompilovaný objekt shaderu má příponu .cso souboru.

Direct3D 9 shadery lze navrhnout pomocí shaderu model 1, shader model 2 a shader model 3; Shadery Direct3D 10 lze navrhovat pouze na model shaderu 4. Shadery Direct3D 11 lze navrhnout pomocí modelu shaderu 5. Direct3D 11.3 a Direct3D 12 lze navrhnout na model shaderu 5.1 a Direct3D 12 lze také navrhnout na model shaderu 6.

Vrcholové a pixelové shadery

Data vstupují do grafického procesoru jako proud primitiv a jsou zpracovávána různými shadery, jako jsou vrcholové shadery a pixlové shadery.

Vrcholové shadery zpracovávají vrcholy a obvykle provádějí operace, jako jsou transformace, kostření a osvětlení. Shadery pixelů umožňují bohaté techniky stínování, jako je osvětlení na pixel a následné zpracování. Kombinuje konstanty, data textury, hodnoty interpolované pro každý vrchol a další data k vytvoření výstupů pro jednotlivé pixely.

Fáze shaderu

Posloupnost těchto různých shaderů, definovaných pro zpracování tohoto streamu primitiv, se v rámci vykreslovacího kanálu označuje jako etapy shaderů. Skutečné fáze závisí na verzi Direct3D, ale obvykle zahrnují vrchol, geometrii a pixelové fáze. K dispozici jsou také další fáze, jako jsou trupové a doménové shadery pro teselaci a výpočetní shader. Všechny tyto fáze jsou zcela programovatelné pomocí HLSL. Další informace naleznete v tématu grafický řetězec.

Různé formáty souborů shaderu

Přípony souborů s kódem shaderu jsou následující.

  • Soubor s příponou .hlsl obsahuje zdrojový kód [HLSL])(#hlsl).
  • Soubor s příponou .cso obsahuje kompilovaný objekt shaderu.
  • Soubor s .h příponou je soubor záhlaví, ale v kontextu kódu shaderu tento soubor záhlaví definuje pole bajtů, které obsahuje data shaderu.
  • Soubor s příponou .hlsli obsahuje formát konstantních bufferů. V ukázkové hře je soubor Shader>ConstantBuffers.hlsli.

Poznámka:

Shader vložíte buď načtením .cso souboru za běhu, nebo přidáním souboru do spustitelného .h kódu. Oba byste ale nepoužili pro stejný shader.

Hlubší porozumění rozhraní DirectX

Direct3D 11 je sada rozhraní API, která nám můžou pomoct vytvořit grafiku pro aplikace náročné na grafiku, jako jsou hry, kde chceme mít dobrou grafickou kartu pro zpracování náročných výpočtů. Tato část stručně vysvětluje koncepty grafického programování Direct3D 11: prostředek, podsourc, zařízení a kontext zařízení.

Zdroj

Prostředky (označované také jako prostředky zařízení) si můžete představit jako informace o tom, jak vykreslit objekt, například texturu, pozici nebo barvu. Prostředky poskytují data do datového kanálu a definují, co se vykreslí během vaší scény. Prostředky se dají načítat z herních médií nebo dynamicky vytvářet v době běhu programu.

Prostředek je ve skutečnosti oblast v paměti, ke které může přistupovat kanál Direct3D . Aby potrubí mohlo efektivně přistupovat k paměti, musí být data, která mu jsou poskytována (například vstupní geometrie, zdroje shaderu a textury), uložena ve zdroji. Existují dva typy prostředků, ze kterých jsou odvozeny všechny prostředky Direct3D: vyrovnávací paměť nebo textura. Pro každou fázi pipeline může být aktivní až 128 prostředků. Další informace naleznete v části Zdroje.

Podzdroj

Termín dílčí zdroj se vztahuje na podmnožinu prostředku. Direct3D může odkazovat na celý prostředek nebo může odkazovat na podmnožinu prostředku. Další informace naleznete v podzdroji.

Vzorník hloubky

Zdroj hloubkového a šablonovacího bufferu obsahuje formát a vyrovnávací paměť pro uchovávání informací o hloubce a šablonování. Vytvoří se pomocí prostředku textury. Další informace o tom, jak vytvořit prostředek podrobného vzorníku, naleznete v tématu Konfigurace Depth-Stencil Funkce. K prostředku podrobného vzorníku přistupujeme prostřednictvím zobrazení podrobného vzorníku implementovaného pomocí rozhraní ID3D11DepthStencilView.

Podrobné informace nám říkají, které oblasti mnohoúhelníku jsou za ostatními, abychom mohli určit, které oblasti jsou skryté. Informace vzorníku nám říkají, které pixely jsou maskované. Lze jej použít k vytvoření speciálních efektů, protože určuje, zda je nakreslený pixel, nebo ne; nastaví bit na 1 nebo 0.

Další informace naleznete v tématu zobrazení hloubkového vzorníku, hloubkové vyrovnávací pamětia vyrovnávací paměti vzorníku.

Cíl vykreslení

Cíl vykreslení je prostředek, na který můžeme zapisovat na konci vykreslovacího průchodu. Obvykle se vytváří pomocí metody ID3D11Device::CreateRenderTargetView pomocí zadního pufru výměnného řetězce (který je také prostředkem) jako vstupního parametru.

Každý cíl vykreslování by měl také mít odpovídající hloubkové zobrazení, protože když použijeme OMSetRenderTargets k nastavení cíle vykreslování před jeho použitím, vyžaduje také hloubkové zobrazení. K cílovému prostředku vykreslení přistupujeme prostřednictvím zobrazení cíle vykreslení implementovaného pomocí rozhraní ID3D11RenderTargetView.

Zařízení

Zařízení si můžete představit jako způsob, jak přidělit a zničit objekty, vykreslit primitivy a komunikovat s grafickou kartou prostřednictvím ovladače grafiky.

Pro přesnější vysvětlení, zařízení Direct3D je vykreslovací komponenta v rámci Direct3D. Zařízení zapouzdřuje a ukládá stav vykreslování, provádí transformace a operace osvětlení a rastruje obrázek na povrch. Další informace naleznete v tématu Zařízení

Zařízení je reprezentováno rozhraním ID3D11Device . Jinými slovy, rozhraní ID3D11Device představuje virtuální zobrazovací adaptér a slouží k vytváření prostředků vlastněných zařízením.

Existují různé verze ID3D11Device. ID3D11Device5 je nejnovější verze a přidává do nich nové metody v ID3D11Device4. Další informace o tom, jak Direct3D komunikuje s podkladovým hardwarem, najdete v tématu Architektura modelu ovladače zařízení systému Windows (WDDM).

Každá aplikace musí mít alespoň jedno zařízení; většina aplikací vytváří pouze jednu. Vytvořte zařízení pro jeden z hardwarových ovladačů nainstalovaných na vašem počítači zavoláním D3D11CreateDevice nebo D3D11CreateDeviceAndSwapChain a zadáním typu ovladače pomocí příznaku D3D_DRIVER_TYPE . Každé zařízení může v závislosti na požadované funkci používat jeden nebo více kontextů zařízení. Další informace naleznete v článku funkce D3D11CreateDevice.

Kontext zařízení

Kontext zařízení je používán k nastavení stavu pipeline a k vytváření příkazů pro vykreslování pomocí prostředků ve vlastnictví zařízení .

Direct3D 11 implementuje dva typy kontextů zařízení, jeden pro okamžité vykreslování a druhý pro odložené vykreslování; Oba kontexty jsou reprezentovány rozhraním ID3D11DeviceContext .

Rozhraní ID3D11DeviceContext mají různé verze; ID3D11DeviceContext4 přidá nové metody do těch v ID3D11DeviceContext3.

ID3D11DeviceContext4 je zaveden v systému Windows 10 Creators Update a je nejnovější verzí rozhraní ID3D11DeviceContext . Aplikace cílené na Windows 10 Creators Update a novější by měly místo starších verzí používat toto rozhraní. Další informace viz ID3D11DeviceContext4.

DX::D eviceResources

Třída DX::DeviceResources je v souborech DeviceResources.cpp/.h a řídí všechny prostředky zařízení DirectX.

Vyrovnávací paměť

Prostředek vyrovnávací paměti je kolekce plně typovaných dat seskupených do elementů. Vyrovnávací paměti můžete použít k ukládání široké škály dat, včetně vektorů pozice, normálních vektorů, souřadnic textury ve vyrovnávací paměti pro vrcholy, indexů ve vyrovnávací paměti pro indexy nebo stavu zařízení. Prvky vyrovnávací paměti mohou obsahovat zhuštěné datové hodnoty (například R8G8B8A8 povrchové hodnoty), jedno 8bitové celé číslo nebo čtyři 32bitové hodnoty s pohyblivou desetinnou čárkou.

K dispozici jsou tři typy vyrovnávacích pamětí: vyrovnávací paměť vrcholu, vyrovnávací paměť indexu a konstantní vyrovnávací paměť.

Buffer vrcholů

Obsahuje vrcholová data použitá k definování vaší geometrie. Vrcholová data zahrnují souřadnice polohy, barevná data, data souřadnic textury, normálová data a tak dále.

Indexový buffer

Obsahuje celočíselné posuny do vyrovnávacích pamětí vrcholů a slouží k efektivnějšímu vykreslení primitiv. Indexový buffer obsahuje sekvenční sadu 16bitových nebo 32bitových indexů; každý index se používá k identifikaci vrcholu ve vyrovnávací paměti vrcholů.

Vyrovnávací paměť konstant nebo vyrovnávací paměť shaderu konstantních hodnot

Umožňuje efektivně dodávat data shaderu do pipeline. Konstantní buffery můžete použít jako vstupy do shaderů, které se spouští pro každý primitiv, a uložit výsledky fáze stream-output v renderovacím řetězci. Koncepčně vypadá konstantní vyrovnávací paměť stejně jako jednoprvková vyrovnávací paměť vrcholů.

Návrh a implementace vyrovnávacích pamětí

Vyrovnávací paměti můžete navrhnout na základě datového typu, například v naší ukázkové hře, vytvoří se jedna vyrovnávací paměť pro statická data, druhá pro data, která jsou konstantní přes rámec, a další pro data specifická pro primitivní data.

Všechny typy vyrovnávací paměti jsou zapouzdřeny rozhraním ID3D11Buffer a můžete vytvořit prostředek vyrovnávací paměti voláním ID3D11Device::CreateBuffer. Vyrovnávací paměť však musí být svázaná s potrubím, aby k ní bylo možné získat přístup. Vyrovnávací paměti mohou být současně svázány s více fázemi potrubí během čtení. Vyrovnávací paměť může být také vázána na jednu etapu pro zápis; stejnou vyrovnávací paměť nelze současně vázat pro čtení i zápis.

Buffery můžete svázat těmito způsoby.

  • Do fáze input assembler voláním ID3D11DeviceContext metod, jako jsou ID3D11DeviceContext::IASetVertexBuffers a ID3D11DeviceContext::IASetIndexBuffer.
  • Do fáze výstupu datového proudu se dostanete voláním ID3D11DeviceContext::SOSetTargets.
  • Do fáze stínování se dostaneme voláním metod shaderu, například ID3D11DeviceContext::VSSetConstantBuffers.

Další informace naleznete v tématu Úvod do vyrovnávací paměti v Direct3D 11.

DXGI

Microsoft DirectX Graphics Infrastructure (DXGI) je subsystém, který zapouzdřuje některé úlohy nízké úrovně potřebné pro Direct3D. Při použití DXGI ve vícevláknové aplikaci je potřeba věnovat zvláštní pozornost, aby se zajistilo, že nedojde k zablokování. Další informace najdete v tématu Multithreading a DXGI

Úroveň funkce

Úroveň funkcí je koncept zavedený v Direct3D 11 pro zpracování rozmanitosti grafických karet v nových a stávajících počítačích. Úroveň funkcí je dobře definovaná sada funkcí grafického procesoru (GPU).

Každá grafická karta implementuje určitou úroveň funkcí DirectX v závislosti na nainstalovaných grafických procesorech. V předchozích verzích Microsoft Direct3D můžete zjistit verzi Direct3D, kterou grafická karta implementovala, a pak aplikaci odpovídajícím způsobem naprogramovat.

Při vytváření zařízení se můžete pokusit vytvořit zařízení odpovídající úrovni funkce, kterou chcete požádat. Pokud vytváření zařízení funguje, existuje tato úroveň funkce, pokud ne, hardware tuto úroveň funkcí nepodporuje. Můžete se pokusit znovu vytvořit zařízení na nižší úrovni funkce, nebo se můžete rozhodnout aplikaci ukončit. Například úroveň funkce 12_0 vyžaduje Direct3D 11.3 nebo Direct3D 12 a model shaderu 5.1. Další informace najdete v tématu Úrovně funkcí Direct3D: Přehled jednotlivých úrovní funkcí.

Pomocí úrovní funkcí můžete vyvíjet aplikaci pro Direct3D 9, Microsoft Direct3D 10 nebo Direct3D 11 a pak ji spustit na hardwaru 9, 10 nebo 11 (s některými výjimkami). Další informace najdete v tématu Úrovně funkcí Direct3D.

Stereo vykreslování

Stereo vykreslování se používá k vylepšení iluze hloubky. Používá dva obrázky, jeden z levého oka a druhý z pravého oka k zobrazení scény na obrazovce.

cs-CZ: Matematicky použijeme stereo projekční matici, což je mírný vodorovný posun doprava a doleva od běžné mono projekční matice, k dosažení tohoto cíle.

Pro stereoskopické vykreslení jsme provedli dva průchody v této ukázkové hře.

  • Vytvořte vazbu k pravému cíli vykreslení, použijte správnou projekci a pak nakreslete primitivní objekt.
  • Vytvořte vazbu k levému cíli vykreslení, použijte levou projekci a pak nakreslete primitivní objekt.

Kamera a souřadnicový prostor

Hra má kód, který umožňuje průběžně aktualizovat svět v jeho vlastním souřadnicovém systému (někdy se nazývá prostor světa nebo prostor scény). Všechny objekty, včetně kamery, jsou umístěny a orientované v tomto prostoru. Další informace naleznete v tématu souřadnicové systémy.

Shader vrcholů provádí náročný proces převodu ze souřadnic modelu na souřadnice zařízení pomocí následujícího algoritmu (kde V je vektor a M je matice).

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

  • M(model-to-world) je transformační matice pro souřadnice modelu do světových souřadnic, označované také jako matice světové transformace. To je poskytováno primitivem.
  • M(world-to-view) je transformační matice, která převádí souřadnice světa na souřadnice zobrazení, označovaná také jako matice zobrazení .
    • Poskytuje to zobrazovací matice kamery. Je definováno umístěním kamery a směrovými vektory (pohled na vektor, který ukazuje přímo na scénu z kamery, a pohled nahoru vektor, který je kolmý a směřuje vzhůru).
    • V ukázkové hře je m_viewMatrix matice transformace zobrazení a počítá se pomocí Camera::SetViewParams.
  • M(view-to-device) je transformační matice pro zobrazení souřadnic na souřadnice zařízení, označované také jako transformační matice projekce.
    • To je poskytováno projekcí kamery. Poskytuje informace o tom, kolik místa je skutečně viditelné v závěrečné scéně. Pole zobrazení (FoV), poměr stran a roviny výřezu definují matici transformace projekce.
    • V ukázkové hře m_projectionMatrix definuje transformaci na souřadnice projekce vypočítané pomocí Camera::SetProjParams (pro stereo projekce použijete dvě matice projekce – jednu pro zobrazení každého oka).

Kód shaderu v VertexShader.hlsl je naplněn těmito vektory a maticemi z konstantních vyrovnávacích pamětí a provádí tuto transformaci pro každý vrchol.

Transformace souřadnic

Direct3D používá tři transformace ke změně souřadnic 3D modelu na souřadnice pixelů (prostor obrazovky). Tyto transformace jsou transformace světa, transformace pohledu a transformace projekce. Další informace najdete v tématu Přehled transformace.

Světová transformační matice

Transformace světa mění souřadnice z prostoru modelu, kde jsou vrcholy definovány vzhledem k místnímu původu modelu, do světového prostoru, kde jsou vrcholy definovány vzhledem k počátku společnému pro všechny objekty ve scéně. V podstatě, transformace světa umisťuje model do světa, a proto se tak jmenuje. Další informace naleznete v tématu Světová transformace.

Zobrazit matici transformace

Nastavení pohledu určuje polohu diváka ve světovém prostoru a transformuje vrcholy do prostoru kamery. V prostoru kamery se pohled nachází v počátku a směřuje do kladného směru osy z. Další informace najdete na Zobrazení transformace.

Matice projekční transformace

Transformace projekce převede zobrazovací frustum do tvaru kvádru. Zobrazovací frustum je 3D objem ve scéně umístěný vzhledem ke kameře zorného pole. Oblast zobrazení je 2D obdélník, do kterého se promítá 3D scéna. Další informace najdete v tématu Výřezy a

Vzhledem k tomu, že blízký konec projekčního objemu je menší než vzdálený konec, má to za následek zvětšení objektů, které jsou blízko kamery; tím se perspektiva aplikuje na scénu. Takže objekty, které jsou blíže hráči, se objeví větší; objekty, které jsou dále pryč, se zobrazují menší.

Matematicky je projekční transformace matice, která je obvykle jak měřítkovou, tak i perspektivní projekcí. Funguje jako objektiv kamery. Další informace naleznete v části Projektivní transformace.

Stav sampleru

Stav sampleru určuje způsob vzorkování dat textury pomocí režimů adres textury, filtrování a úrovně podrobností. Vzorkování se provádí při každém čtení texturového pixelu (nebo texelu) z textury.

Textura obsahuje pole texelů. Pozice každého texelu je označena (u,v), kde u je šířka a v je výška a je mapována mezi 0 a 1 na základě šířky a výšky textury. Výsledné souřadnice textury se používají k adresaci texelu při vzorkování textury.

Pokud jsou souřadnice textury nižší než 0 nebo vyšší než 1, režim adresy textury definuje, jak souřadnice textury adresuje umístění texelu. Například při použití TextureAddressMode.Clamp, všechny souřadnice mimo rozsah 0-1 se uchytnou na maximální hodnotu 1 a minimální hodnotu 0 před vzorkováním.

Pokud je textura příliš velká nebo příliš malá pro mnohoúhelník, je textura filtrována tak, aby odpovídala prostoru. Filtr zvětšení zvětší texturu, filtr minifikace zmenší texturu tak, aby se vešla do menší oblasti. Zvětšení textury opakuje vzorový texel pro jednu nebo více adres, což vede k rozmazanějšímu obrazu. Minifikace textury je složitější, protože vyžaduje kombinování více než jedné hodnoty texelu do jedné hodnoty. To může způsobit aliasing nebo nerovné okraje v závislosti na texturových údajích. Nejoblíbenější přístup k minifikaci je použití mapy mipmap. Mipmap je víceúrovňová textura. Velikost každé úrovně je o mocninu 2 menší než předchozí úroveň až do textury 1x1. Když se použije minifikace, hra zvolí úroveň mipmap nejblíže k velikosti potřebné v době vykreslování.

Třída BasicLoader

BasicLoader je jednoduchá načítací třída, která poskytuje podporu pro načítání shaderů, textur a modelů ze souborů na disku. Poskytuje synchronní i asynchronní metody. V této ukázkové hře BasicLoader.h/.cpp jsou soubory nalezeny ve složce Nástroje .

Další informace naleznete v části Základní načítání.