Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
Opmerking
Dit onderwerp maakt deel uit van de tutorialreeks Een eenvoudig Universal Windows Platform (UWP) spel maken met DirectX. In het onderwerp op die koppeling wordt de context voor de reeks ingesteld.
In het Rendering-framework I hebben we besproken hoe we de scènegegevens nemen en deze presenteren aan het scherm. We nemen nu een stap terug en leren hoe u de gegevens voorbereidt voor rendering.
Opmerking
Als u de nieuwste gamecode voor dit voorbeeld nog niet hebt gedownload, gaat u naar Direct3D-voorbeeldspel. Dit voorbeeld maakt deel uit van een grote verzameling UWP-functievoorbeelden. Zie Voorbeeldtoepassingen voor Windows-ontwikkelingvoor instructies over het downloaden van het voorbeeld.
Doelstelling
Kort overzicht van de doelstelling. Het is om te begrijpen hoe u een eenvoudig renderingframework instelt om de grafische uitvoer voor een UWP DirectX-game weer te geven. We kunnen ze losjes groeperen in deze drie stappen.
- Verbinding maken met onze grafische interface
- Voorbereiding: Maak de resources die we nodig hebben om de afbeeldingen te tekenen
- De afbeeldingen weergeven: Het frame weergeven
Rendering framework I: Introductie tot renderen legde uit hoe grafische weergaves worden gerealiseerd, met nadruk op stappen 1 en 3.
In dit artikel wordt uitgelegd hoe u andere onderdelen van dit framework instelt en de vereiste gegevens voorbereidt voordat het weergeven kan plaatsvinden. Dit is stap 2 van het proces.
De renderer ontwerpen
De renderer is verantwoordelijk voor het maken en onderhouden van alle D3D11- en D2D-objecten die worden gebruikt om de gamevisuals te genereren. De GameRenderer-klasse is de renderer voor dit voorbeeldspel en is ontworpen om te voldoen aan de renderingbehoeften van het spel.
Dit zijn enkele concepten die u kunt gebruiken om de renderer voor uw game te ontwerpen:
- Omdat Direct3D 11-API's zijn gedefinieerd als COM-API's, moet u ComPtr-verwijzingen opgeven naar de objecten die door deze API's zijn gedefinieerd. Deze objecten worden automatisch vrijgemaakt wanneer hun laatste verwijzing buiten het bereik valt wanneer de app wordt beëindigd. Zie ComPtr voor meer informatie. Voorbeeld van deze objecten: constante buffers, shader-objecten - hoekpunt-shader, pixel-shader en shader-resourceobjecten.
- Constante buffers worden in deze klasse gedefinieerd voor het opslaan van verschillende gegevens die nodig zijn voor rendering.
- Gebruik meerdere constante buffers met verschillende frequenties om de hoeveelheid gegevens te verminderen die per frame naar de GPU moet worden verzonden. Dit voorbeeld scheidt constanten in verschillende buffers op basis van de frequentie die ze moeten worden bijgewerkt. Dit is een best practice voor Direct3D-programmering.
- In dit voorbeeldspel worden 4 constante buffers gedefinieerd.
- m_constantBufferNeverChanges bevat de lichtparameters. Deze wordt één keer ingesteld in de methode FinalizeCreateGameDeviceResources en wordt nooit meer gewijzigd.
- m_constantBufferChangeOnResize bevat de projectiematrix. De projectiematrix is afhankelijk van de grootte en hoogte-breedteverhouding van het venster. Deze is ingesteld in CreateWindowSizeDependentResources en wordt vervolgens bijgewerkt nadat de resources zijn geladen in de methode FinalizeCreateGameDeviceResources . Als de weergave in 3D wordt weergegeven, wordt deze ook tweemaal per frame gewijzigd.
- m_constantBufferChangesEveryFrame bevat de weergavematrix. Deze matrix is afhankelijk van de camerapositie en de richting van het uiterlijk (normaal voor de projectie) en verandert één keer per frame in de rendermethode . Dit is eerder besproken in renderingframework I: Inleiding tot rendering, onder de methode GameRenderer::Render.
- m_constantBufferChangesEveryPrim bevat de modelmatrix en materiaaleigenschappen van elke primitieve. De modelmatrix transformeert hoekpunten van lokale coördinaten in wereldcoördinaten. Deze constanten zijn specifiek voor elke primitieve en worden bijgewerkt voor elke tekenoproep. Dit is eerder besproken in Rendering-framework I: Inleiding tot het weergeven van, onder de Primitieve rendering.
- Shader-resourceobjecten die patronen voor de primitieven bevatten, worden ook in deze klasse gedefinieerd.
- Sommige patronen zijn vooraf gedefinieerd (DDS is een bestandsindeling die kan worden gebruikt om gecomprimeerde en niet-gecomprimeerde patronen op te slaan. DDS-patronen worden gebruikt voor de muren en vloer van de wereld, evenals de ammobolen.)
- In dit voorbeeldspel zijn shader resource-objecten: m_sphereTexture, m_cylinderTexture, m_ceilingTexture, m_floorTexture, m_wallsTexture.
- Arceringsobjecten worden in deze klasse gedefinieerd om onze primitieven en patronen te berekenen.
- In dit voorbeeldspel zijn de arceringsobjecten m_vertexShader, m_vertexShaderFlat, en m_pixelShader, m_pixelShaderFlat.
- De hoekpunt-shader verwerkt de primitieven en de basisverlichting, en de pixel-shader (ook wel een fragment-shader genoemd) verwerkt de patronen en eventuele effecten per pixel.
- Er zijn twee versies van deze shaders (normaal en plat) voor het weergeven van verschillende primitieven. De reden dat we verschillende versies hebben, is dat de platte versies veel eenvoudiger zijn en geen speculaire hoogtepunten of eventuele lichteffecten per pixel uitvoeren. Deze worden gebruikt voor de wanden en zorgen voor snellere rendering op apparaten met een lager vermogen.
GameRenderer.h
Laten we nu kijken naar de code in het rendererklasseobject van het voorbeeldspel.
// 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;
};
Constructeur
Laten we vervolgens de GameRenderer-constructor van het voorbeeldspel bekijken en deze vergelijken met de Sample3DSceneRenderer-constructor die is opgegeven in de DirectX 11-app-sjabloon.
// 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();
}
DirectX-grafische hulpmiddelen maken en laden
In het voorbeeldspel (en in de DirectX 11 App (Universal Windows) -sjabloon van Visual Studio wordt het maken en laden van gameresources geïmplementeerd met behulp van deze twee methoden die worden aangeroepen vanuit de GameRenderer-constructor :
Methode CreateDeviceDependentResources
In de DirectX 11-app-sjabloon wordt deze methode gebruikt om asynchroon hoekpunt en pixel-shader te laden, de shader en constante buffer te maken, een mesh te maken met hoekpunten die positie- en kleurgegevens bevatten.
In het voorbeeldspel worden deze bewerkingen van de scèneobjecten verdeeld over de methoden CreateGameDeviceResourcesAsync en FinalizeCreateGameDeviceResources .
Wat gaat er in deze methode voor dit voorbeeldspel?
- Geïnstantieerde variabelen (m_gameResourcesLoaded = false en m_levelResourcesLoaded = false) die aangeven of resources zijn geladen voordat ze worden weergegeven, omdat we ze asynchroon laden.
- Omdat HUD- en overlay-rendering zich in afzonderlijke klasseobjecten bevinden, roept u Hier de methoden GameHud::CreateDeviceDependentResources en GameInfoOverlay::CreateDeviceDependentResources aan.
Dit is de code voor 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();
}
Hieronder ziet u een lijst met de methoden die worden gebruikt om resources te maken en te laden.
- CreateDeviceDependentResources
- CreateGameDeviceResourcesAsync (toegevoegd)
- FinalizeCreateGameDeviceResources (toegevoegd)
- CreateWindowSizeAfhankelijkeMiddelen
Voordat u ingaat op de andere methoden die worden gebruikt om resources te maken en te laden, gaan we eerst de renderer maken en zien hoe deze in de gamelus past.
De renderer maken
De GameRenderer- wordt in de constructor van GameMain-aangemaakt. Ook worden de twee andere methoden CreateGameDeviceResourcesAsync en FinalizeCreateGameDeviceResources aangeroepen die worden toegevoegd om resources te maken en te laden.
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();
...
}
Methode CreateGameDeviceResourcesAsync
CreateGameDeviceResourcesAsync- wordt aangeroepen vanuit de GameMain constructormethode binnen de create_task lus, omdat we gamebronnen asynchroon aan het laden zijn.
CreateDeviceResourcesAsync is een methode die wordt uitgevoerd als een afzonderlijke set asynchrone taken om de gameresources te laden. Omdat het naar verwachting wordt uitgevoerd op een afzonderlijke thread, heeft het alleen toegang tot de Direct3D 11-apparaatmethoden (die zijn gedefinieerd op ID3D11Device) en niet de apparaatcontextmethoden (de methoden die zijn gedefinieerd in ID3D11DeviceContext), zodat er geen rendering wordt uitgevoerd.
De methode FinalizeCreateGameDeviceResources wordt uitgevoerd op de hoofdthread en heeft wel toegang tot de Direct3D 11-apparaatcontextmethoden.
In principe:
- Gebruik alleen ID3D11Device-methoden in CreateGameDeviceResourcesAsync omdat ze gratis threads bevatten, wat betekent dat ze op elke thread kunnen worden uitgevoerd. Er wordt ook van uitgegaan dat ze niet op dezelfde thread draaien als die waarop de GameRenderer is gemaakt.
- Gebruik hier geen methoden in ID3D11DeviceContext omdat ze moeten worden uitgevoerd op één thread en op dezelfde thread als GameRenderer.
- Gebruik deze methode om constante buffers te maken.
- Gebruik deze methode om patronen (zoals de .dds bestanden) en arceringsgegevens (zoals de CSO-bestanden) in de shaders te laden.
Deze methode wordt gebruikt voor het volgende:
- Maak de 4 constante buffers: m_constantBufferNeverChanges, m_constantBufferChangeOnResize, m_constantBufferChangesEveryFrame, m_constantBufferChangesEveryPrim
- Maak een sampler-state object aan dat de samplinginformatie voor een textuur inkapselt
- Maak een taakgroep die alle asynchrone taken bevat die door de methode zijn gemaakt. Het wacht totdat al deze asynchrone taken zijn voltooid en roept vervolgens FinalizeCreateGameDeviceResources aan.
- Maak een laadprogramma met behulp van Basic Loader. Voeg de asynchrone laadbewerkingen van het laadprogramma toe als taken in de eerder gemaakte taakgroep.
- Methoden zoals BasicLoader::LoadShaderAsync en BasicLoader::LoadTextureAsync worden gebruikt om te laden:
- gecompileerde shader-objecten (VertextShader.cso, VertexShaderFlat.cso, PixelShader.cso en PixelShaderFlat.cso). Ga naar Verschillende shader-bestandsindelingen voor meer informatie.
- spelspecifieke patronen (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://learn.microsoft.com/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://learn.microsoft.com/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://learn.microsoft.com/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://learn.microsoft.com/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://learn.microsoft.com/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;
}
}
Methode FinalizeCreateGameDeviceResources
De methode FinalizeCreateGameDeviceResources wordt aangeroepen nadat alle taken voor het laden van resources in de methode CreateGameDeviceResourcesAsync zijn voltooid.
- Initialiseer constantBufferNeverChanges met de lichtposities en kleuren. Laadt de initiële gegevens in de constante buffers met een aanroep van de apparaatcontextmethode naar ID3D11DeviceContext::UpdateSubresource.
- Nu de asynchroon geladen resources zijn ingeladen, is het tijd om ze aan de juiste game-objecten te koppelen.
- Maak voor elk spelobject de mesh en het materiaal met behulp van de patronen die zijn geladen. Koppel vervolgens het mesh en materiaal aan het spelobject.
- Voor het doelspelobject wordt het patroon dat bestaat uit concentrische gekleurde ringen, met een numerieke waarde aan de bovenkant, niet geladen uit een patroonbestand. In plaats daarvan wordt deze procedureel gegenereerd met behulp van de code in TargetTexture.cpp. De TargetTexture-klasse maakt de benodigde resources aan om de textuur bij de initialisatie te tekenen in een resource buiten het scherm. Het resulterende patroon wordt vervolgens gekoppeld aan de juiste doelspelobjecten.
FinalizeCreateGameDeviceResources en CreateWindowSizeDependentResources bevatten vergelijkbare codefragmenten voor de volgende elementen:
- Gebruik SetProjParams om ervoor te zorgen dat de camera de juiste projectiematrix heeft. Ga voor meer informatie naar Camera en coördinaatruimte.
- Greep schermrotatie door de 3D-draaiingsmatrix te vermenigvuldigen met de projectiematrix van de camera. Werk vervolgens de constante buffer ConstantBufferChangeOnResize bij met de resulterende projectiematrix.
- Stel de m_gameResourcesLoadedbooleaanse globale variabele in om aan te geven dat de resources nu in de buffers worden geladen, klaar voor de volgende stap. Zoals u weet, hebben we deze variabele eerst geïnitialiseerd als FALSE in de constructormethode van GameRenderer, via de methode GameRenderer::CreateDeviceDependentResources .
- Wanneer deze m_gameResourcesLoaded is TRUE, kan het weergeven van de scèneobjecten plaatsvinden. Dit is behandeld in het Rendering-framework I: Inleiding tot het rendering-artikel onder de methode GameRenderer::Render.
// 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;
}
Methode CreateWindowSizeDependentResource
CreateWindowSizeDependentResources-methoden worden elke keer aangeroepen wanneer de venstergrootte, oriëntatie, stereoweergave of resolutie wijzigt. In het voorbeeldspel wordt de projectiematrix bijgewerkt in ConstantBufferChangeOnResize.
Venstergrootteresources worden op deze manier bijgewerkt:
- Het App-framework krijgt een van de mogelijke gebeurtenissen die een wijziging in de vensterstatus aangeven.
- Uw hoofdspellus wordt vervolgens geïnformeerd over de gebeurtenis en roept de methode CreateWindowSizeDependentResources aan op de hoofdklasse-instantie (GameMain), die vervolgens de CreateWindowSizeDependentResources implementatie in de game renderer (GameRenderer) aanroept.
- De primaire taak van deze methode is ervoor te zorgen dat de visuals niet in de war raken of ongeldig zijn vanwege een wijziging in venstereigenschappen.
Voor dit voorbeeldspel zijn een aantal methodeaanroepen hetzelfde als de methode FinalizeCreateGameDeviceResources . Ga naar de vorige sectie voor stapsgewijze instructies voor code.
De weergaveaanpassingen van de game-HUD en overlayvenstergrootte worden behandeld onder Een gebruikersinterface toevoegen.
// 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
);
}
}
Volgende stappen
Dit is het basisproces voor het implementeren van het grafische renderingframework van een game. Hoe groter uw spel, hoe meer abstracties u zou moeten plaatsen om hiërarchieën van objecttypen en animatiegedrag af te handelen. U moet complexere methoden implementeren voor het laden en beheren van assets, zoals meshes en texturen. Laten we nu leren hoe u een gebruikersinterface toevoegt.