Laden von Ressourcen im DirectX-SpielLoad resources in your DirectX game

In den meisten Spielen werden Ressourcen und Objekte (wie Shader, Texturen, vordefinierte Gitter oder andere Grafikdaten) an bestimmten Stellen aus dem lokalen Speicher oder über einen anderen Datenstrom geladen.Most games, at some point, load resources and assets (such as shaders, textures, predefined meshes or other graphics data) from local storage or some other data stream. Hier finden Sie eine Übersicht über die Aspekte, die Sie beim Laden dieser Dateien für die Verwendung in Ihrem DirectX C/C++ universelle Windows-Plattform-Spiel (UWP) beachten müssen.Here, we walk you through a high-level view of what you must consider when loading these files to use in your DirectX C/C++ Universal Windows Platform (UWP) game.

Es kann beispielsweise sein, dass die Gitter für polygonale Objekte im Spiel mit einem anderen Tool erstellt und in einem bestimmten Format exportiert wurden.For example, the meshes for polygonal objects in your game might have been created with another tool, and exported to a specific format. Die kann auch besonders für Texturen der Fall sein: Während eine flache, unkomprimierte Bitmapgrafik in der Regel von den meisten Tools geschrieben und von den meisten Grafik-APIs verarbeitet werden kann, ist dieses Format zur Verwendung im Spiel möglicherweise sehr ineffizient.The same is true for textures, and more so: while a flat, uncompressed bitmap can be commonly written by most tools and understood by most graphics APIs, it can be extremely inefficient for use in your game. Sie werden durch die grundlegenden Schritte des Ladens von drei unterschiedlichen Arten von Grafikressourcen geführt, die in Verbindung mit Direct3D verwendet werden: Gitter (Modelle), Texturen (Bitmaps) und kompilierte Shaderobjekte.Here, we guide you through the basic steps for loading three different types of graphic resources for use with Direct3D: meshes (models), textures (bitmaps), and compiled shader objects.

Was Sie wissen müssenWhat you need to know

TechnologienTechnologies

  • Parallel Patterns Library (PPL)Parallel Patterns Library (ppltasks.h)

VoraussetzungenPrerequisites

  • Informationen zur grundlegenden Windows-RuntimeUnderstand the basic Windows Runtime
  • Grundlegendes zu asynchronen AufgabenUnderstand asynchronous tasks
  • Grundbegriffe der Programmierung von 3D-GrafikenUnderstand the basic concepts of 3-D graphics programming.

Dieses Beispiel enthält auch drei Codedateien zum Laden und Verwalten von Ressourcen.This sample also includes three code files for resource loading and management. Sie werden in diesem Thema häufiger auf die in diesen Dateien definierten Codeobjekte treffen.You'll encounter the code objects defined in these files throughout this topic.

  • BasicLoader.h/.cpp BasicLoader.h/.cpp
  • BasicReaderWriter.h/.cppBasicReaderWriter.h/.cpp
  • DDSTextureLoader.h/.cppDDSTextureLoader.h/.cpp

Sie können über die folgenden Links auf den vollständigen Code für diese Beispiele zugreifen.The complete code for these samples can be found in the following links.

ThemaTopic BESCHREIBUNGDescription

Vollständiger Code für BasicLoaderComplete code for BasicLoader

Vollständiger Code für eine Klasse und Methoden, die gitterförmige Grafikobjekte konvertieren und in den Speicher laden.Complete code for a class and methods that convert and load graphics mesh objects into memory.

Vollständiger Code für BasicReaderWriterComplete code for BasicReaderWriter

Vollständiger Code für eine Klasse und Methoden zum allgemeinen Lesen und Schreiben von Binärdatendateien.Complete code for a class and methods for reading and writing binary data files in general. Wird von der BasicLoader-Klasse verwendet.Used by the BasicLoader class.

Vollständiger Code für DDSTextureLoaderComplete code for DDSTextureLoader

Vollständiger Code für eine Klasse und Methode, die eine DDS-Textur aus dem Speicher lädt.Complete code for a class and method that loads a DDS texture from memory.

 

InstructionsInstructions

Asynchrones LadenAsynchronous loading

Das asynchrone Laden wird mithilfe der task-Vorlage der Parallel Patterns Library (PPL) durchgeführt.Asynchronous loading is handled using the task template from the Parallel Patterns Library (PPL). Eine task enthält einen Methodenaufruf gefolgt von einer Lambda-Funktion, mit der die Ergebnisse des asynchronen Aufrufs nach dessen Abschluss verarbeitet werden. Dabei wird normalerweise das folgende Format verwendet:A task contains a method call followed by a lambda that processes the results of the async call after it completes, and usually follows the format of:

task<generic return type>(async code to execute).then((parameters for lambda){ lambda code contents });.task<generic return type>(async code to execute).then((parameters for lambda){ lambda code contents });.

Aufgaben können mithilfe der .then()-Syntax verkettet werden. Wenn ein Vorgang abgeschlossen ist, kann ein anderer asynchroner Vorgang ausgeführt werden, der von den Ergebnissen des vorherigen Vorgangs abhängt.Tasks can be chained together using the .then() syntax, so that when one operation completes, another async operation that depends on the results of the prior operation can be run. Auf diese Weise können Sie komplexe Objekte in separaten Threads für den Spieler nahezu unbemerkt laden, konvertieren und verwalten.In this way, you can load, convert, and manage complex assets on separate threads in a way that appears almost invisible to the player.

Weitere Informationen finden Sie unter Asynchrone Programmierung in C++.For more details, read Asynchronous programming in C++.

Als Nächstes werfen wir einen Blick auf die Grundstruktur zum Deklarieren und Erstellen einer Methode für das asynchrone Laden von Dateien: ReadDataAsync.Now, let's look at the basic structure for declaring and creating an async file loading method, ReadDataAsync.

#include <ppltasks.h>

// ...
concurrency::task<Platform::Array<byte>^> ReadDataAsync(
        _In_ Platform::String^ filename);

// ...

using concurrency;

task<Platform::Array<byte>^> BasicReaderWriter::ReadDataAsync(
    _In_ Platform::String^ filename
    )
{
    return task<StorageFile^>(m_location->GetFileAsync(filename)).then([=](StorageFile^ file)
    {
        return FileIO::ReadBufferAsync(file);
    }).then([=](IBuffer^ buffer)
    {
        auto fileData = ref new Platform::Array<byte>(buffer->Length);
        DataReader::FromBuffer(buffer)->ReadBytes(fileData);
        return fileData;
    });
}

