Megosztás a következőn keresztül:


Renderelési keretrendszer I: Bevezetés a renderelésbe

Megjegyzés:

Ez a témakör a Egyszerű Univerzális Windows Platform (UWP) játék készítése DirectX-szel című oktatóanyag-sorozat része. A hivatkozás témaköre beállítja a sorozat kontextusát.

Eddig bemutattuk, hogyan építhet fel univerzális Windows platform (UWP) játékokat, és hogyan definiálhat egy állapotgépet a játék folyamatának kezeléséhez. Most itt az ideje, hogy megtanulja, hogyan fejlesztheti a renderelési keretrendszert. Nézzük meg, hogyan jeleníti meg a mintajáték a játékjelenetet a Direct3D 11 használatával.

A Direct3D 11 olyan API-kat tartalmaz, amelyek hozzáférést biztosítanak a nagy teljesítményű grafikus hardver speciális funkcióihoz, amelyek 3D-s grafikák létrehozására használhatók a grafikus igényes alkalmazásokhoz, például játékokhoz.

A játék ábráinak képernyőre renderelése alapvetően képkockák sorozatának képernyőre renderelését jelenti. Minden egyes keretben a jelenetben látható objektumokat kell renderelnie a nézet alapján.

A keret megjelenítéséhez át kell adnia a szükséges jelenetinformációkat a hardvernek, hogy azok megjelenjenek a képernyőn. Ha valamit meg szeretne jeleníteni a képernyőn, azonnal el kell kezdenie a renderelést, amint a játék elindul.

Célkitűzések

Alapszintű renderelési keretrendszer beállítása egy UWP DirectX-játék grafikus kimenetének megjelenítéséhez. Ezt lazán lebonthatja erre a három lépésre.

  1. Hozzon létre kapcsolatot a grafikus felülettel.
  2. Hozza létre a grafikus rajzhoz szükséges erőforrásokat.
  3. A kép megjelenítése a keret megjelenítésével.

Ez a témakör az 1. és a 3. lépésre kiterjedő grafikus megjelenítést ismerteti.

Renderelési keretrendszer II: A játékmegjelenítés a 2. lépésre terjed ki – a renderelési keretrendszer beállításának és az adatok előkészítésének módjára a renderelés előtt.

Kezdj hozzá

Érdemes megismerkednie az alapszintű grafikus és renderelési fogalmakkal. Ha még nem használta a Direct3D-t és a renderelést, a jelen témakörben használt grafikus és renderelési kifejezések rövid leírásáért tekintse meg a használati feltételeket és fogalmakat .

Ebben a játékban a GameRenderer osztály a mintajáték renderelője. Feladata a játékvizualizációk létrehozásához használt összes Direct3D 11 és Direct2D objektum létrehozása és karbantartása. Emellett hivatkozik a renderelendő objektumok listájának lekérésére használt Simple3DGame objektumra, valamint a játék állapotára a fej-up megjelenítéshez (HUD).

Az oktatóanyag ezen részében a 3D objektumok renderelésével foglalkozunk a játékban.

Kapcsolat létrehozása a grafikus felülettel

A rendereléshez szükséges hardver eléréséről további információt a játék UWP-alkalmazás-keretrendszerének meghatározása című témakörben talál.

Az alkalmazás::Inicializálási módszer

Az std::make_shared függvény, az alábbi példa szerint, egy shared_ptr-t hoz létre a DX::DeviceResourcespéldányához, amely szintén hozzáférést biztosít az eszközhöz.

A Direct3D 11-ben az eszköz objektumok lefoglalására és megsemmisítésére, primitívek renderelésére és a grafikus illesztőprogramon keresztüli kommunikációra szolgál.

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

A kép megjelenítése a keret megjelenítésével

A játékjelenetnek renderelnie kell a játék indításakor. A renderelésre vonatkozó utasítások a GameMain::Run metódusban kezdődnek, ahogy az alább látható.

Az egyszerű folyamat ez.

  1. Frissítés
  2. Megjelenítés
  3. Jelenlegi

GameMain::Run metódus

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

Frissítés

A GameMain::Update metódusban a játékállapotok frissítésével kapcsolatos további információkért tekintse meg a Játékfolyamat-kezelési témakört.

Renderelés

A renderelés a GameRenderer::Render metódus hívásával van megvalósítva a GameMain::Runmetódusból.

