Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Anmärkning
Det här avsnittet är en del av Skapa ett enkelt UWP-spel (Universal Windows Platform) med DirectX självstudieserie. Ämnet på länken anger kontexten för serien.
I renderingsramverk har vi igått igenom hur vi tar sceninformationen och presenterar den på visningsskärmen. Nu ska vi ta ett steg tillbaka och lära oss hur du förbereder data för rendering.
Anmärkning
Om du inte har laddat ned den senaste spelkoden för det här exemplet går du till Direct3D-exempelspel. Det här exemplet är en del av en stor samling UWP-funktionsexempel. Anvisningar om hur du laddar ned exemplet finns i Exempelprogram för Windows-utveckling.
Mål
Snabb sammanfattning av målet. Det är för att förstå hur du konfigurerar ett grundläggande renderingsramverk för att visa grafikutdata för ett UWP DirectX-spel. Vi kan löst gruppera dem i dessa tre steg.
- Upprätta en anslutning till vårt grafikgränssnitt
- Förberedelse: Skapa de resurser vi behöver för att rita grafiken
- Visa grafiken: Rendera ramen
Rendering framework I: Intro till rendering förklarade hur grafik renderas, som täcker steg 1 och 3.
Den här artikeln beskriver hur du konfigurerar andra delar av det här ramverket och förbereder nödvändiga data innan renderingen kan ske, vilket är steg 2 i processen.
Utforma renderaren
Renderaren ansvarar för att skapa och underhålla alla D3D11- och D2D-objekt som används för att generera de visuella spelobjekten. GameRenderer-klassen är renderaren för det här exempelspelet och är utformad för att uppfylla spelets renderingsbehov.
Det här är några begrepp som du kan använda för att utforma renderaren för ditt spel:
- Eftersom Direct3D 11-API:er definieras som COM- API:er måste du ange ComPtr- referenser till de objekt som definieras av dessa API:er. Dessa objekt frigörs automatiskt när deras senaste referens hamnar utanför omfånget när appen avslutas. Mer information finns i ComPtr. Exempel på dessa objekt: konstanta buffertar, skuggningsobjekt – hörnskuggning, pixelskuggningoch skuggningsresursobjekt.
- Konstanta buffertar definieras i den här klassen för att lagra olika data som behövs för rendering.
- Använd flera konstanta buffertar med olika frekvenser för att minska mängden data som måste skickas till GPU:n per bildruta. Det här exemplet separerar konstanter i olika buffertar baserat på hur ofta de måste uppdateras. Det här är bästa praxis för Direct3D-programmering.
- I det här exempelspelet definieras 4 konstanta buffertar.
- m_constantBufferNeverChanges innehåller belysningsparametrarna. Den anges en gång i FinalizeCreateGameDeviceResources-metoden och ändras aldrig igen.
- m_constantBufferChangeOnResize innehåller projektionsmatrisen. Projektionsmatrisen är beroende av fönstrets storlek och proportioner. Den anges i CreateWindowSizeDependentResources och uppdateras sedan när resurser har lästs in i metoden FinalizeCreateGameDeviceResources. Om återgivning i 3D ändras den också två gånger per bildruta.
- m_constantBufferChangesEveryFrame innehåller visningsmatrisen. Den här matrisen är beroende av kamerans position och utseenderiktning (det normala för projektionen) och ändras en gång per bildruta i metoden Rendera. Detta diskuterades tidigare i Rendering framework I: Intro to rendering, under metoden GameRenderer::Render.
- m_constantBufferChangesEveryPrim innehåller modellmatrisen och materialegenskaperna för varje primitiv. Modellmatrisen omvandlar hörn från lokala koordinater till världskoordinater. Dessa konstanter är specifika för varje primitiv och uppdateras för varje draw call. Detta diskuterades tidigare i Rendering framework I: Intro to rendering, under Primitive rendering.
- Skuggningsresursobjekt som innehåller texturer för primitiverna definieras också i den här klassen.
- Vissa texturer är fördefinierade (DDS är ett filformat som kan användas för att lagra komprimerade och okomprimerade texturer. DDS-texturer används för väggar och golv i världen samt ammo sfärer.)
- I det här exempelspelet är skuggningsresursobjekt: m_sphereTexture, m_cylinderTexture, m_ceilingTexture, m_floorTexture, m_wallsTexture.
- Skuggningsobjekt definieras i den här klassen för att beräkna våra primitiver och texturer.
- I det här exempelspelet är skuggningsobjekten m_vertexShader, m_vertexShaderFlatoch m_pixelShader, m_pixelShaderFlat.
- Hörnskuggningen bearbetar primitiverna och den grundläggande belysningen, och pixelskuggaren (kallas ibland fragmentskuggning) bearbetar texturerna och eventuella effekter per bildpunkt.
- Det finns två versioner av dessa skuggor (vanliga och platta) för återgivning av olika primitiver. Anledningen till att vi har olika versioner är att de platta versionerna är mycket enklare och inte gör spektulära höjdpunkter eller några ljuseffekter per bildpunkt. Dessa används för väggarna och gör renderingen snabbare på enheter med lägre prestanda.
GameRenderer.h
Nu ska vi titta på koden i exempelspelets renderarklassobjekt.
// Class handling the rendering of the game
class GameRenderer : public std::enable_shared_from_this<GameRenderer>
{
public:
GameRenderer(std::shared_ptr<DX::DeviceResources> const& deviceResources);
void CreateDeviceDependentResources();
void CreateWindowSizeDependentResources();
void ReleaseDeviceDependentResources();
void Render();
// --- end of async related methods section
winrt::Windows::Foundation::IAsyncAction CreateGameDeviceResourcesAsync(_In_ std::shared_ptr<Simple3DGame> game);
void FinalizeCreateGameDeviceResources();
winrt::Windows::Foundation::IAsyncAction LoadLevelResourcesAsync();
void FinalizeLoadLevelResources();
Simple3DGameDX::IGameUIControl* GameUIControl() { return &m_gameInfoOverlay; };
DirectX::XMFLOAT2 GameInfoOverlayUpperLeft()
{
return DirectX::XMFLOAT2(m_gameInfoOverlayRect.left, m_gameInfoOverlayRect.top);
};
DirectX::XMFLOAT2 GameInfoOverlayLowerRight()
{
return DirectX::XMFLOAT2(m_gameInfoOverlayRect.right, m_gameInfoOverlayRect.bottom);
};
bool GameInfoOverlayVisible() { return m_gameInfoOverlay.Visible(); }
// --- end of rendering overlay section
...
private:
// Cached pointer to device resources.
std::shared_ptr<DX::DeviceResources> m_deviceResources;
...
// Shader resource objects
winrt::com_ptr<ID3D11ShaderResourceView> m_sphereTexture;
winrt::com_ptr<ID3D11ShaderResourceView> m_cylinderTexture;
winrt::com_ptr<ID3D11ShaderResourceView> m_ceilingTexture;
winrt::com_ptr<ID3D11ShaderResourceView> m_floorTexture;
winrt::com_ptr<ID3D11ShaderResourceView> m_wallsTexture;
// Constant buffers
winrt::com_ptr<ID3D11Buffer> m_constantBufferNeverChanges;
winrt::com_ptr<ID3D11Buffer> m_constantBufferChangeOnResize;
winrt::com_ptr<ID3D11Buffer> m_constantBufferChangesEveryFrame;
winrt::com_ptr<ID3D11Buffer> m_constantBufferChangesEveryPrim;
// Texture sampler
winrt::com_ptr<ID3D11SamplerState> m_samplerLinear;
// Shader objects: Vertex shaders and pixel shaders
winrt::com_ptr<ID3D11VertexShader> m_vertexShader;
winrt::com_ptr<ID3D11VertexShader> m_vertexShaderFlat;
winrt::com_ptr<ID3D11PixelShader> m_pixelShader;
winrt::com_ptr<ID3D11PixelShader> m_pixelShaderFlat;
winrt::com_ptr<ID3D11InputLayout> m_vertexLayout;
};
Konstruktor
Nu ska vi undersöka exempelspelets GameRenderer konstruktor och jämföra det med Sample3DSceneRenderer konstruktor som finns i DirectX 11-appmallen.
// Constructor method of the main rendering class object
GameRenderer::GameRenderer(std::shared_ptr<DX::DeviceResources> const& deviceResources) : ...
m_gameInfoOverlay(deviceResources),
m_gameHud(deviceResources, L"Windows platform samples", L"DirectX first-person game sample")
{
// m_gameInfoOverlay is a GameHud object to render text in the top left corner of the screen.
// m_gameHud is Game info rendered as an overlay on the top-right corner of the screen,
// for example hits, shots, and time.
CreateDeviceDependentResources();
CreateWindowSizeDependentResources();
}
Skapa och läsa in DirectX-grafiska resurser
I exempelspelet (och i Visual Studios DirectX 11-app (Universal Windows) mall) implementeras skapandet och inläsningen av spelresurser med hjälp av dessa två metoder som anropas från GameRenderer konstruktor:
Metoden CreateDeviceDependentResources
I DirectX 11-appmallen används den här metoden för att läsa in hörn och pixelskuggning asynkront, skapa skuggnings- och konstantbufferten, skapa ett nät med hörn som innehåller position och färginformation.
I exempelspelet delas dessa åtgärder av scenobjekten i stället mellan CreateGameDeviceResourcesAsync och FinalizeCreateGameDeviceResources metoder.
Vad går in i den här metoden för det här exempelspelet?
- Instansierade variabler (m_gameResourcesLoaded = false och m_levelResourcesLoaded = false) som anger om resurser har lästs in innan vi går vidare för att rendera, eftersom vi läser in dem asynkront.
- Eftersom HUD och överläggsrendering finns i separata klassobjekt anropar du GameHud::CreateDeviceDependentResources och GameInfoOverlay::CreateDeviceDependentResources metoder här.
Här är koden för GameRenderer::CreateDeviceDependentResources.
// This method is called in GameRenderer constructor when it's created in GameMain constructor.
void GameRenderer::CreateDeviceDependentResources()
{
// instantiate variables that indicate whether resources were loaded.
m_gameResourcesLoaded = false;
m_levelResourcesLoaded = false;
// game HUD and overlay are design as separate class objects.
m_gameHud.CreateDeviceDependentResources();
m_gameInfoOverlay.CreateDeviceDependentResources();
}
Nedan visas en lista över de metoder som används för att skapa och läsa in resurser.
- SkapaEnhetsberoendeResurser
- CreateGameDeviceResourcesAsync (tillagd)
- SlutförCreateGameDeviceResources (tillagt)
- SkapaResurserFörFönsterStorlek
Innan vi går in på andra metoder som används för att skapa och läsa in resurser ska vi först skapa återgivningen och se hur den passar in i spelloopen.
Skapa renderaren
GameRenderer skapas i GameMainkonstruktor. Den anropar också de två andra metoderna, CreateGameDeviceResourcesAsync och FinalizeCreateGameDeviceResources som läggs till för att skapa och läsa in resurser.
GameMain::GameMain(std::shared_ptr<DX::DeviceResources> const& deviceResources) : ...
{
m_deviceResources->RegisterDeviceNotify(this);
// Creation of GameRenderer
m_renderer = std::make_shared<GameRenderer>(m_deviceResources);
...
ConstructInBackground();
}
winrt::fire_and_forget GameMain::ConstructInBackground()
{
...
// Asynchronously initialize the game class and load the renderer device resources.
// By doing all this asynchronously, the game gets to its main loop more quickly
// and in parallel all the necessary resources are loaded on other threads.
m_game->Initialize(m_controller, m_renderer);
co_await m_renderer->CreateGameDeviceResourcesAsync(m_game);
// The finalize code needs to run in the same thread context
// as the m_renderer object was created because the D3D device context
// can ONLY be accessed on a single thread.
// co_await of an IAsyncAction resumes in the same thread context.
m_renderer->FinalizeCreateGameDeviceResources();
InitializeGameState();
...
}
CreateGameDeviceResourcesAsync-metod
CreateGameDeviceResourcesAsync anropas från GameMain-konstruktormetoden i create_task-loopen eftersom vi laddar in spelresurser asynkront.
CreateDeviceResourcesAsync är en metod som körs som en separat uppsättning asynkrona uppgifter för att läsa in spelresurserna. Eftersom den förväntas köras på en separat tråd har den bara åtkomst till Direct3D 11-enhetsmetoderna (de som definierats på ID3D11Enhet) och inte metoderna för enhetskontext (metoderna som definierats på ID3D11DeviceContext), så den utför inte någon rendering.
FinalizeCreateGameDeviceResources-metoden körs på huvudtråden och har åtkomst till Direct3D 11-enhetskontextmetoderna.
I princip:
- Använd endast ID3D11Enhet metoder i CreateGameDeviceResourcesAsync eftersom de är fritrådade, vilket innebär att de kan köras på valfri tråd. Det förväntas också att de inte körs på samma tråd som den där GameRenderer skapades.
- Använd inte metoder i ID3D11DeviceContext här eftersom de måste köras på en enda tråd och på samma tråd som GameRenderer.
- Använd den här metoden för att skapa konstanta buffertar.
- Använd den här metoden för att läsa in texturer (som .dds-filer) och shaderinformation (som .cso-filer) i shaders.
Den här metoden används för att:
- Skapa de 4 konstanta buffertar: m_constantBufferNeverChanges, m_constantBufferChangeOnResize, m_constantBufferChangesEveryFrame, m_constantBufferChangesEveryPrim
- Skapa ett sampler-state objekt som kapslar in samplingsinformation för en struktur
- Skapa en aktivitetsgrupp som innehåller alla asynkrona aktiviteter som skapats av metoden. Den väntar på att slutföra alla dessa asynkrona uppgifter och anropar sedan FinalizeCreateGameDeviceResources.
- Skapa en inläsare med hjälp av Basic Loader. Lägg till inläsarens asynkrona inläsningsåtgärder som aktiviteter i den aktivitetsgrupp som skapades tidigare.
- Metoder som BasicLoader::LoadShaderAsync och BasicLoader::LoadTextureAsync används för att läsa in:
- kompilerade skuggningsobjekt (VertextShader.cso, VertexShaderFlat.cso, PixelShader.cso och PixelShaderFlat.cso). Mer information finns i Olika skuggningsfilformat.
- spelspecifika texturer (Assets\seafloor.dds, metal_texture.dds, cellceiling.dds, cellfloor.dds, cellwall.dds).
IAsyncAction GameRenderer::CreateGameDeviceResourcesAsync(_In_ std::shared_ptr<Simple3DGame> game)
{
auto lifetime = shared_from_this();
// Create the device dependent game resources.
// Only the d3dDevice is used in this method. It is expected
// to not run on the same thread as the GameRenderer was created.
// Create methods on the d3dDevice are free-threaded and are safe while any methods
// in the d3dContext should only be used on a single thread and handled
// in the FinalizeCreateGameDeviceResources method.
m_game = game;
auto d3dDevice = m_deviceResources->GetD3DDevice();
// Define D3D11_BUFFER_DESC. See
// https://free.blessedness.top/windows/win32/api/d3d11/ns-d3d11-d3d11_buffer_desc
D3D11_BUFFER_DESC bd;
ZeroMemory(&bd, sizeof(bd));
// Create the constant buffers.
bd.Usage = D3D11_USAGE_DEFAULT;
...
// Create the constant buffers: m_constantBufferNeverChanges, m_constantBufferChangeOnResize,
// m_constantBufferChangesEveryFrame, m_constantBufferChangesEveryPrim
// CreateBuffer is used to create one of these buffers: vertex buffer, index buffer, or
// shader-constant buffer. For CreateBuffer API ref info, see
// https://free.blessedness.top/windows/win32/api/d3d11/nf-d3d11-id3d11device-createbuffer.
winrt::check_hresult(
d3dDevice->CreateBuffer(&bd, nullptr, m_constantBufferNeverChanges.put())
);
...
// Define D3D11_SAMPLER_DESC. For API ref, see
// https://free.blessedness.top/windows/win32/api/d3d11/ns-d3d11-d3d11_sampler_desc.
D3D11_SAMPLER_DESC sampDesc;
// ZeroMemory fills a block of memory with zeros. For API ref, see
// https://free.blessedness.top/previous-versions/windows/desktop/legacy/aa366920(v=vs.85).
ZeroMemory(&sampDesc, sizeof(sampDesc));
sampDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
...
// Create a sampler-state object that encapsulates sampling information for a texture.
// The sampler-state interface holds a description for sampler state that you can bind to any
// shader stage of the pipeline for reference by texture sample operations.
winrt::check_hresult(
d3dDevice->CreateSamplerState(&sampDesc, m_samplerLinear.put())
);
// Start the async tasks to load the shaders and textures.
// Load compiled shader objects (VertextShader.cso, VertexShaderFlat.cso, PixelShader.cso, and PixelShaderFlat.cso).
// The BasicLoader class is used to convert and load common graphics resources, such as meshes, textures,
// and various shader objects into the constant buffers. For more info, see
// https://free.blessedness.top/windows/uwp/gaming/complete-code-for-basicloader.
BasicLoader loader{ d3dDevice };
std::vector<IAsyncAction> tasks;
uint32_t numElements = ARRAYSIZE(PNTVertexLayout);
// Load shaders asynchronously with the shader and pixel data using the
// BasicLoader::LoadShaderAsync method. Push these method calls into a list of tasks.
tasks.push_back(loader.LoadShaderAsync(L"VertexShader.cso", PNTVertexLayout, numElements, m_vertexShader.put(), m_vertexLayout.put()));
tasks.push_back(loader.LoadShaderAsync(L"VertexShaderFlat.cso", nullptr, numElements, m_vertexShaderFlat.put(), nullptr));
tasks.push_back(loader.LoadShaderAsync(L"PixelShader.cso", m_pixelShader.put()));
tasks.push_back(loader.LoadShaderAsync(L"PixelShaderFlat.cso", m_pixelShaderFlat.put()));
// Make sure the previous versions if any of the textures are released.
m_sphereTexture = nullptr;
...
// Load Game specific textures (Assets\\seafloor.dds, metal_texture.dds, cellceiling.dds,
// cellfloor.dds, cellwall.dds).
// Push these method calls also into a list of tasks.
tasks.push_back(loader.LoadTextureAsync(L"Assets\\seafloor.dds", nullptr, m_sphereTexture.put()));
...
// Simulate loading additional resources by introducing a delay.
tasks.push_back([]() -> IAsyncAction { co_await winrt::resume_after(GameConstants::InitialLoadingDelay); }());
// Returns when all the async tasks for loading the shader and texture assets have completed.
for (auto&& task : tasks)
{
co_await task;
}
}
SlutförcreateGameDeviceResources-metoden
FinalizeCreateGameDeviceResources-metoden anropas efter att alla belastningsresurser som finns i metoden CreateGameDeviceResourcesAsync har slutförts.
- Initialisera constantBufferNeverChanges med ljusets positioner och färg. Läser in initiala data i konstantbuffertar genom att anropa metoden för enhetskontext ID3D11DeviceContext::UpdateSubresource.
- Eftersom asynkront inlästa resurser har slutfört inläsningen är det dags att associera dem med lämpliga spelobjekt.
- För varje spelobjekt skapar du nätet och materialet med hjälp av de texturer som har lästs in. Associera sedan meshen och materialet med spelobjektet.
- För målobjektet läses texturen, som består av koncentriska färgade ringar med ett numeriskt värde ovanpå, inte in från en texturfil. I stället genereras den procedurmässigt med hjälp av koden i TargetTexture.cpp. Klassen TargetTexture skapar de resurser som krävs för att dra strukturen till en resurs utanför skärmen vid initieringstillfället. Den resulterande strukturen associeras sedan med lämpliga målspelobjekt.
FinalizeCreateGameDeviceResources och CreateWindowSizeDependentResources delar liknande kodavsnitt för dessa:
- Använd SetProjParams för att säkerställa att kameran har rätt projektionsmatris. Mer information finns i Kamera och koordinatutrymme.
- Hantera skärmrotation genom att multiplicera 3D-rotationsmatrisen med kamerans projektionsmatris. Uppdatera sedan den konstanta bufferten ConstantBufferChangeOnResize med projektionsmatrisen som har skapats.
- Ange den m_gameResourcesLoadedbooleanska globala variabeln för att indikera att resurserna nu läses in i buffertarna, redo för nästa steg. Kom ihåg att vi först initierade den här variabeln som FALSE- i GameRendererkonstruktormetod, via GameRenderer::CreateDeviceDependentResources-metoden.
- När m_gameResourcesLoaded är TRUEkan scenobjekten renderas. Detta beskrevs i Rendering framework I: Intro till rendering artikeln, under GameRenderer::Render metoden.
// This method is called from the GameMain constructor.
// Make sure that 2D rendering is occurring on the same thread as the main rendering.
void GameRenderer::FinalizeCreateGameDeviceResources()
{
// All asynchronously loaded resources have completed loading.
// Now associate all the resources with the appropriate game objects.
// This method is expected to run in the same thread as the GameRenderer
// was created. All work will happen behind the "Loading ..." screen after the
// main loop has been entered.
// Initialize the Constant buffer with the light positions
// These are handled here to ensure that the d3dContext is only
// used in one thread.
auto d3dDevice = m_deviceResources->GetD3DDevice();
ConstantBufferNeverChanges constantBufferNeverChanges;
constantBufferNeverChanges.lightPosition[0] = XMFLOAT4(3.5f, 2.5f, 5.5f, 1.0f);
...
constantBufferNeverChanges.lightColor = XMFLOAT4(0.25f, 0.25f, 0.25f, 1.0f);
// CPU copies data from memory (constantBufferNeverChanges) to a subresource
// created in non-mappable memory (m_constantBufferNeverChanges) which was created in the earlier
// CreateGameDeviceResourcesAsync method. For UpdateSubresource API ref info,
// go to: https://msdn.microsoft.com/library/windows/desktop/ff476486.aspx
// To learn more about what a subresource is, go to:
// https://msdn.microsoft.com/library/windows/desktop/ff476901.aspx
m_deviceResources->GetD3DDeviceContext()->UpdateSubresource(
m_constantBufferNeverChanges.get(),
0,
nullptr,
&constantBufferNeverChanges,
0,
0
);
// For the objects that function as targets, they have two unique generated textures.
// One version is used to show that they have never been hit and the other is
// used to show that they have been hit.
// TargetTexture is a helper class to procedurally generate textures for game
// targets. The class creates the necessary resources to draw the texture into
// an off screen resource at initialization time.
TargetTexture textureGenerator(
d3dDevice,
m_deviceResources->GetD2DFactory(),
m_deviceResources->GetDWriteFactory(),
m_deviceResources->GetD2DDeviceContext()
);
// CylinderMesh is a class derived from MeshObject and creates a ID3D11Buffer of
// vertices and indices to represent a canonical cylinder (capped at
// both ends) that is positioned at the origin with a radius of 1.0,
// a height of 1.0 and with its axis in the +Z direction.
// In the game sample, there are various types of mesh types:
// CylinderMesh (vertical rods), SphereMesh (balls that the player shoots),
// FaceMesh (target objects), and WorldMesh (Floors and ceilings that define the enclosed area)
auto cylinderMesh = std::make_shared<CylinderMesh>(d3dDevice, (uint16_t)26);
...
// The Material class maintains the properties that represent how an object will
// look when it is rendered. This includes the color of the object, the
// texture used to render the object, and the vertex and pixel shader that
// should be used for rendering.
auto cylinderMaterial = std::make_shared<Material>(
XMFLOAT4(0.8f, 0.8f, 0.8f, .5f),
XMFLOAT4(0.8f, 0.8f, 0.8f, .5f),
XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f),
15.0f,
m_cylinderTexture.get(),
m_vertexShader.get(),
m_pixelShader.get()
);
...
// Attach the textures to the appropriate game objects.
// We'll loop through all the objects that need to be rendered.
for (auto&& object : m_game->RenderObjects())
{
if (object->TargetId() == GameConstants::WorldFloorId)
{
// Assign a normal material for the floor object.
// This normal material uses the floor texture (cellfloor.dds) that was loaded asynchronously from
// the Assets folder using BasicLoader::LoadTextureAsync method in the earlier
// CreateGameDeviceResourcesAsync loop
object->NormalMaterial(
std::make_shared<Material>(
XMFLOAT4(0.5f, 0.5f, 0.5f, 1.0f),
XMFLOAT4(0.8f, 0.8f, 0.8f, 1.0f),
XMFLOAT4(0.3f, 0.3f, 0.3f, 1.0f),
150.0f,
m_floorTexture.get(),
m_vertexShaderFlat.get(),
m_pixelShaderFlat.get()
)
);
// Creates a mesh object called WorldFloorMesh and assign it to the floor object.
object->Mesh(std::make_shared<WorldFloorMesh>(d3dDevice));
}
...
else if (auto cylinder = dynamic_cast<Cylinder*>(object.get()))
{
cylinder->Mesh(cylinderMesh);
cylinder->NormalMaterial(cylinderMaterial);
}
else if (auto target = dynamic_cast<Face*>(object.get()))
{
const int bufferLength = 16;
wchar_t str[bufferLength];
int len = swprintf_s(str, bufferLength, L"%d", target->TargetId());
auto string{ winrt::hstring(str, len) };
winrt::com_ptr<ID3D11ShaderResourceView> texture;
textureGenerator.CreateTextureResourceView(string, texture.put());
target->NormalMaterial(
std::make_shared<Material>(
XMFLOAT4(0.8f, 0.8f, 0.8f, 0.5f),
XMFLOAT4(0.8f, 0.8f, 0.8f, 0.5f),
XMFLOAT4(0.3f, 0.3f, 0.3f, 1.0f),
5.0f,
texture.get(),
m_vertexShader.get(),
m_pixelShader.get()
)
);
texture = nullptr;
textureGenerator.CreateHitTextureResourceView(string, texture.put());
target->HitMaterial(
std::make_shared<Material>(
XMFLOAT4(0.8f, 0.8f, 0.8f, 0.5f),
XMFLOAT4(0.8f, 0.8f, 0.8f, 0.5f),
XMFLOAT4(0.3f, 0.3f, 0.3f, 1.0f),
5.0f,
texture.get(),
m_vertexShader.get(),
m_pixelShader.get()
)
);
target->Mesh(targetMesh);
}
...
}
// The SetProjParams method calculates the projection matrix based on input params and
// ensures that the camera has been initialized with the right projection
// matrix.
// The camera is not created at the time the first window resize event occurs.
auto renderTargetSize = m_deviceResources->GetRenderTargetSize();
m_game->GameCamera().SetProjParams(
XM_PI / 2,
renderTargetSize.Width / renderTargetSize.Height,
0.01f,
100.0f
);
// Make sure that the correct projection matrix is set in the ConstantBufferChangeOnResize buffer.
// Get the 3D rotation transform matrix. We are handling screen rotations directly to eliminate an unaligned
// fullscreen copy. So it is necessary to post multiply the 3D rotation matrix to the camera's projection matrix
// to get the projection matrix that we need.
auto orientation = m_deviceResources->GetOrientationTransform3D();
ConstantBufferChangeOnResize changesOnResize;
// The matrices are transposed due to the shader code expecting the matrices in the opposite
// row/column order from the DirectX math library.
// XMStoreFloat4x4 takes a matrix and writes the components out to sixteen single-precision floating-point values at the given address.
// The most significant component of the first row vector is written to the first four bytes of the address,
// followed by the second most significant component of the first row, and so on. The second row is then written out in a
// like manner to memory beginning at byte 16, followed by the third row to memory beginning at byte 32, and finally
// the fourth row to memory beginning at byte 48. For more API ref info, go to:
// https://msdn.microsoft.com/library/windows/desktop/microsoft.directx_sdk.storing.xmstorefloat4x4.aspx
XMStoreFloat4x4(
&changesOnResize.projection,
XMMatrixMultiply(
XMMatrixTranspose(m_game->GameCamera().Projection()),
XMMatrixTranspose(XMLoadFloat4x4(&orientation))
)
);
// UpdateSubresource method instructs CPU to copy data from memory (changesOnResize) to a subresource
// created in non-mappable memory (m_constantBufferChangeOnResize ) which was created in the earlier
// CreateGameDeviceResourcesAsync method.
m_deviceResources->GetD3DDeviceContext()->UpdateSubresource(
m_constantBufferChangeOnResize.get(),
0,
nullptr,
&changesOnResize,
0,
0
);
// Finally we set the m_gameResourcesLoaded as TRUE, so we can start rendering.
m_gameResourcesLoaded = true;
}
Metoden CreateWindowSizeDependentResource
CreateWindowSizeDependentResources-metoder anropas varje gång fönstrets storlek, orientering, stereoaktiverad rendering eller upplösning ändras. I exempelspelet uppdateras projektionsmatrisen i ConstantBufferChangeOnResize.
Resurser för fönsterstorlek uppdateras på det här sättet:
- Appramverket hämtar en av flera möjliga händelser som indikerar en ändring i fönstertillståndet.
- Din huvudspelsloop informeras sedan om händelsen och anropar metoden CreateWindowSizeDependentResources i huvudklassens (GameMain) instans, som sedan anropar CreateWindowSizeDependentResources-implementeringen i spelrenderarens (GameRenderer) klass.
- Det primära jobbet för den här metoden är att se till att de visuella objekten inte blir förvirrade eller ogiltiga på grund av en ändring i fönsteregenskaperna.
För det här exempelspelet är ett antal metodanrop samma som metoden FinalizeCreateGameDeviceResources. För genomgång av kod går du till föregående avsnitt.
Storleksjusteringar för spelets HUD och överläggsfönster är del av Lägg till ett användargränssnitt.
// Initializes view parameters when the window size changes.
void GameRenderer::CreateWindowSizeDependentResources()
{
// Game HUD and overlay window size rendering adjustments are done here
// but they'll be covered in the UI section instead.
m_gameHud.CreateWindowSizeDependentResources();
...
auto d3dContext = m_deviceResources->GetD3DDeviceContext();
// In Sample3DSceneRenderer::CreateWindowSizeDependentResources, we had:
// Size outputSize = m_deviceResources->GetOutputSize();
auto renderTargetSize = m_deviceResources->GetRenderTargetSize();
...
m_gameInfoOverlay.CreateWindowSizeDependentResources(m_gameInfoOverlaySize);
if (m_game != nullptr)
{
// Similar operations as the last section of FinalizeCreateGameDeviceResources method
m_game->GameCamera().SetProjParams(
XM_PI / 2, renderTargetSize.Width / renderTargetSize.Height,
0.01f,
100.0f
);
XMFLOAT4X4 orientation = m_deviceResources->GetOrientationTransform3D();
ConstantBufferChangeOnResize changesOnResize;
XMStoreFloat4x4(
&changesOnResize.projection,
XMMatrixMultiply(
XMMatrixTranspose(m_game->GameCamera().Projection()),
XMMatrixTranspose(XMLoadFloat4x4(&orientation))
)
);
d3dContext->UpdateSubresource(
m_constantBufferChangeOnResize.get(),
0,
nullptr,
&changesOnResize,
0,
0
);
}
}
Nästa steg
Det här är den grundläggande processen för att implementera grafikrenderingsramverket för ett spel. Ju större ditt spel är, desto fler abstraktioner måste du införa för att hantera hierarkier med objekttyper och animeringsbeteenden. Du måste implementera mer komplexa metoder för att läsa in och hantera tillgångar som nät och texturer. Nästa steg, låt oss lära oss hur vi lägger till ett användargränssnitt.