Wenn in Ihrem Code basierend auf diesem Code die oben definierte ReadDataAsync-Methode aufgerufen wird, wird eine Aufgabe zum Auslesen eines Puffers aus dem Dateisystem erstellt.In this code, when your code calls the ReadDataAsync method defined above, a task is created to read a buffer from the file system. Nach Abschluss dieses Vorgangs übernimmt eine verkettete Aufgabe den Puffer und führt für die Bytes dieses Puffers einen Datenstrom in ein Array durch, indem der statische DataReader-Typ verwendet wird.Once it completes, a chained task takes the buffer and streams the bytes from that buffer into an array using the static DataReader type.

m_basicReaderWriter = ref new BasicReaderWriter();

// ...
return m_basicReaderWriter->ReadDataAsync(filename).then([=](const Platform::Array<byte>^ bytecode)
    {
      // Perform some operation with the data when the async load completes.          
    });

Dies ist der Aufruf an ReadDataAsync.Here's the call you make to ReadDataAsync. Nach Abschluss des Vorgangs empfängt der Code ein Array mit Bytes, die aus der bereitgestellten Datei ausgelesen wurden.When it completes, your code receives an array of bytes read from the provided file. Da ReadDataAsync selbst als Aufgabe definiert ist, können Sie mithilfe einer Lambda-Funktion einen bestimmten Vorgang durchführen, wenn das Bytearray zurückgegeben wird. Dies kann beispielsweise die Übergabe dieser Bytedaten an eine geeignete DirectX-Funktion sein.Since ReadDataAsync itself is defined as a task, you can use a lambda to perform a specific operation when the byte array is returned, such as passing that byte data to a DirectX function that can use it.

Wenn Ihr Spiel nicht zu komplex aufgebaut ist, können Sie die Ressourcen mit einer Methode dieser Art laden, wenn Benutzer das Spiel starten.If your game is sufficiently simple, load your resources with a method like this when the user starts the game. Sie können diesen Schritt ausführen, bevor Sie an einem bestimmten Punkt der Aufrufabfolge Ihrer IFrameworkView::Run-Implementierung die Hauptspielschleife starten.You can do this before you start the main game loop from some point in the call sequence of your IFrameworkView::Run implementation. Sie rufen auch hier wieder die Methoden zum Laden der Ressourcen asynchron auf, damit das Spiel schneller gestartet werden kann und Spieler nicht auf den Abschluss des Ladevorgangs warten müssen, bevor die ersten Interaktionen möglich sind.Again, you call your resource loading methods asynchronously so the game can start quicker and so the player doesn't have to wait until the loading completes before engaging in early interactions.

Das eigentliche Spiel sollten jedoch erst richtig gestartet werden, nachdem das asynchrone Laden vollständig abgeschlossen ist!However, you don't want to start the game proper until all of the async loading has completed! Erstellen Sie eine Methode, mit der angezeigt wird, wann der Ladevorgang abgeschlossen ist, z. B. ein bestimmtes Feld. Verwenden Sie dann die Lambda-Funktionen für die Lademethoden, um den Abschluss des Vorgangs anzuzeigen.Create some method for signaling when loading is complete, such as a specific field, and use the lambdas on your loading method(s) to set that signal when finished. Überprüfen Sie die Variable, bevor Sie Komponenten starten, in denen die geladenen Ressourcen verwendet werden.Check the variable before starting any components that use those loaded resources.

In diesem Beispiel werden die in „BasicLoader.cpp“ definierten asynchronen Methoden verwendet, um Shader, ein Gitter und eine Textur zu laden, wenn das Spiel gestartet wird.Here's an example using the async methods defined in BasicLoader.cpp to load shaders, a mesh, and a texture when the game starts up. Beachten Sie, dass ein bestimmtes Feld für das Spielobjekt " m _ loadingcomplete" festgelegt wird, wenn alle Lade Methoden abgeschlossen sind.Notice that it sets a specific field on the game object, m_loadingComplete, when all of the loading methods finish.

void ResourceLoading::CreateDeviceResources()
{
    // DirectXBase is a common sample class that implements a basic view provider. 
    
    DirectXBase::CreateDeviceResources(); 

    // ...

    // This flag will keep track of whether or not all application
    // resources have been loaded.  Until all resources are loaded,
    // only the sample overlay will be drawn on the screen.
    m_loadingComplete = false;

    // Create a BasicLoader, and use it to asynchronously load all
    // application resources.  When an output value becomes non-null,
    // this indicates that the asynchronous operation has completed.
    BasicLoader^ loader = ref new BasicLoader(m_d3dDevice.Get());

    auto loadVertexShaderTask = loader->LoadShaderAsync(
        "SimpleVertexShader.cso",
        nullptr,
        0,
        &m_vertexShader,
        &m_inputLayout
        );

    auto loadPixelShaderTask = loader->LoadShaderAsync(
        "SimplePixelShader.cso",
        &m_pixelShader
        );

    auto loadTextureTask = loader->LoadTextureAsync(
        "reftexture.dds",
        nullptr,
        &m_textureSRV
        );

    auto loadMeshTask = loader->LoadMeshAsync(
        "refmesh.vbo",
        &m_vertexBuffer,
        &m_indexBuffer,
        nullptr,
        &m_indexCount
        );

    // The && operator can be used to create a single task that represents
    // a group of multiple tasks. The new task's completed handler will only
    // be called once all associated tasks have completed. In this case, the
    // new task represents a task to load various assets from the package.
    (loadVertexShaderTask && loadPixelShaderTask && loadTextureTask && loadMeshTask).then([=]()
    {
        m_loadingComplete = true;
    });

    // Create constant buffers and other graphics device-specific resources here.
}

Die Aufgaben werden mithilfe des Operators „&&“ aggregiert. So wird die Lambda-Funktion, mit der das Kennzeichen für „Laden abgeschlossen“ festgelegt wird, erst ausgelöst, wenn alle Aufgaben abgeschlossen sind.Note that the tasks have been aggregated using the && operator such that the lambda that sets the loading complete flag is triggered only when all of the tasks complete. Beachten Sie, dass Sie bei Verwendung mehrerer Kennzeichen auch Racebedingungen nutzen können.Note that if you have multiple flags, you have the possibility of race conditions. Wenn mit der Lambda-Funktion beispielsweise zwei Kennzeichen hintereinander für denselben Wert festgelegt werden, wird von einem anderen Thread möglicherweise nur das erste Kennzeichen erkannt, falls die Prüfung auf Kennzeichen vor dem Festlegen des zweiten Kennzeichens durchgeführt wird.For example, if the lambda sets two flags sequentially to the same value, another thread may only see the first flag set if it examines them before the second flag is set.