Ha a sztereó renderelés engedélyezve van, akkor két megjelenítési lépés van– egy a bal szemhez, egy pedig a jobbhoz. Minden renderelési lépésben a renderelési célt és a mélységi rajzsablon nézetet az eszközhöz kötjük. A mélységi-sablon nézetet is töröljük a folyamat végén.

Megjegyzés:

A sztereó renderelés más módszerekkel is elvégezhető, például csúcspont-instancing vagy geometriai árnyékolók használatával. A két rendereléses eljárás lassabb, de kényelmesebb módja a sztereó renderelésnek.

A játék futtatása és az erőforrások betöltése után a leképezési mátrixot renderelésenként egyszer frissítjük. Az objektumok kissé eltérnek az egyes nézetektől. Ezután beállítjuk a grafikus renderelési folyamatot.

Megjegyzés:

Az erőforrások betöltési módjáról további információt a DirectX grafikus erőforrások létrehozása és betöltése című témakörben talál.

Ebben a mintajátékban a renderelő úgy van kialakítva, hogy minden objektumon szabványos csúcselrendezést használjon. Ez leegyszerűsíti az árnyékoló kialakítását, és lehetővé teszi az árnyékolók közötti egyszerű változásokat, függetlenül az objektumok geometriájától.

GameRenderer::Render metódus

A Direct3D-környezetet egy bemeneti csúcselrendezés használatára állítottuk be. A bemeneti elrendezésű objektumok azt írják le, hogyan történik a csúcspont pufferadatainak streamelése a renderelési folyamatba.

Ezután a Direct3D-környezetet úgy állítjuk be, hogy a korábban definiált állandó puffereket használja, amelyeket a csúcspont-árnyékoló folyamatszakasza és a képpontárnyékoló folyamatszakasza használ.

Megjegyzés:

Az állandó pufferek definíciójáról további információt a Rendering Framework II: Game Rendering című témakörben talál.

Mivel a folyamat összes shaderje ugyanazt a bemeneti elrendezést és állandó pufferkészletet használja, ezt keretenként csak egyszer kell beállítani.

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

Primitív renderelés

A jelenet megjelenítésekor végighalad az összes megjelenítendő objektumon. Az alábbi lépések minden objektumhoz (primitív) ismétlődnek.

  • Frissítse az állandó puffert (m_constantBufferChangesEveryPrim) a modell világátalakítási mátrixával és anyagadataival.
  • A m_constantBufferChangesEveryPrim minden objektumhoz tartalmaz paramétereket. Tartalmazza az objektum-világ transzformációs mátrixot, valamint az olyan anyagtulajdonságokat, mint a szín és a fényszámítások spekuláris kitevője.
  • Állítsa be a Direct3D környezetet úgy, hogy a mesh objektum adatok bemeneti csúcselrendezését használják a bemeneti-összeállító (IA) szakaszban a renderelési folyamat során.
  • Állítsa be a Direct3D környezetet az IA szakaszban egy indexpuffer használatára. Adja meg a primitív adatokat: típus, adatsorrend.
  • Indítson el egy rajzhívást az indexelt, nem példányos primitív rajzolásához. A GameObject::Render metódus frissíti a primitív állandó puffert az adott primitív adatokkal. Ez egy DrawIndexed hívást eredményez a környezeten, hogy megrajzolja az egyes primitívek geometriáit. Ez a kirajzolási hívás a parancsokat és adatokat a grafikus feldolgozó egység (GPU) számára sorba állítja, az állandó pufferadatok alapján paraméterezve. Minden rajzhívás csúcsonként egyszer hajtja végre a csúcsárnyékolót, majd a képpontárnyékoló egyszer a primitív háromszögek minden képpontjához. A textúrák annak az állapotnak a részét képezik, amelyet a képpontárnyékoló a rendereléshez használ.

Az alábbiakban bemutatjuk, hogy miért érdemes több állandó puffert használni.

  • A játék több állandó puffert használ, de primitívenként csak egyszer kell frissítenie ezeket a puffereket. Ahogy korábban említettük, az állandó pufferek olyan bemenetek, mint az egyes primitívekhez futó árnyékolók bemenetei. Egyes adatok statikusak (m_constantBufferNeverChanges); egyes adatok állandóak a kereten (m_constantBufferChangesEveryFrame), például a kamera helyzete; és néhány adat a primitívre jellemző, például a színére és anyagmintáira (m_constantBufferChangesEveryPrim).
  • A játék renderelője ezeket a bemeneteket különböző állandó pufferekre választja el, hogy optimalizálja a processzor és a GPU által használt memória-sávszélességet. Ez a megközelítés segít minimalizálni a GPU által nyomon követendő adatok mennyiségét is. A GPU-nak nagy parancssora van, és minden alkalommal, amikor a játék felhívja a Rajzot, a parancs várólistára kerül a hozzá tartozó adatokkal együtt. Amikor a játék frissíti a primitív állandó puffert, és kiadja a következő Rajz parancsot, a grafikus illesztőprogram hozzáadja a következő parancsot és a kapcsolódó adatokat a sorba. Ha a játék 100 primitívet rajzol, akkor akár 100 másolata is lehet az állandó puffer adatoknak az üzenetsorban. A játék által a GPU-ra küldött adatok mennyiségének minimalizálása érdekében a játék egy külön primitív állandó puffert használ, amely csak az egyes primitívek frissítéseit tartalmazza.

GameObject::Render metódus

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

MeshObject::Render módszer

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 metódus

Meghívjuk a DeviceResources::P resent metódust a pufferekben elhelyezett tartalom megjelenítéséhez.

A felcserélési lánc kifejezést olyan pufferek gyűjteményéhez használjuk, amelyek kereteket jelenítenek meg a felhasználó számára. Minden alkalommal, amikor egy alkalmazás új keretet jelenít meg a megjelenítéshez, a felcserélési lánc első puffere a megjelenített puffer helyét veszi át. Ezt a folyamatot felcserélésnek vagy megfordításnak nevezzük. További információ: Láncok felcserélése.

  • Az IDXGISwapChain1 interfész Present metódusa arra utasítja a DXGI-t , hogy tiltsa le a függőleges szinkronizálás (VSync) befejezéséig, így az alkalmazás alvó állapotba kerül a következő VSync-ig. Az biztosítja, hogy ne pazaroljunk időt olyan ciklusokra, amelyek kereteket renderelnek, de soha nem jelennek meg a képernyőn.
  • Az ID3D11DeviceContext3 interfész DiscardView metódusa elveti a képfeldolgozási céltartalmát. Ez csak akkor érvényes művelet, ha a meglévő tartalom teljesen felülíródik. Ha piszkos vagy görgető téglalapokat használ, akkor ezt a hívást el kell távolítani.

Jótanács

A sima képkockasebesség eléréséhez gondoskodnia kell arról, hogy a keret rendereléséhez szükséges munka mennyisége illeszkedjen a VSyncs közötti időhöz.

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

Következő lépések

Ez a témakör bemutatta, hogyan jelennek meg a grafikus elemek a kijelzőn, és rövid leírást nyújt a használt renderelési kifejezések némelyikéről (alább). További információ a renderelésről a Renderelési keretrendszer II. játékmegjelenítési témakörében, és megtudhatja, hogyan készítheti elő a renderelés előtt szükséges adatokat.

Kifejezések és fogalmak

Egyszerű játékjelenet

Egy egyszerű játékjelenet néhány objektumból áll, több fényforrással.

Az objektum alakzatát az X, Y, Z koordináták készlete határozza meg a térben. A tényleges renderelési hely a játék világában úgy határozható meg, hogy egy transzformációs mátrixot alkalmaz az X, Y, Z koordinátákra. Emellett rendelkezhet egy textúra-koordináták készletével – U és V –, amelyek meghatározzák, hogyan alkalmazzuk az anyagot az objektumra. Ez határozza meg az objektum felületi tulajdonságait, és lehetővé teszi annak megtekintését, hogy egy objektum durva felülettel (például teniszlabda) vagy sima fényes felülettel (például bowlinglabda) rendelkezik-e.

A jelenet- és objektumadatokat a renderelési keretrendszer használja a jelenetkeret keret szerinti újbóli létrehozásához, így azok életre kelnek a kijelzőmonitoron.

Renderelési folyamat

A renderelési folyamat az a folyamat, amellyel a 3D jelenetinformációk a képernyőn megjelenő képre lesznek lefordítva. A Direct3D 11-ben ez a folyamat programozható. A fázisokat a renderelési igényekhez igazíthatja. A közös árnyékolómagokat tartalmazó szakaszok a HLSL programozási nyelv használatával programozhatók. Grafikus renderelési folyamatnak vagy egyszerűen folyamatnak is nevezik.

A pipeline létrehozásában ismernie kell ezeket a részleteket.

További információ : A Direct3D 11 renderelési folyamat és aGrafikus folyamat ismertetése.

HLSL