Sie haben erfahren, wie Ressourcendateien asynchron geladen werden.You've seen how to load resource files asynchronously. Das synchrone Laden von Dateien ist wesentlich einfacher. Entsprechende Beispiele dafür finden Sie unter Vollständiger Code für BasicReaderWriter und Vollständiger Code für BasicLoader.Synchronous file loads are much simpler, and you can find examples of them in Complete code for BasicReaderWriter and Complete code for BasicLoader.

Für unterschiedliche Arten von Ressourcen und Objekten ist häufig eine zusätzliche Verarbeitung oder Konvertierung erforderlich, bevor Sie diese in Ihrer Grafikpipeline verwenden.Of course, different resource and asset types often require additional processing or conversion before they are ready to be used in your graphics pipeline. Wir sehen uns drei spezielle Typen von Ressourcen an: Gitter, Texturen und Shader.Let's take a look at three specific types of resources: meshes, textures, and shaders.

Laden von GitternLoading meshes

Bei Gittern handelt es sich um Vertexdaten, die entweder mithilfe von Prozeduren im Code des Spiels generiert oder aus einer anderen App (wie 3DStudio MAX oder Alias WaveFront) oder einem anderen Tool in eine Datei exportiert werden.Meshes are vertex data, either generated procedurally by code within your game or exported to a file from another app (like 3DStudio MAX or Alias WaveFront) or tool. Diese Gitter stellen die Modelle Ihres Spiels dar, von einfachen Grundtypen wie Würfel und Kugeln bis zu Autos, Häusern und Figuren.These meshes represent the models in your game, from simple primitives like cubes and spheres to cars and houses and characters. Je nach Format sind darin auch Farb- und Animationsdaten enthalten.They often contain color and animation data, as well, depending on their format. Wir konzentrieren uns hier auf Gitter, die nur Vertexdaten enthalten.We'll focus on meshes that contain only vertex data.

Um ein Gitter richtig laden zu können, müssen Sie das Format der Daten in der Datei für das Gitter kennen.To load a mesh correctly, you must know the format of the data in the file for the mesh. Mit dem obigen einfachen BasicReaderWriter-Typ werden die Daten lediglich als Bytestream gelesen. Dabei ist dem Typ nicht bekannt, dass die Bytedaten ein Gitter darstellen, geschweige denn, dass es sich um ein bestimmtes Gitterformat handelt, das von einer anderen Anwendung exportiert wurde!Our simple BasicReaderWriter type above simply reads the data in as a byte stream; it doesn't know that the byte data represents a mesh, much less a specific mesh format as exported by another application! Sie müssen die Konvertierung durchführen, wenn Sie die Gitterdaten in den Arbeitsspeicher stellen.You must perform the conversion as you bring the mesh data into memory.