A HLSL a DirectX magas szintű árnyékoló nyelve. A HLSL használatával C-szerű programozható árnyékolókat hozhat létre a Direct3D-folyamathoz. További információ: HLSL.

Shader

Az árnyékolók olyan utasítások halmazaként is felfoghatók, amelyek meghatározzák, hogyan jelenik meg egy objektum felülete rendereléskor. A HLSL használatával programozottakat HLSL-árnyékolóknak nevezzük. A [HLSL])(#hlsl) árnyékolók forráskódfájljai rendelkeznek a .hlsl fájlkiterjesztéssel. Ezek az árnyékolók összeállításkor vagy futásidőben állíthatók össze, és futásidőben a megfelelő folyamatszakaszba állíthatók be. A lefordított árnyékolóobjektum fájlkiterjesztése .cso.

A Direct3D 9 árnyékolók az 1. árnyékolómodell, a 2. árnyékolómodell és a 3. árnyékolómodell használatával tervezhetők; A Direct3D 10 árnyékolók csak a 4. árnyékolómodellen tervezhetők. A Direct3D 11 árnyékolók az 5. árnyékolómodellen tervezhetők. A Direct3D 11.3 és a Direct3D 12 az 5.1-s árnyékolómodellen, a Direct3D 12 pedig a 6. árnyékolómodellen is tervezhető.

Csúcsárnyalók és képpontárnyalók

Az adatok primitívek adatfolyamaként kerülnek be a grafikus folyamatba, és különböző árnyékolók, például a csúcspont-árnyékolók és a képpontárnyékolók dolgozzák fel.

A vertex shaderek csúcsokat dolgoznak fel, és általában olyan műveleteket hajtanak végre, mint az átalakítások, a bőrözés és a világítás. A képpontárnyékolók olyan gazdag árnyékolási technikákat tesz lehetővé, mint a képpontonkénti világítás és az utófeldolgozás. Az állandó változókat, a textúraadatokat, az interpolált csúcsonkénti értékeket és más adatokat egyesíti a képpontonkénti kimenetek létrehozásához.

Árnyékoló fázisai

A primitívek adatfolyamának feldolgozására definiált különböző árnyékolók sorozatát árnyékoló szakaszoknak nevezzük egy renderelési folyamatban. A tényleges szakaszok a Direct3D verziójától függenek, de általában tartalmazzák a csúcsot, a geometriát és a képpontszakaszokat. Vannak más szakaszok is, például a tessellation burkolat- és tartományárnyékolói, valamint a számítási árnyékoló. Ezek a szakaszok teljesen programozhatók a HLSL használatával. További információért lásd: Grafikai folyamat.

Különböző shader fájlformátumok

Az alábbiakban a shader-kód kiterjesztéseit találja.

  • A kiterjesztéssel rendelkező .hlsl fájlok [HLSL])(#hlsl) forráskódot tárolnak.
  • A .cso kiterjesztésű fájlok egy lefordított árnyékolóobjektumot tárolnak.
  • A kiterjesztéssel rendelkező .h fájlok fejlécfájlok, de árnyékolókód-környezetben ez a fejlécfájl definiál egy bájttömböt, amely árnyékolóadatokat tartalmaz.
  • A kiterjesztéssel rendelkező .hlsli fájlok az állandó pufferek formátumát tartalmazzák. A mintajátékban a fájl Shaders>ConstantBuffers.hlsli.

Megjegyzés:

Árnyékolót úgy ágyazhat be, hogy futtatáskor betölt egy .cso fájlt, vagy hozzáad egy .h fájlt a végrehajtható kódhoz. De mindkettőt nem használná ugyanahhoz az árnyékolóhoz.

A DirectX mélyebb ismerete

A Direct3D 11 olyan API-k készlete, amelyek segítségével grafikus elemeket hozhatunk létre olyan grafikus alkalmazásokhoz, mint a játékok, ahol jó grafikus kártyát szeretnénk használni az intenzív számítások feldolgozásához. Ez a szakasz röviden ismerteti a Direct3D 11 grafikus programozási fogalmait: az erőforrást, az alforrást, az eszközt és az eszközkörnyezetet.

Erőforrás

Az erőforrásokra (más néven eszközerőforrásokra) úgy gondolhat, mint az objektumok megjelenítésére vonatkozó információk, például a textúra, a pozíció vagy a szín. Az erőforrások adatokat szolgáltatnak a folyamatnak, és meghatározzák, hogy mi jelenik meg a jelenet során. Az erőforrások betölthetők a játék adathordozójáról, vagy futtatáskor dinamikusan hozhatók létre.

Az erőforrás valójában egy memóriaterület, amelyet a Direct3D csővezetékérhet el. Ahhoz, hogy a folyamat hatékonyan férhessen hozzá a memóriához, a folyamathoz biztosított adatokat (például bemeneti geometriát, árnyékoló erőforrásokat és textúrákat) egy erőforrásban kell tárolni. Kétféle erőforrás létezik, amelyekből az összes Direct3D-erőforrás származik: puffer vagy textúra. Minden folyamatszakaszhoz legfeljebb 128 erőforrás lehet aktív. További információ: Erőforrások.

Alforrás

Az alforrás kifejezés egy erőforrás egy részhalmazára hivatkozik. A Direct3D hivatkozhat egy teljes erőforrásra, vagy hivatkozhat egy erőforrás részhalmazára. További információ: Subresource.

Mélységi rajzsablon

A mélységi rajzsablon-erőforrás a mélység és a rajzsablon adatainak tárolására használható formátumot és puffert tartalmazza. Ez egy textúraerőforrás használatával jön létre. További információ a mélységi rajzsablon-erőforrás létrehozásáról: Depth-Stencil funkció konfigurálása. A mélységi rajzsablon erőforrást az ID3D11DepthStencilView felülettel implementált mélységi rajzsablon nézeten keresztül érhetjük el.

A mélységi információk azt jelzik, hogy a sokszögek mely területei állnak mások mögött, hogy megállapíthassuk, melyek rejtettek. A rajzsablon adatai azt jelzik, hogy mely képpontokat maszkolja a rendszer. Speciális effektusok előállítására használható, mivel meghatározza, hogy egy képpont rajzolva van-e, vagy sem; a bitet 1 vagy 0 értékre állítja.

További információ: Rajzsablon nézet, mélységi pufferés rajzsablon puffer.

Renderelési cél

A renderelési cél egy olyan erőforrás, amelybe a renderelés végén írhatunk. Ez általában az ID3D11Device::CreateRenderTargetView metódussal jön létre a swap lánc hátpufferét bemeneti paraméterként használva (amely szintén erőforrás).

Minden renderelési célnak rendelkeznie kell egy megfelelő mélységi rajzsablon nézettel is, mert ha az OMSetRenderTargets használatával állítjuk be a renderelési célhelyet a használat előtt, akkor mélységi rajzsablon nézetre is szükség van. A renderelési célerőforrást az ID3D11RenderTargetView felülettel implementált renderelési célnézeten keresztül érjük el.

Eszköz

Képzelhet el egy eszközt úgy, hogy objektumokat foglaljon le és pusztítson el, primitíveket jelenítsen meg, és kommunikáljon a grafikus kártyával a grafikus illesztőprogramon keresztül.

Pontosabb magyarázatként a Direct3D-eszköz a Direct3D renderelési összetevője. Az eszköz beágyazza és tárolja a renderelési állapotot, elvégzi az átalakításokat és a világítási műveleteket, és raszterizál egy képet egy felületre. További információ: Eszközök

Az eszközt az ID3D11Device interfész jelöli. Más szóval az ID3D11Device interfész egy virtuális kijelzőadaptert jelöl, és az eszköz tulajdonában lévő erőforrások létrehozására szolgál.

Az ID3D11Device különböző verziói vannak. Az ID3D11Device5 a legújabb verzió, és új metódusokat ad hozzá az ID3D11Device4-ben lévőkhöz. További információért arról, hogyan kommunikál a Direct3D a mögöttes hardverrel, tekintse meg a Windows-eszközillesztő-modell (WDDM) architektúráját.

Minden alkalmazásnak rendelkeznie kell legalább egy eszközzel; a legtöbb alkalmazás csak egyet hoz létre. A D3D11CreateDevice vagy a D3D11CreateDeviceAndSwapChain meghívásával hozzon létre egy eszközt a számítógépen telepített hardverillesztők egyikéhez, és adja meg az illesztőprogram típusát a D3D_DRIVER_TYPE jelzővel. Minden eszköz használhat egy vagy több eszközkörnyezetet a kívánt funkciótól függően. További információ: D3D11CreateDevice függvény.

Eszközkörnyezet

Az eszközkörnyezetek folyamat állapotának beállítására szolgálnak, és renderelési parancsokat hoznak létre a erőforrások használatával, egy eszköztulajdonában.

A Direct3D 11 kétféle eszközkörnyezetet valósít meg, az egyiket azonnali renderelésre, a másikat pedig halasztott renderelésre; mindkét környezet egy ID3D11DeviceContext felülettel van ábrázolva.

Az ID3D11DeviceContext interfészek különböző verziókkal rendelkeznek; Az ID3D11DeviceContext4 új metódusokat ad hozzá az ID3D11DeviceContext3 metódusokhoz.

ID3D11DeviceContext4 a Windows 10 Alkotók frissítésében, és a ID3D11DeviceContext felület legújabb verziója. A Windows 10 Creators Update-et és újabb verziókat célzó alkalmazásoknak ezt a felületet kell használniuk a korábbi verziók helyett. További információ: ID3D11DeviceContext4.

DX::D eviceResources

A DX::DeviceResources osztály a DeviceResources.cpp/.h fájlokban található, és az összes DirectX-eszköz erőforrást vezérli.

Puffer

A puffererőforrás elemekbe csoportosított, teljesen gépelt adatok gyűjteménye. A pufferekkel sokféle adatot tárolhat, beleértve a pozícióvektorokat, a normál vektorokat, a csúcspufferben lévő anyagminta-koordinátákat, az indexpufferben lévő indexeket vagy az eszközállapotokat. A pufferelemek tartalmazhatnak csomagolt adatértékeket (például R8G8B8A8 felületi értékeket), egy 8 bites egész számokat vagy négy 32 bites lebegőpontos értéket.

Háromféle puffer érhető el: csúcspuffer, indexpuffer és állandó puffer.

Csúcspuffer

A geometria meghatározásához használt csúcsadatokat tartalmazza. A csúcsadatok közé tartoznak a pozíciókoordináták, a színadatok, a textúrakoordináta-adatok, a normál adatok stb.

Indexpuffer

Egész szám eltolásokat tartalmaz a csúcspufferekbe, és a primitívek hatékonyabb megjelenítésére szolgálnak. Az indexpuffer 16 bites vagy 32 bites indexek szekvenciális készletét tartalmazza; az egyes indexek egy csúcspuffer csúcspontjainak azonosítására szolgálnak.

Állandó puffer vagy árnyaló-állandó puffer

Lehetővé teszi, hogy hatékonyan adjon árnyékolóadatokat a folyamatnak. Állandó puffereket használhat bemenetként azokhoz az árnyékolókhoz, amelyek minden primitívhez futnak, valamint a renderelési folyamat stream-kimeneti szakaszának eredményeit tárolhatja. Elméletileg az állandó puffer ugyanúgy néz ki, mint egy egyelemes csúcspuffer.

Pufferek tervezése és megvalósítása

Az adattípus alapján puffereket is tervezhet, például a mintajátékban például egy puffer jön létre statikus adatokhoz, egy másik a kereten állandó adatokhoz, egy pedig a primitív adatokhoz.

Az ID3D11Buffer interfész minden puffertípust beágyaz, és az ID3D11Device::CreateBuffer hívásával létrehozhat puffererőforrást. Azonban a puffert hozzá kell kötni a rendszerhez, mielőtt hozzá lehetne férni. A pufferek egyszerre több folyamatszakaszhoz is köthetők olvasáshoz. A puffer íráshoz egyetlen folyamatszakaszhoz is köthető; azonban ugyanazt a puffert nem lehet egyszerre beolvasni és írni.

A puffereket ilyen módon kötheti össze.

  • Az input-assembler fázisba az olyan ID3D11DeviceContext metódusok hívásával léphet, mint például a ID3D11DeviceContext::IASetVertexBuffers és a ID3D11DeviceContext::IASetIndexBuffer.
  • A stream-output szakaszhoz a ID3D11DeviceContext::SOSetTargetsmeghívásával.
  • A shader szakasz eléréséhez a shader metódusok meghívásával, például a ID3D11DeviceContext::VSSetConstantBufferssegítségével.

További információ: A Direct3D 11 puffereinek bemutatása.

DXGI

A Microsoft DirectX Graphics Infrastructure (DXGI) egy alrendszer, amely magában foglalja a Direct3D által igényelt alacsony szintű feladatok némelyikét. A DXGI többszálú alkalmazásokban való használatakor különös figyelmet kell fordítanunk arra, hogy ne forduljanak elő holtpontok. További információért lásd: Többszálúság és DXGI

Szolgáltatásszint

A funkciószint a Direct3D 11-ben bevezetett fogalom, amely a videokártyák sokféleségének kezelésére szolgál az új és a meglévő gépeken. A funkciószintek a grafikus feldolgozóegységek (GPU) jól definiált funkciói.

Minden videokártya a telepített GPU-któl függően bizonyos szintű DirectX-funkciókat valósít meg. A Microsoft Direct3D korábbi verzióiban megtudhatta, hogy a videokártya a Direct3D mely verzióját implementálta, majd ennek megfelelően programozhatta az alkalmazását.

A funkciószint esetén az eszköz létrehozásakor megpróbálhat létrehozni egy olyan eszközt, amely megfelel az igényelni kívánt funkciószintnek. Ha az eszköz létrehozása működik, ez a szolgáltatásszint létezik, ha nem, akkor a hardver nem támogatja ezt a szolgáltatási szintet. Megpróbálhatja újra létrehozni az eszközt egy alacsonyabb szolgáltatási szinten, vagy kiléphet az alkalmazásból. A 12_0 funkciószinthez például Direct3D 11.3 vagy Direct3D 12, valamint 5.1-es árnyékolómodell szükséges. További információ: Direct3D-funkciók szintjei: Áttekintés az egyes szolgáltatási szintekről.

A funkciószintek használatával létrehozhat egy alkalmazást a Direct3D 9, a Microsoft Direct3D 10 vagy a Direct3D 11 rendszerhez, majd futtathatja 9, 10 vagy 11 hardveren (néhány kivétellel). További információ: Direct3D szolgáltatásszintek.

Sztereó renderelés

A sztereó renderelés a mélység illúziójának javítására szolgál. Két képet használ, egyet a bal szemtől, a másikat a jobb szemtől, hogy megjelenítsen egy jelenetet a képernyőn.

Matematikailag egy sztereó vetítési mátrixot alkalmazunk, amely a normál monovetítési mátrixtól jobbra és balra kissé vízszintes eltolást jelent ennek eléréséhez.

Ebben a mintajátékban két renderelési menetet végeztünk a sztereó renderelés eléréséhez.

  • Kötés a jobb renderelési célhoz, alkalmazza a megfelelő vetítést, majd rajzolja meg a primitív objektumot.
  • Kössük a bal oldali renderelési célhoz, alkalmazzuk a bal oldali vetítést, majd rajzoljuk meg a primitív objektumot.

Kamera és koordináta-tér

A játéknak van olyan kódja, amely a helyén van ahhoz, hogy frissítse a világot a saját koordináta-rendszerében, amelyet néha világtérnek vagy jelenettérnek neveznek. Minden objektum, beleértve a kamerát is, ebben a térben van elhelyezve és orientált. További információ: Koordinátarendszerek.

A vertex-árnyékoló végzi a modellkoordináták eszközkoordinátákra való átalakításának oroszlánrészét az alábbi algoritmussal (ahol V egy vektor, M pedig egy mátrix).

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

  • M(model-to-world) a modellkoordinátákat világkoordinátákká alakító transzformációs mátrix, más néven világátalakulási mátrix. Ezt a primitívek biztosítják.
  • M(world-to-view) a világkoordinátákat nézetkoordinátákra átalakító mátrix, más néven Nézet átalakító mátrix.
    • Ezt a kamera nézetmátrixa biztosítja. A kamera pozíciója és a nézővektorok határozzák meg (a vektor, amely közvetlenül a kamerából a jelenetbe mutat, és a felfelé mutató vektor, amely erre merőlegesen felfelé irányul).
    • A mintajátékban m_viewMatrix a nézetátalakítási mátrix, amely a Camera::SetViewParams használatával van kiszámítva.
  • M(view-to-device) egy transzformációs mátrix, amely a nézetkoordinátákat eszközkoordinátákká alakítja, más néven a vetítési transzformációs mátrix.
    • Ezt a kamera kivetítése biztosítja. Információt nyújt arról, hogy a tér mekkora része látható az utolsó jelenetben. A nézetmező (FoV), az oldalarány és a vágósíkok határozzák meg a vetítési transzformációs mátrixot.
    • A mintajátékban a m_projectionMatrix határozza meg az átalakítást a vetítési koordinátákhoz, amelyet a Camera::SetProjParams használatával számítanak ki. Sztereó vetítéshez két vetítési mátrixot használunk—egyet minden szem nézetéhez.

A VertexShader.hlsl shader kódja be van töltve ezekkel a vektorokkal és mátrixokkal az állandó pufferekből, és minden csúcshoz végrehajtja ezt az átalakítást.

Koordináta-átalakítás

A Direct3D három átalakítással képpontkoordinátákra (képernyőtérre) módosítja a 3D modell koordinátáit. Ezek az átalakítások a világtranszformáció, a nézettranszformáció és a vetítési transzformáció. További információ: Átalakítás áttekintése.

Világtranszformációs mátrix

A világtranszformáció átalakítja a modelltér koordinátáit, ahol a csúcsok a modell helyi eredetéhez viszonyítva vannak meghatározva, a világ térbe, ahol az összes objektum közös eredetéhez képest vannak definiálva. Lényegében a világ-transzformáció elhelyez egy modellt a világba; innen a neve. További információért lásd: Világátalakítás.

Átalakító mátrix megtekintése

A nézetátalakítás elhelyezi a nézőt a világtérben, a csúcsokat pedig kameratérbe alakítja át. A kameratérben a kamera, vagyis a megfigyelő az origóban van, és a pozitív z-irányba néz. További információkért menjen ide: Átalakítás megtekintése.

Vetítési transzformációs mátrix

A vetítési transzformáció a megtekintési frustumot kuboid alakzattá alakítja. A megtekintési frustum egy 3D-s térfogat egy jelenetben, amely a nézetport kamerájához viszonyítva van elhelyezve. A nézetablak egy 2D téglalap, amelybe egy térhatású jelenetet vetítenek. További információ: Nézetablakok és kivágás

Mivel a megtekintési frustum közel vége kisebb, mint a távoli vége, ez a kamera közelében lévő objektumok kibontásának hatása; így történik a perspektíva alkalmazása a jelenetre. Így a játékoshoz közelebbi objektumok nagyobbnak tűnnek; a távolabbi objektumok kisebbnek tűnnek.

Matematikailag a kivetítési transzformáció egy olyan mátrix, amely jellemzően léptékezés és perspektív vetítés. Úgy működik, mint a kamera lencséje. További információ: kivetítési transzformáció.

Mintavevő állapota

A mintavevő állapota határozza meg, hogyan történik a mintázatadatok mintavételezése a textúracímzési módok, a szűrés és a részletességi szint használatával. A mintavételezés minden alkalommal történik, amikor egy textúra képpontja (vagy texelje) olvasódik be a textúrából.

A textúra texelek tömböt tartalmaz. Az egyes texelek pozícióját (u,v)u a szélesség és v a magasság jelöli, és a mintázat szélessége és magassága alapján 0 és 1 között van leképezve. Az eredményként kapott textúrakoordinátákat a texelek azonosítására használják, amikor mintát veszünk egy textúráról.

Ha a textúra koordinátái 0 vagy 1 felett vannak, a textúracím mód határozza meg, hogy a textúrakoordináta hogyan kezeli a texel helyét. A TextureAddressMode.Clamp használatakor például a 0-1 tartományon kívüli koordinátákat a rendszer legfeljebb 1 értékre, a mintavételezés előtt pedig minimum 0 értékre rögzíti.

Ha a textúra túl nagy vagy túl kicsi a sokszöghöz, akkor a textúra szűrve lesz, hogy illeszkedjen a térhez. A nagyítási szűrő nagyítja a textúrát, a minification szűrő csökkenti a textúrát, hogy elférjen egy kisebb területen. A textúra nagyítása megismétli a minta texelt egy vagy több cím esetében, amely elmosódottabb képet eredményez. A textúra minifikációja bonyolultabb, mert egynél több texel értéket kell egyetlen értékbe egyesíteni. Ez a textúraadatoktól függően aliasinget vagy recés éleket okozhat. A minifikáció legnépszerűbb módszere a mipmap használata. A mipmap egy többszintű textúra. Az egyes szintek mérete kettő hatványával kisebb, mint az előző szinté, egészen egy 1x1-es textúráig. A minification használata esetén a játék a rendereléskor szükséges mérethez legközelebb eső mipmap szintet választja ki.

A BasicLoader osztály

A BasicLoader egy egyszerű betöltőosztály, amely támogatja az árnyékolók, textúrák és hálók betöltését a lemezen lévő fájlokból. Szinkron és aszinkron metódusokat is biztosít. Ebben a mintajátékban a BasicLoader.h/.cpp fájlok a Segédprogramok mappában találhatók.

További információ: Alapszintű betöltő.