(Sie sollten stets versuchen, Objektdaten in einem Format zu verpacken, das möglichst genau mit der internen Darstellung übereinstimmt.(You should always try to package asset data in a format that's as close to the internal representation as possible. Dadurch wird der Ressourceneinsatz verringert und Zeit gespart.)Doing so will reduce resource utilization and save time.)

Als Nächstes werden die Bytedaten aus der Datei des Gitters abgerufen.Let's get the byte data from the mesh's file. Im Beispiel wird angenommen, dass die Datei in einem speziellen Format für Beispiele vorliegt und die Erweiterung ".vbo" aufweist.The format in the example assumes that the file is a sample-specific format suffixed with .vbo. (Wieder gilt, dass dieses Format nicht dem OpenGL-VBO-Format entspricht.) Jeder Vertex ist selbst dem BasicVertex-Typ zugeordnet. Dies ist eine Struktur, die im Code für das Konvertierungstool obj2vbo definiert ist.(Again, this format is not the same as OpenGL's VBO format.) Each vertex itself maps to the BasicVertex type, which is a struct defined in the code for the obj2vbo converter tool. Die Vertexdaten in der VBO-Datei haben das folgende Layout:The layout of the vertex data in the .vbo file looks like this:

  • Die ersten 32 Bits (4 Byte) des Datenstroms enthalten die Anzahl an Vertices (numVertices) im Gitter, dargestellt als uint32-Wert.The first 32 bits (4 bytes) of the data stream contain the number of vertices (numVertices) in the mesh, represented as a uint32 value.
  • Die nächsten 32 Bits (4 Byte) des Datenstroms enthalten die Anzahl an Indizes (numIndices) im Gitter, dargestellt als uint32-Wert.The next 32 bits (4 bytes) of the data stream contain the number of indices in the mesh (numIndices), represented as a uint32 value.
  • Danach enthalten die nachfolgenden (numvertices * sizeof (basicvertex)) Bits die Scheitelpunkt Daten.After that, the subsequent (numVertices * sizeof(BasicVertex)) bits contain the vertex data.
  • Die letzten Bits (numindexes * 16) der Daten enthalten die Indexdaten, die als Sequenz von UInt16-Werten dargestellt werden.The last (numIndices * 16) bits of data contain the index data, represented as a sequence of uint16 values.

Entscheidend ist: Sie sollten das Bitebenenlayout der geladenen Gitterdaten kennen.The point is this: know the bit-level layout of the mesh data you have loaded. Stellen Sie außerdem sicher, dass für Endian-Konsistenz gesorgt ist.Also, be sure you are consistent with endian-ness. Für alle Windows 8-Plattformen wird „Little Endian“ verwendet.All Windows 8 platforms are little-endian.

Im Beispiel wird die „CreateMesh“-Methode aus der LoadMeshAsync-Methode aufgerufen, um diese Interpretation auf Bitebene durchzuführen.In the example, you call a method, CreateMesh, from the LoadMeshAsync method to perform this bit-level interpretation.

task<void> BasicLoader::LoadMeshAsync(
    _In_ Platform::String^ filename,
    _Out_ ID3D11Buffer** vertexBuffer,
    _Out_ ID3D11Buffer** indexBuffer,
    _Out_opt_ uint32* vertexCount,
    _Out_opt_ uint32* indexCount
    )
{
    return m_basicReaderWriter->ReadDataAsync(filename).then([=](const Platform::Array<byte>^ meshData)
    {
        CreateMesh(
            meshData->Data,
            vertexBuffer,
            indexBuffer,
            vertexCount,
            indexCount,
            filename
            );
    });
}

In " featemesh " werden die aus der Datei geladenen Bytedaten interpretiert, und es werden ein Vertex-Puffer und ein Index Puffer für das Mesh erstellt, indem die Vertex-und Indexlisten an ID3D11Device:: featebuffer übergeben und entweder D3D11 _ Bind _ Vertex- _ Puffer oder D3D11 _ Bind _ Index _ buffer angegeben werden.CreateMesh interprets the byte data loaded from the file, and creates a vertex buffer and an index buffer for the mesh by passing the vertex and index lists, respectively, to ID3D11Device::CreateBuffer and specifying either D3D11_BIND_VERTEX_BUFFER or D3D11_BIND_INDEX_BUFFER. In BasicLoader wird der folgende Code verwendet:Here's the code used in BasicLoader:

void BasicLoader::CreateMesh(
    _In_ byte* meshData,
    _Out_ ID3D11Buffer** vertexBuffer,
    _Out_ ID3D11Buffer** indexBuffer,
    _Out_opt_ uint32* vertexCount,
    _Out_opt_ uint32* indexCount,
    _In_opt_ Platform::String^ debugName
    )
{
    // The first 4 bytes of the BasicMesh format define the number of vertices in the mesh.
    uint32 numVertices = *reinterpret_cast<uint32*>(meshData);

    // The following 4 bytes define the number of indices in the mesh.
    uint32 numIndices = *reinterpret_cast<uint32*>(meshData + sizeof(uint32));

    // The next segment of the BasicMesh format contains the vertices of the mesh.
    BasicVertex* vertices = reinterpret_cast<BasicVertex*>(meshData + sizeof(uint32) * 2);

    // The last segment of the BasicMesh format contains the indices of the mesh.
    uint16* indices = reinterpret_cast<uint16*>(meshData + sizeof(uint32) * 2 + sizeof(BasicVertex) * numVertices);

    // Create the vertex and index buffers with the mesh data.

    D3D11_SUBRESOURCE_DATA vertexBufferData = {0};
    vertexBufferData.pSysMem = vertices;
    vertexBufferData.SysMemPitch = 0;
    vertexBufferData.SysMemSlicePitch = 0;
    CD3D11_BUFFER_DESC vertexBufferDesc(numVertices * sizeof(BasicVertex), D3D11_BIND_VERTEX_BUFFER);

    m_d3dDevice->CreateBuffer(
            &vertexBufferDesc,
            &vertexBufferData,
            vertexBuffer
            );
    
    D3D11_SUBRESOURCE_DATA indexBufferData = {0};
    indexBufferData.pSysMem = indices;
    indexBufferData.SysMemPitch = 0;
    indexBufferData.SysMemSlicePitch = 0;
    CD3D11_BUFFER_DESC indexBufferDesc(numIndices * sizeof(uint16), D3D11_BIND_INDEX_BUFFER);
    
    m_d3dDevice->CreateBuffer(
            &indexBufferDesc,
            &indexBufferData,
            indexBuffer
            );
  
    if (vertexCount != nullptr)
    {
        *vertexCount = numVertices;
    }
    if (indexCount != nullptr)
    {
        *indexCount = numIndices;
    }
}

Normalerweise erstellen Sie für jedes im Spiel eingesetzte Gitter ein Vertex/Index-Pufferpaar.You typically create a vertex/index buffer pair for every mesh you use in your game. Wo und wann Sie die Gitter laden, ist Ihre Entscheidung.Where and when you load the meshes is up to you. Falls Sie viele Gitter verwenden, kann es ratsam sein, nur an bestimmten Stellen im Spiel einige dieser Gitter vom Datenträger zu laden, z. B. während spezieller vordefinierter Ladezustände.If you have a lot of meshes, you may only want to load some from the disk at specific points in the game, such as during specific, pre-defined loading states. Für große Gitter, z. B. Geländedaten, können Sie die Vertices per Datenstrom aus einem Cache laden. Das ist jedoch eine komplexere Prozedur, die den Rahmen dieses Themas sprengt.For large meshes, like terrain data, you can stream the vertices from a cache, but that is a more complex procedure and not in the scope of this topic.

Wieder gilt: Es ist wichtig, dass Sie mit dem Format Ihrer Vertexdaten vertraut sind!Again, know your vertex data format! Es gibt sehr viele Möglichkeiten, Vertexdaten in den Tools darzustellen, die zum Erstellen von Modellen verwendet werden.There are many, many ways to represent vertex data across the tools used to create models. Außerdem haben Sie viele unterschiedliche Optionen, was die Darstellung des Eingabelayouts der Vertexdaten für Direct3D betrifft, z. B. Dreieckslisten und -ketten.There are also many different ways to represent the input layout of the vertex data to Direct3D, such as triangle lists and strips. Weitere Informationen zu Vertexdaten finden Sie unter Einführung in Puffer in Direct3D 11 und Grundtypen.For more information about vertex data, read Introduction to Buffers in Direct3D 11 and Primitives.

Als Nächstes sehen wir uns das Laden von Texturen an.Next, let's look at loading textures.

Laden von TexturenLoading textures

Das am häufigsten in Spielen verwendete Objekt – und das Objekt mit den meisten Dateien auf dem Datenträger und im Arbeitsspeicher – sind Texturen.The most common asset in a game—and the one that comprises most of the files on disk and in memory—are textures. Wie Gitter auch, können Texturen in vielen unterschiedlichen Formaten vorliegen, und Sie können Texturen in ein Format konvertieren, das von Direct3D beim Laden verwendet werden kann.Like meshes, textures can come in a variety of formats, and you convert them to a format that Direct3D can use when you load them. Außerdem gibt es viele verschiedene Arten von Texturen, die zum Erzeugen unterschiedlicher Effekte eingesetzt werden.Textures also come in a wide variety of types and are used to create different effects. MIP-Ebenen für Texturen können verwendet werden, um das Aussehen und die Leistung von entfernten Objekten zu verbessern. Verschmutzungs- und Beleuchtungsmaps werden verwendet, um Effekte und Details über einer Basistextur anzuordnen. Normale Maps werden zur Berechnung der Beleuchtung pro Pixel eingesetzt.MIP levels for textures can be used to improve the look and performance of distance objects; dirt and light maps are used to layer effects and detail atop a base texture; and normal maps are used in per-pixel lighting calculations. In modernen Spielen kann eine typische Szene über Tausende einzelner Texturen verfügen, die im Code alle effektiv verwaltet werden müssen!In a modern game, a typical scene can potentially have thousands of individual textures, and your code must effectively manage them all!

Wieder analog zu Gittern gibt es einige spezielle Formate, die mit dem Ziel einer effizienteren Arbeitsspeichernutzung eingesetzt werden.Also like meshes, there are a number of specific formats that are used to make memory usage for efficient. Da für Texturen häufig ein großer Anteil des GPU-Speichers (und Systemspeichers) verbraucht wird, werden diese Daten meist komprimiert.Since textures can easily consume a large portion of the GPU (and system) memory, they are often compressed in some fashion. Es besteht keine Notwendigkeit, für die Texturen Ihres Spiels die Komprimierung zu verwenden. Sie können beliebige Algorithmen für die Komprimierung bzw. Dekomprimierung nutzen, solange Sie für die Direct3D-Shader Daten in einem geeigneten Format bereitstellen (z. B. eine Texture2D-Bitmap).You aren't required to use compression on your game's textures, and you can use any compression/decompression algorithm(s) you want as long as you provide the Direct3D shaders with data in a format it can understand (like a Texture2D bitmap).

Direct3D bietet Unterstützung für die DXT-Texturkomprimierungsalgorithmen. Es kann jedoch sein, dass nicht alle DXT-Formate von der Grafikhardware des Spielers unterstützt werden.Direct3D provides support for the DXT texture compression algorithms, although every DXT format may not be supported in the player's graphics hardware. DDS-Dateien enthalten DXT-Texturen (sowie andere Texturkomprimierungsformate) und weisen die Erweiterung ".dds" auf.DDS files contain DXT textures (and other texture compression formats as well), and are suffixed with .dds.

Eine DDS-Datei ist eine Binärdatei mit den folgenden Informationen:A DDS file is a binary file that contains the following information:

  • DWORD ("Magic Number") mit dem vierstelligen Codewert "DDS" (0x20534444)A DWORD (magic number) containing the four character code value 'DDS ' (0x20534444).

  • Beschreibung der Daten in der DateiA description of the data in the file.

    Die Daten werden mit einer Header Beschreibung mithilfe des DDS- _ Headersbeschrieben. das Pixel Format wird mit DDS _ Pixel Formatdefiniert.The data is described with a header description using DDS_HEADER; the pixel format is defined using DDS_PIXELFORMAT. Beachten Sie, dass der DDS- _ Header und die DDS- _ Pixel Format -Strukturen die veralteten DDSURFACEDESC2-, DDSCAPS2-und ddpixelformat DirectDraw 7-Strukturen ersetzen.Note that the DDS_HEADER and DDS_PIXELFORMAT structures replace the deprecated DDSURFACEDESC2, DDSCAPS2 and DDPIXELFORMAT DirectDraw 7 structures. DDS _ Header ist die binäre Entsprechung von DDSURFACEDESC2 und DDSCAPS2.DDS_HEADER is the binary equivalent of DDSURFACEDESC2 and DDSCAPS2. DDS _ Pixel Format ist das binäre Äquivalent von ddpixelformat.DDS_PIXELFORMAT is the binary equivalent of DDPIXELFORMAT.

    DWORD               dwMagic;
    DDS_HEADER          header;
    

    Wenn der Wert von dwFlags in DDS _ Pixel Format auf ddpf FourCC festgelegt ist _ und dwfourcc auf "DX10" festgelegt ist, wird eine zusätzliche DDS- _ Header _ DXT10 -Struktur vorhanden sein, um Textur Arrays oder DXGI-Formate zu verarbeiten, die nicht als RGB-Pixel formatiert werden können, z. b. Gleit Komma Formate, sRGB-Formate Wenn die ** _ _ DXT10** -Struktur des DDS-Headers vorhanden ist, sieht die gesamte Daten Beschreibung wie folgt aus.If the value of dwFlags in DDS_PIXELFORMAT is set to DDPF_FOURCC and dwFourCC is set to "DX10" an additional DDS_HEADER_DXT10 structure will be present to accommodate texture arrays or DXGI formats that cannot be expressed as an RGB pixel format such as floating point formats, sRGB formats etc. When the DDS_HEADER_DXT10 structure is present, the entire data description will looks like this.

    DWORD               dwMagic;
    DDS_HEADER          header;
    DDS_HEADER_DXT10    header10;
    
  • Ein Zeiger auf ein Bytearray, in dem die Hauptoberflächendaten enthalten sind.A pointer to an array of bytes that contains the main surface data.

    BYTE bdata[]
    
  • Ein Zeiger auf ein Bytearray, in dem die verbleibenden Oberflächen enthalten sind, z. B. Mipmap-Ebenen, Seiten einer Würfelmap, Tiefen in einer Volumentextur.A pointer to an array of bytes that contains the remaining surfaces such as; mipmap levels, faces in a cube map, depths in a volume texture. Weitere Informationen zum Layout der DDS-Datei finden Sie unter den folgenden Links: textureCubemap oder Volumentextur.Follow these links for more information about the DDS file layout for a: texture, a cube map, or a volume texture.

    BYTE bdata2[]
    

In vielen Tools erfolgt der Export im DDS-Format.Many tools export to the DDS format. Falls Sie nicht über ein Tool verfügen, mit dem Exporte der Textur in diesem Format möglich sind, können Sie ein Tool erstellen.If you don't have a tool to export your texture to this format, consider creating one. Weitere Informationen zum DDS-Format und zur Verwendung im Code finden Sie unter Programmieranleitung für DDS.For more detail on the DDS format and how to work with it in your code, read Programming Guide for DDS. In unserem Beispiel wird DDS verwendet.In our example, we'll use DDS.

Wie bei anderen Ressourcentypen auch, lesen Sie die Daten aus einer Datei als Bytestream aus.As with other resource types, you read the data from a file as a stream of bytes. Nachdem die Aufgabe für den Ladevorgang abgeschlossen ist, führt der Lambda-Aufruf Code aus (CreateTexture-Methode), um den Bytestream in ein für Direct3D geeignetes Format zu bringen.Once your loading task completes, the lambda call runs code (the CreateTexture method) to process the stream of bytes into a format that Direct3D can use.

task<void> BasicLoader::LoadTextureAsync(
    _In_ Platform::String^ filename,
    _Out_opt_ ID3D11Texture2D** texture,
    _Out_opt_ ID3D11ShaderResourceView** textureView
    )
{
    return m_basicReaderWriter->ReadDataAsync(filename).then([=](const Platform::Array<byte>^ textureData)
    {
        CreateTexture(
            GetExtension(filename) == "dds",
            textureData->Data,
            textureData->Length,
            texture,
            textureView,
            filename
            );
    });
}

Im vorherigen Codeausschnitt wird mit der Lambda-Funktion geprüft, ob der Dateiname die Erweiterung "dds" aufweist.In the previous snippet, the lambda checks to see if the filename has an extension of "dds". Wenn ja, wird angenommen, dass es sich um eine DDS-Textur handelt.If it does, you assume that it is a DDS texture. Wenn nicht, verwenden Sie die Windows-Bilderstellungskomponenten-APIs (WIC), um das Format zu ermitteln und die Daten als Bitmap zu decodieren.If not, well, use the Windows Imaging Component (WIC) APIs to discover the format and decode the data as a bitmap. In beiden Fällen ist das Ergebnis eine Texture2D-Bitmap (oder ein Fehler).Either way, the result is a Texture2D bitmap (or an error).

void BasicLoader::CreateTexture(
    _In_ bool decodeAsDDS,
    _In_reads_bytes_(dataSize) byte* data,
    _In_ uint32 dataSize,
    _Out_opt_ ID3D11Texture2D** texture,
    _Out_opt_ ID3D11ShaderResourceView** textureView,
    _In_opt_ Platform::String^ debugName
    )
{
    ComPtr<ID3D11ShaderResourceView> shaderResourceView;
    ComPtr<ID3D11Texture2D> texture2D;

    if (decodeAsDDS)
    {
        ComPtr<ID3D11Resource> resource;

        if (textureView == nullptr)
        {
            CreateDDSTextureFromMemory(
                m_d3dDevice.Get(),
                data,
                dataSize,
                &resource,
                nullptr
                );
        }
        else
        {
            CreateDDSTextureFromMemory(
                m_d3dDevice.Get(),
                data,
                dataSize,
                &resource,
                &shaderResourceView
                );
        }

        resource.As(&texture2D);
    }
    else
    {
        if (m_wicFactory.Get() == nullptr)
        {
            // A WIC factory object is required in order to load texture
            // assets stored in non-DDS formats.  If BasicLoader was not
            // initialized with one, create one as needed.
            CoCreateInstance(
                    CLSID_WICImagingFactory,
                    nullptr,
                    CLSCTX_INPROC_SERVER,
                    IID_PPV_ARGS(&m_wicFactory));
        }

        ComPtr<IWICStream> stream;
        m_wicFactory->CreateStream(&stream);

        stream->InitializeFromMemory(
                data,
                dataSize);

        ComPtr<IWICBitmapDecoder> bitmapDecoder;
        m_wicFactory->CreateDecoderFromStream(
                stream.Get(),
                nullptr,
                WICDecodeMetadataCacheOnDemand,
                &bitmapDecoder);

        ComPtr<IWICBitmapFrameDecode> bitmapFrame;
        bitmapDecoder->GetFrame(0, &bitmapFrame);

        ComPtr<IWICFormatConverter> formatConverter;
        m_wicFactory->CreateFormatConverter(&formatConverter);

        formatConverter->Initialize(
                bitmapFrame.Get(),
                GUID_WICPixelFormat32bppPBGRA,
                WICBitmapDitherTypeNone,
                nullptr,
                0.0,
                WICBitmapPaletteTypeCustom);

        uint32 width;
        uint32 height;
        bitmapFrame->GetSize(&width, &height);

        std::unique_ptr<byte[]> bitmapPixels(new byte[width * height * 4]);
        formatConverter->CopyPixels(
                nullptr,
                width * 4,
                width * height * 4,
                bitmapPixels.get());

        D3D11_SUBRESOURCE_DATA initialData;
        ZeroMemory(&initialData, sizeof(initialData));
        initialData.pSysMem = bitmapPixels.get();
        initialData.SysMemPitch = width * 4;
        initialData.SysMemSlicePitch = 0;

        CD3D11_TEXTURE2D_DESC textureDesc(
            DXGI_FORMAT_B8G8R8A8_UNORM,
            width,
            height,
            1,
            1
            );

        m_d3dDevice->CreateTexture2D(
                &textureDesc,
                &initialData,
                &texture2D);

        if (textureView != nullptr)
        {
            CD3D11_SHADER_RESOURCE_VIEW_DESC shaderResourceViewDesc(
                texture2D.Get(),
                D3D11_SRV_DIMENSION_TEXTURE2D
                );

            m_d3dDevice->CreateShaderResourceView(
                    texture2D.Get(),
                    &shaderResourceViewDesc,
                    &shaderResourceView);
        }
    }


    if (texture != nullptr)
    {
        *texture = texture2D.Detach();
    }
    if (textureView != nullptr)
    {
        *textureView = shaderResourceView.Detach();
    }
}

Nach Abschluss dieses Codes befindet sich ein Texture2D-Objekt im Arbeitsspeicher, das aus einer Bilddatei geladen wurde.When this code completes, you have a Texture2D in memory, loaded from an image file. Wie bei Gittern auch, enthalten Ihr Spiel und die einzelnen Szenen eine große Zahl dieser Objekte.As with meshes, you probably have a lot of them in your game and in any given scene. Es ist ratsam, pro Szene oder Level Caches für Texturen einzurichten, auf die regelmäßig zugegriffen wird, anstatt alle Texturen zu Beginn des Spiels oder eines Levels zu laden.Consider creating caches for regularly accessed textures per-scene or per-level, rather than loading them all when the game or level starts.

(Die CreateDDSTextureFromMemory-Methode, die im obigen Beispiel aufgerufen wird, ist in ganzer Länge unter Vollständiger Code für DDSTextureLoader zu finden.)(The CreateDDSTextureFromMemory method called in the above sample can be explored in full in Complete code for DDSTextureLoader.)

Außerdem können einzelne Texturen oder Texturdesigns bestimmten Gitterpolygonen oder Oberflächen zugeordnet sein.Also, individual textures or texture "skins" may map to specific mesh polygons or surfaces. Diese Zuordnungsdaten werden normalerweise mit dem Tool exportiert, das von einem Spezialisten oder Designer zum Erstellen des Modells und der Texturen verwendet wurde.This mapping data is usually exported by the tool an artist or designer used to create the model and the textures. Stellen Sie sicher, dass Sie beim Laden der exportierten Daten auch diese Informationen erfassen. Sie benötigen diese Daten, um die richtigen Texturen den entsprechenden Oberflächen zuzuordnen, wenn Sie die Fragmentschattierung erstellen.Make sure that you capture this information as well when you load the exported data, as you will use it map the correct textures to the corresponding surfaces when you perform fragment shading.

Laden von ShadernLoading shaders

Bei Shadern handelt es sich um kompilierte High Level Shader Language (HLSL)-Dateien, die in den Arbeitsspeicher geladen und an bestimmten Stellen der Grafikpipeline aufgerufen werden.Shaders are compiled High Level Shader Language (HLSL) files that are loaded into memory and invoked at specific stages of the graphics pipeline. Die am häufigsten verwendeten und wichtigsten Shader sind die Vertex- und Pixel-Shader, mit denen die einzelnen Vertices des Gitters bzw. die Pixel in den Viewports der Szenen verarbeitet werden.The most common and essential shaders are the vertex and pixel shaders, which process the individual vertices of your mesh and the pixels in the scene's viewport(s), respectively. Der HLSL-Code wird ausgeführt, um die Geometrie zu transformieren, Beleuchtungseffekte und Texturen anzuwenden und die Nachbearbeitung der gerenderten Szene durchzuführen.The HLSL code is executed to transform the geometry, apply lighting effects and textures, and perform post-processing on the rendered scene.

Ein Direct3D-Spiel kann über eine Reihe unterschiedlicher Shader verfügen, die jeweils zu einer separaten CSO-Datei (Compiled Shader Object, .cso) kompiliert werden.A Direct3D game can have a number of different shaders, each one compiled into a separate CSO (Compiled Shader Object, .cso) file. Normalerweise sind nicht so viele Dateien vorhanden, dass diese dynamisch geladen werden müssen. In den meisten Fällen können Sie die Dateien einfach zu Beginn des Spiels oder pro Level (z. B. einen Shader für Regeneffekte) laden.Normally, you don't have so many that you need to load them dynamically, and in most cases, you can simply load them when the game is starting, or on a per-level basis (such as a shader for rain effects).

Im Code der BasicLoader-Klasse werden einige Überladungen für unterschiedliche Shader bereitgestellt, einschließlich Vertex-, Geometry-, Pixel- und Hull-Shader.The code in the BasicLoader class provides a number of overloads for different shaders, including vertex, geometry, pixel, and hull shaders. Im folgenden Code ist ein Beispiel für Pixel-Shader enthalten.The code below covers pixel shaders as an example. (Den gesamten Code finden Sie unter Vollständiger Code für BasicLoader.)(You can review the complete code in Complete code for BasicLoader.)

concurrency::task<void> LoadShaderAsync(
    _In_ Platform::String^ filename,
    _Out_ ID3D11PixelShader** shader
    );

// ...

task<void> BasicLoader::LoadShaderAsync(
    _In_ Platform::String^ filename,
    _Out_ ID3D11PixelShader** shader
    )
{
    return m_basicReaderWriter->ReadDataAsync(filename).then([=](const Platform::Array<byte>^ bytecode)
    {
        
       m_d3dDevice->CreatePixelShader(
                bytecode->Data,
                bytecode->Length,
                nullptr,
                shader);
    });
}

In diesem Beispiel verwenden Sie die basishaderwriter -Instanz (m _ basikreaderwriter), um die angegebene kompilierte Shader-Objektdatei (. CSO) als Bytestream zu lesen.In this example, you use the BasicReaderWriter instance (m_basicReaderWriter) to read in the supplied compiled shader object (.cso) file as a byte stream. Nach Abschluss der Aufgabe ruft die Lambda-Funktion ID3D11Device::CreatePixelShader mit den aus der Datei geladenen Bytedaten auf.Once that task completes, the lambda calls ID3D11Device::CreatePixelShader with the byte data loaded from the file. In Ihrem Rückruf muss ein Kennzeichen dafür festgelegt werden, dass der Ladevorgang erfolgreich war, und der Code muss dieses Kennzeichen überprüfen, bevor der Shader ausgeführt wird.Your callback must set some flag indicating that the load was successful, and your code must check this flag before running the shader.

Vertex-Shader sind etwas komplexer.Vertex shaders are bit more complex. Für einen Vertex-Shader laden Sie zusätzlich ein separates Eingabelayout, mit dem die Vertexdaten definiert werden.For a vertex shader, you also load a separate input layout that defines the vertex data. Mit dem folgenden Code kann ein Vertex-Shader zusammen mit einem benutzerdefinierten Vertexeingabelayout asynchron geladen werden.The following code can be used to asynchronously load a vertex shader along with a custom vertex input layout. Achten Sie darauf, dass die aus den Gittern geladenen Vertexinformationen von diesem Eingabelayout richtig dargestellt werden können!Be sure that the vertex information that you load from your meshes can be correctly represented by this input layout!

Wir erstellen das Eingabelayout, bevor der Vertex-Shader geladen wird.Let's create the input layout before you load the vertex shader.

void BasicLoader::CreateInputLayout(
    _In_reads_bytes_(bytecodeSize) byte* bytecode,
    _In_ uint32 bytecodeSize,
    _In_reads_opt_(layoutDescNumElements) D3D11_INPUT_ELEMENT_DESC* layoutDesc,
    _In_ uint32 layoutDescNumElements,
    _Out_ ID3D11InputLayout** layout
    )
{
    if (layoutDesc == nullptr)
    {
        // If no input layout is specified, use the BasicVertex layout.
        const D3D11_INPUT_ELEMENT_DESC basicVertexLayoutDesc[] =
        {
            { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0,  D3D11_INPUT_PER_VERTEX_DATA, 0 },
            { "NORMAL",   0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
            { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT,    0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0 },
        };

        m_d3dDevice->CreateInputLayout(
                basicVertexLayoutDesc,
                ARRAYSIZE(basicVertexLayoutDesc),
                bytecode,
                bytecodeSize,
                layout);
    }
    else
    {
        m_d3dDevice->CreateInputLayout(
                layoutDesc,
                layoutDescNumElements,
                bytecode,
                bytecodeSize,
                layout);
    }
}

In diesem speziellen Layout werden für jeden Vertex vom Vertex-Shader die folgenden Daten verarbeitet:In this particular layout, each vertex has the following data processed by the vertex shader:

  • Eine 3D-Koordinatenposition (x, y, z) im Koordinatenbereich des Modells, dargestellt in Form von drei 32-Bit-Gleitkommawerten.A 3D coordinate position (x, y, z) in the model's coordinate space, represented as a trio of 32-bit floating point values.
  • Ein normaler Vektor für den Vertex, ebenfalls dargestellt in Form von drei 32-Bit-Gleitkommawerten.A normal vector for the vertex, also represented as three 32-bit floating point values.
  • Ein transformierter 2D-Texturkoordinatenwert (u, v), dargestellt als 32-Bit-Gleitkommawertpaar.A transformed 2D texture coordinate value (u, v) , represented as a pair of 32-bit floating values.

Diese Eingabeelemente pro Vertex werden als HLSL-Semantik bezeichnet. Es handelt sich dabei um eine Gruppe definierter Register, die zum Übergeben von Daten in das bzw. aus dem kompilierten Shaderobjekt verwendet werden.These per-vertex input elements are called HLSL semantics, and they are a set of defined registers used to pass data to and from your compiled shader object. Die Pipeline führt den Vertex-Shader einmal für jeden Vertex des geladenen Gitters aus.Your pipeline runs the vertex shader once for every vertex in the mesh that you've loaded. Mit der Semantik wird die Eingabe in den Vertex-Shader (sowie auch die Ausgabe) während der Ausführung definiert, und diese Daten werden dann für die Berechnungen pro Vertex im HLSL-Code des Shaders bereitgestellt.The semantics define the input to (and output from) the vertex shader as it runs, and provide this data for your per-vertex computations in your shader's HLSL code.

Laden Sie als Nächstes das Vertex-Shaderobjekt.Now, load the vertex shader object.

concurrency::task<void> LoadShaderAsync(
        _In_ Platform::String^ filename,
        _In_reads_opt_(layoutDescNumElements) D3D11_INPUT_ELEMENT_DESC layoutDesc[],
        _In_ uint32 layoutDescNumElements,
        _Out_ ID3D11VertexShader** shader,
        _Out_opt_ ID3D11InputLayout** layout
        );

// ...

task<void> BasicLoader::LoadShaderAsync(
    _In_ Platform::String^ filename,
    _In_reads_opt_(layoutDescNumElements) D3D11_INPUT_ELEMENT_DESC layoutDesc[],
    _In_ uint32 layoutDescNumElements,
    _Out_ ID3D11VertexShader** shader,
    _Out_opt_ ID3D11InputLayout** layout
    )
{
    // This method assumes that the lifetime of input arguments may be shorter
    // than the duration of this task.  In order to ensure accurate results, a
    // copy of all arguments passed by pointer must be made.  The method then
    // ensures that the lifetime of the copied data exceeds that of the task.

    // Create copies of the layoutDesc array as well as the SemanticName strings,
    // both of which are pointers to data whose lifetimes may be shorter than that
    // of this method's task.
    shared_ptr<vector<D3D11_INPUT_ELEMENT_DESC>> layoutDescCopy;
    shared_ptr<vector<string>> layoutDescSemanticNamesCopy;
    if (layoutDesc != nullptr)
    {
        layoutDescCopy.reset(
            new vector<D3D11_INPUT_ELEMENT_DESC>(
                layoutDesc,
                layoutDesc + layoutDescNumElements
                )
            );

        layoutDescSemanticNamesCopy.reset(
            new vector<string>(layoutDescNumElements)
            );

        for (uint32 i = 0; i < layoutDescNumElements; i++)
        {
            layoutDescSemanticNamesCopy->at(i).assign(layoutDesc[i].SemanticName);
        }
    }

    return m_basicReaderWriter->ReadDataAsync(filename).then([=](const Platform::Array<byte>^ bytecode)
    {
       m_d3dDevice->CreateVertexShader(
                bytecode->Data,
                bytecode->Length,
                nullptr,
                shader);

        if (layout != nullptr)
        {
            if (layoutDesc != nullptr)
            {
                // Reassign the SemanticName elements of the layoutDesc array copy to point
                // to the corresponding copied strings. Performing the assignment inside the
                // lambda body ensures that the lambda will take a reference to the shared_ptr
                // that holds the data.  This will guarantee that the data is still valid when
                // CreateInputLayout is called.
                for (uint32 i = 0; i < layoutDescNumElements; i++)
                {
                    layoutDescCopy->at(i).SemanticName = layoutDescSemanticNamesCopy->at(i).c_str();
                }
            }

            CreateInputLayout(
                bytecode->Data,
                bytecode->Length,
                layoutDesc == nullptr ? nullptr : layoutDescCopy->data(),
                layoutDescNumElements,
                layout);   
        }
    });
}

In diesem Code erstellen Sie den Vertex-Shader per Aufruf von ID3D11Device::CreateVertexShader, nachdem Sie die Bytedaten für die CSO-Datei des Vertex-Shaders eingelesen haben.In this code, once you've read in the byte data for the vertex shader's CSO file, you create the vertex shader by calling ID3D11Device::CreateVertexShader. Danach erstellen Sie das Eingabelayout für den Shader in derselben Lambda-Funktion.After that, you create your input layout for the shader in the same lambda.

Für andere Arten von Shadern, z. B. Geometry- und Hull-Shader, kann ebenfalls eine spezielle Konfiguration erforderlich sein.Other shader types, such as hull and geometry shaders, can also require specific configuration. Den vollständigen Code für verschiedene Methoden zum Laden von Shadern finden Sie unter Vollständiger Code für BasicLoader und Beispiel für das Laden der Direct3D-Ressource.Complete code for a variety of shader loading methods is provided in Complete code for BasicLoader and in the Direct3D resource loading sample.

BemerkungenRemarks

Sie sollten jetzt mit den Methoden zum asynchronen Laden häufig verwendeter Ressourcen und Objekte für Spiele, wie Gittern, Texturen und kompilierten Shadern, vertraut sein und diese Methoden erstellen und ändern können.At this point, you should understand and be able to create or modify methods for asynchronously loading common game resources and assets, such as meshes, textures, and compiled shaders.