Charger des ressources dans votre jeu DirectXLoad resources in your DirectX game

La plupart des jeux, à un moment donné, chargent des ressources et des éléments multimédias (comme les nuanceurs, les textures, les maillages prédéfinis ou d’autres données graphiques) à partir d’un stockage local ou d’autres flux de données.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. Ici, nous vous guidons tout au long d’une vue d’ensemble de ce que vous devez prendre en compte lors du chargement de ces fichiers à utiliser dans votre jeu de plateforme Windows universelle DirectX C/C++ (UWP).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.

Par exemple, les maillages des objets polygonaux de votre jeu peuvent avoir été créés avec un autre outil et exportés vers un format spécifique.For example, the meshes for polygonal objects in your game might have been created with another tool, and exported to a specific format. Il en va de même pour les textures et plus encore : alors que généralement la plupart des outils peuvent écrire une image bitmap plate, sans compression, qui est comprise par la plupart des API graphiques, utiliser dans votre jeu une telle image peut s’avérer extrêmement inefficace.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. Ici, nous vous guidons à travers les étapes de base du chargement de trois types de ressources graphiques pour une utilisation avec Direct3D : les maillages (modèles), les textures (bitmaps) et les objets nuanceurs compilés.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.

Ce que vous devez savoirWhat you need to know

TechnologiesTechnologies

  • Bibliothèque de modèles parallèles (ppltasks.h)Parallel Patterns Library (ppltasks.h)

PrérequisPrerequisites

  • Comprendre le Windows Runtime de baseUnderstand the basic Windows Runtime
  • Comprendre les tâches asynchronesUnderstand asynchronous tasks
  • Comprendre les concepts de base de la programmation graphique 3DUnderstand the basic concepts of 3-D graphics programming.

Cet exemple inclut également trois fichiers de code permettant le chargement et la gestion des ressources.This sample also includes three code files for resource loading and management. Vous rencontrerez les objets de code définis dans ces fichiers tout au long de cette rubrique.You'll encounter the code objects defined in these files throughout this topic.

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

Vous pouvez trouver le code complet de ces exemples dans les liens suivants.The complete code for these samples can be found in the following links.

RubriqueTopic DescriptionDescription

Code complet de BasicLoaderComplete code for BasicLoader

Code complet pour une classe et des méthodes qui convertissent et chargent des objets maillés graphiques en mémoire.Complete code for a class and methods that convert and load graphics mesh objects into memory.

Code complet de BasicReaderWriterComplete code for BasicReaderWriter

Code complet pour une classe et des méthodes permettant de lire et d’écrire des fichiers de données binaires en général.Complete code for a class and methods for reading and writing binary data files in general. Utilisé par la classe BasicLoader.Used by the BasicLoader class.

Code complet de DDSTextureLoaderComplete code for DDSTextureLoader

Code complet pour une classe et la méthode permettant de charger une texture DDS à partir de la mémoire.Complete code for a class and method that loads a DDS texture from memory.

 

InstructionsInstructions

Chargement asynchroneAsynchronous loading

Le chargement asynchrone est géré à l’aide du modèle task de la bibliothèque de modèles parallèles (PPL).Asynchronous loading is handled using the task template from the Parallel Patterns Library (PPL). Une task contient un appel de méthode suivi d’une expression lambda qui traite les résultats de l’appel asynchrone après qu’il se termine, et suit généralement le format :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 });.

Des tâches peuvent être enchaînées à l’aide de la syntaxe .then(), afin qu’à l’issue d’une opération, une autre opération asynchrone qui dépend des résultats de cette opération préalable puisse s’exécuter.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. Ainsi, vous pouvez charger, convertir et gérer des éléments multimédias complexes sur des threads séparés de façon pratiquement invisible pour le joueur.In this way, you can load, convert, and manage complex assets on separate threads in a way that appears almost invisible to the player.

Pour plus d’informations, voir Programmation asynchrone en C++.For more details, read Asynchronous programming in C++.

Maintenant, examinons la structure de base pour déclarer et créer une méthode de chargement de fichier asynchrone, 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;
    });
}

Dans ce code, lorsque la méthode ReadDataAsync définie ci-dessus est appelée, une tâche est créée pour lire une mémoire tampon à partir du système de fichiers.In this code, when your code calls the ReadDataAsync method defined above, a task is created to read a buffer from the file system. Une fois qu’elle est terminée, une tâche chaînée récupère la mémoire tampon et place les octets de la mémoire tampon dans un tableau à l’aide du type statique DataReader.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.          
    });

Voici l’appel que vous effectuez pour lire de façon asynchrone : ReadDataAsync.Here's the call you make to ReadDataAsync. Lorsque c’est terminé, votre code reçoit un tableau d’octets lus à partir du fichier fourni.When it completes, your code receives an array of bytes read from the provided file. Puisque ReadDataAsync est défini comme une tâche, vous pouvez utiliser une expression lambda pour exécuter une opération spécifique lorsque le tableau d’octets est renvoyé, comme transmettre ces données à une fonction DirectX qui peut les utiliser.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.

Si votre jeu est assez simple, chargez vos ressources avec une méthode comme celle-ci lorsque l’utilisateur démarre le jeu.If your game is sufficiently simple, load your resources with a method like this when the user starts the game. Vous pouvez le faire avant de démarrer la boucle principale du jeu à partir d’un certain point dans la séquence d’appels de votre implémentation IFrameworkView::Run.You can do this before you start the main game loop from some point in the call sequence of your IFrameworkView::Run implementation. Encore une fois, vous appelez les méthodes de chargement de vos ressources de façon asynchrone afin que le jeu puisse démarrer plus rapidement et que le joueur n’ait pas à attendre la fin du chargement pour s’engager dans les interactions précoces.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.

Toutefois, vous ne voulez pas démarrer le jeu à proprement parler tant que le chargement asynchrone n’est pas terminé !However, you don't want to start the game proper until all of the async loading has completed! Créez une méthode pour signaler que le chargement est terminé, tel qu’un champ spécifique, et utilisez les expressions lambda sur vos méthodes de chargement pour activer ce signal lorsque c’est terminé.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. Vérifiez la variable avant de démarrer tout composant qui utilise ces ressources chargées.Check the variable before starting any components that use those loaded resources.

Voici un exemple d’utilisation de méthodes asynchrones définies dans BasicLoader.cpppour charger des nuanceurs, un maillage et une texture quand le jeu démarre.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. Notez qu’il définit un champ spécifique sur l’objet de jeu, m _ loadingComplete, lorsque toutes les méthodes de chargement se terminent.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.
}

Notez que les tâches ont été agrégées à l’aide de l’opérateur &&amp; de sorte que l’expression lambda qui définit l’indicateur de chargement complet se déclenche uniquement une fois toutes les tâches terminées.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. Notez que si vous avez plusieurs indicateurs, vous avez la possibilité de conditions de concurrence critique.Note that if you have multiple flags, you have the possibility of race conditions. Par exemple, si l’expression lambda définit deux indicateurs séquentiellement sur la même valeur, un autre thread ne pourrait voir que le premier indicateur s’il les examine avant que le deuxième ne soit défini.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.

Vous avez vu comment charger des fichiers de ressources de manière asynchrone.You've seen how to load resource files asynchronously. Les chargements synchrones de fichiers sont beaucoup plus simples, et vous en trouverez des exemples dans le Code complet de BasicReaderWriter et le Code complet de BasicLoader.Synchronous file loads are much simpler, and you can find examples of them in Complete code for BasicReaderWriter and Complete code for BasicLoader.

Bien sûr, les différents types de ressources et d’actifs nécessitent souvent un traitement ou une conversion supplémentaire avant de pouvoir être utilisés dans votre pipeline graphique.Of course, different resource and asset types often require additional processing or conversion before they are ready to be used in your graphics pipeline. Nous allons examiner trois types de ressources spécifiques : les maillages, les textures et les nuanceurs.Let's take a look at three specific types of resources: meshes, textures, and shaders.

Chargement des maillagesLoading meshes

Les maillages sont des données de vertex, soit générées de façon procédurale par du code au sein de votre jeu soit exportées vers un fichier depuis une autre application (comme 3DStudio MAX ou Alias WaveFront) ou un outil.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. Ces maillages représentent les modèles dans votre jeu, allant de simples primitives comme des cubes ou des sphères à des voitures, des maisons et des personnages.These meshes represent the models in your game, from simple primitives like cubes and spheres to cars and houses and characters. Ils contiennent souvent des données de couleur et d’animation, également, en fonction de leur format.They often contain color and animation data, as well, depending on their format. Nous nous concentrerons sur les maillages qui contiennent uniquement des données de vertex.We'll focus on meshes that contain only vertex data.

Pour charger un maillage correctement, vous devez connaître le format des données dans le fichier du maillage.To load a mesh correctly, you must know the format of the data in the file for the mesh. Notre type simple BasicReaderWriter ci-dessus lit simplement les données en entrée comme un flux d’octets, sans savoir que ces octets représentent un maillage, encore moins un format de maillage spécifique exporté par une autre application !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! Vous devez effectuer la conversion au fur et à mesure que vous introduisez les données du maillage en mémoire.You must perform the conversion as you bring the mesh data into memory.

(Vous devriez toujours essayer d’empaqueter les données des actifs du jeu dans un format aussi proche de la représentation interne que possible.(You should always try to package asset data in a format that's as close to the internal representation as possible. Cela permet de réduire l’utilisation des ressources et de gagner du temps.)Doing so will reduce resource utilization and save time.)

Nous allons récupérer les données octets du fichier de maillage.Let's get the byte data from the mesh's file. Le format de l’exemple suppose que le fichier est un format spécifique à l’exemple avec le suffixe .vbo.The format in the example assumes that the file is a sample-specific format suffixed with .vbo. (Là encore, ce format est différent du format VBO de OpenGL.) Chaque vertex est mappé au type BasicVertex, qui est une structure définie dans le code de l’outil convertisseur obj2vbo.(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. La disposition des données de vertex dans le fichier .vbo ressemble à ceci :The layout of the vertex data in the .vbo file looks like this:

  • Les 32 premiers bits (4 octets) du flux de données contiennent le nombre de vertex (numVertices) dans le maillage, représenté par une valeur uint32.The first 32 bits (4 bytes) of the data stream contain the number of vertices (numVertices) in the mesh, represented as a uint32 value.
  • Les 32 bits suivants (4 octets) du flux de données contiennent le nombre d’index (numIndices) dans le maillage, représenté par une valeur uint32.The next 32 bits (4 bytes) of the data stream contain the number of indices in the mesh (numIndices), represented as a uint32 value.
  • Après cela, les bits (numVertices * sizeof (BasicVertex)) suivants contiennent les données de vertex.After that, the subsequent (numVertices * sizeof(BasicVertex)) bits contain the vertex data.
  • Les derniers (numIndices * 16) bits de données contiennent les données d’index, représentées sous la forme d’une séquence de valeurs UInt16.The last (numIndices * 16) bits of data contain the index data, represented as a sequence of uint16 values.

La clé est donc de connaître la disposition au niveau des bits des données de maillage chargées.The point is this: know the bit-level layout of the mesh data you have loaded. Assurez-vous aussi d’être cohérent sur le plan endian.Also, be sure you are consistent with endian-ness. Toutes les plates-formes Windows 8 sont en mode Little Endian.All Windows 8 platforms are little-endian.

Dans l’exemple, vous appelez une méthode, CreateMesh, à partir de la méthode LoadMeshAsync pour effectuer cette interprétation au niveau des bits.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
            );
    });
}

CreateMesh interprète les données d’octets chargées à partir du fichier, crée une mémoire tampon de vertex et une mémoire tampon d’index pour le maillage en passant respectivement les listes de vertex et d’index à ID3D11Device :: CreateBuffer et en spécifiant d3d11 la _ _ mémoire tampon de vertex Bind _ ou la _ mémoire tampon d’index de liaison d3d11 _ _ .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. Voici le code utilisé dans BasicLoader :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;
    }
}

En général, vous créez une paire de mémoires tampons vertex/index pour chaque maillage utilisé dans votre jeu.You typically create a vertex/index buffer pair for every mesh you use in your game. Où et quand vous chargez les maillages vous appartient.Where and when you load the meshes is up to you. Si vous avez un grand nombre de maillages, vous pouvez n’en charger que certains à partir du disque à des points précis du jeu, comme durant les états de chargement prédéfinis spécifiques.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. Pour les grands maillages, comme les données de terrain, vous pouvez lire les vertex à partir d’un cache, mais cette procédure plus complexe sort du cadre de cette rubrique.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.

Encore une fois, vous devez connaître le format des données de vertex !Again, know your vertex data format! Il existe de nombreuses façons de représenter les données de vertex selon les outils utilisés pour créer des modèles.There are many, many ways to represent vertex data across the tools used to create models. Il existe également différentes façons de représenter la disposition d’entrée des données vertex pour Direct3D, comme les bandes et listes de triangles.There are also many different ways to represent the input layout of the vertex data to Direct3D, such as triangle lists and strips. Pour plus d’informations sur les données vertex, voir Présentation des mémoires tampons dans Direct3D 11 et Primitives.For more information about vertex data, read Introduction to Buffers in Direct3D 11 and Primitives.

Maintenant, penchons-nous sur le chargement des textures.Next, let's look at loading textures.

Chargement des texturesLoading textures

L’élément multimédia le plus courant d’un jeu, et celui qui comprend la plupart des fichiers sur disque et en mémoire, correspond aux textures.The most common asset in a game—and the one that comprises most of the files on disk and in memory—are textures. Comme les maillages, les textures peuvent être dans divers formats, et vous les convertissez en format utilisable par Direct3D lorsque vous les chargez.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. Les textures sont également disponibles dans une grande variété de types et permettent de créer différents effets.Textures also come in a wide variety of types and are used to create different effects. Les niveaux MIP pour les textures peuvent servir à améliorer l’apparence et les performances des objets à distance ; des cartes de poussière et d’éclairage permettent de disposer en couches des effets et détails au-dessus d’une texture de base ; et des cartes normales sont utilisées dans les calculs d’éclairage par pixel.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. Dans un jeu moderne, une scène typique peut contenir potentiellement des milliers de textures individuelles, et votre code doit les gérer efficacement toutes !In a modern game, a typical scene can potentially have thousands of individual textures, and your code must effectively manage them all!

Comme pour les maillages, un certain nombre de formats spécifiques permettent d’optimiser l’utilisation de la mémoire.Also like meshes, there are a number of specific formats that are used to make memory usage for efficient. Étant donné que les textures peuvent facilement consommer une grande partie de la mémoire GPU (et système ), elles sont souvent compressées d’une certaine façon.Since textures can easily consume a large portion of the GPU (and system) memory, they are often compressed in some fashion. Vous n’êtes pas obligé d’utiliser la compression pour les textures de votre jeu, et vous pouvez utiliser n’importe quel algorithme de compression/décompression du moment que vous fournissez les nuanceurs Direct3D avec les données dans un format qu’il peut comprendre (comme un bitmap Texture2D).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 prend en charge les algorithmes de compression de texture DXT, bien que chaque format DXT ne soit pas forcément géré par le matériel graphique du joueur.Direct3D provides support for the DXT texture compression algorithms, although every DXT format may not be supported in the player's graphics hardware. Les fichiers DDS qui contiennent des textures DXT (et autres formats de compression de texture également) ont le suffixe .dds.DDS files contain DXT textures (and other texture compression formats as well), and are suffixed with .dds.

Un fichier DDS est un fichier binaire qui contient les informations suivantes :A DDS file is a binary file that contains the following information:

  • Un DWORD (nombre magique) contenant la valeur codée des quatre caractères "DDS " (0x20534444).A DWORD (magic number) containing the four character code value 'DDS ' (0x20534444).

  • Une description des données du fichier.A description of the data in the file.

    Les données sont décrites avec une description d’en-tête à l’aide de l' ** _ en-tête DDS**; le format de pixel est défini à l’aide de DDS _ PIXELFORMAT.The data is described with a header description using DDS_HEADER; the pixel format is defined using DDS_PIXELFORMAT. Notez que l' ** _ en-tête DDS** et les structures DDS _ PIXELFORMAT remplacent les structures DDSURFACEDESC2, DDSCAPS2 et DDPIXELFORMAT DirectDraw 7 déconseillées.Note that the DDS_HEADER and DDS_PIXELFORMAT structures replace the deprecated DDSURFACEDESC2, DDSCAPS2 and DDPIXELFORMAT DirectDraw 7 structures. DDS _ L’en-tête est l’équivalent binaire de DDSURFACEDESC2 et DDSCAPS2.DDS_HEADER is the binary equivalent of DDSURFACEDESC2 and DDSCAPS2. DDS _ PIXELFORMAT est l’équivalent binaire de DDPIXELFORMAT.DDS_PIXELFORMAT is the binary equivalent of DDPIXELFORMAT.

    DWORD               dwMagic;
    DDS_HEADER          header;
    

    Si la valeur de dwFlags dans DDS _ PIXELFORMAT est définie sur DDPF _ FourCC et que dwFourCC est défini sur « facilement », une structure de ** _ _ DXT10 d’en-tête DDS** supplémentaire sera présente pour prendre en charge les tableaux de texture ou les formats de dxgi qui ne peuvent pas être exprimés sous la forme d’un format de pixel RVB comme les formats à virgule flottante, les formats sRVB, etc. Lorsque la structure DXT10 de l' ** _ en-tête _ DDS** est présente, la description de la totalité des données se présente comme suit.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;
    
  • Pointeur vers un tableau d’octets qui contient les principales données de surface.A pointer to an array of bytes that contains the main surface data.

    BYTE bdata[]
    
  • Pointeur vers un tableau d’octets qui contient les surfaces restantes telles que niveaux de mipmap, faces dans un plan de cube, profondeurs dans une texture de volume.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. Suivez ces liens pour plus d’informations sur le schéma du fichier DDS pour une texture, un mappage de cube ou une texture de volume.Follow these links for more information about the DDS file layout for a: texture, a cube map, or a volume texture.

    BYTE bdata2[]
    

De nombreux outils permettent d’exporter vers le format DDS.Many tools export to the DDS format. Si vous n’avez pas d’outil pour exporter votre texture dans ce format, envisagez d’en créer un.If you don't have a tool to export your texture to this format, consider creating one. Pour plus de détails sur le format DDS et pour savoir comment l’utiliser dans votre code, voir le Guide de programmation pour DDS.For more detail on the DDS format and how to work with it in your code, read Programming Guide for DDS. Dans notre exemple, nous allons utiliser DDS.In our example, we'll use DDS.

Comme avec d’autres types de ressources, vous lisez les données dans un fichier sous forme de flux d’octets.As with other resource types, you read the data from a file as a stream of bytes. Une fois votre tâche de chargement terminée, l’appel lambda exécute du code (la méthode CreateTexture) pour traiter le flux d’octets dans un format utilisable par Direct3D.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
            );
    });
}

Dans l’extrait de code précédent, l’appel lambda vérifie si le nom du fichier a une extension dds.In the previous snippet, the lambda checks to see if the filename has an extension of "dds". Dans ce cas, vous supposez qu’il s’agit d’une texture DDS.If it does, you assume that it is a DDS texture. Sinon, utilisez les API WIC (Windows Imaging Component) pour découvrir le format et décoder les données sous forme de bitmap.If not, well, use the Windows Imaging Component (WIC) APIs to discover the format and decode the data as a bitmap. Quelle que soit la méthode utilisée, le résultat est une image bitmap Texture2D (ou une erreur).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();
    }
}

Une fois ce code terminé, vous disposez d’un Texture2D en mémoire, chargé à partir d’un fichier image.When this code completes, you have a Texture2D in memory, loaded from an image file. À l’instar des maillages, vous en avez probablement beaucoup dans votre jeu et dans n’importe quelle scène donnée.As with meshes, you probably have a lot of them in your game and in any given scene. Envisagez de créer des caches pour les textures auxquelles le jeu accède régulièrement par scène ou par niveau, plutôt que toutes les charger au démarrage du jeu ou du niveau.Consider creating caches for regularly accessed textures per-scene or per-level, rather than loading them all when the game or level starts.

(Pour explorer dans son intégralité la méthode CreateDDSTextureFromMemory appelée dans l’exemple ci-dessus, voir Code complet de DDSTextureLoader.)(The CreateDDSTextureFromMemory method called in the above sample can be explored in full in Complete code for DDSTextureLoader.)

En outre, des textures individuelles ou « peau » peuvent être mappées sur des surfaces ou des polygones de maillage spécifiques.Also, individual textures or texture "skins" may map to specific mesh polygons or surfaces. Ces données de mappage sont généralement exportées par l’outil que l’artiste ou le graphiste a utilisé pour créer le modèle et les textures.This mapping data is usually exported by the tool an artist or designer used to create the model and the textures. Assurez-vous que vous capturez ces informations aussi lorsque vous chargez les données exportées, car vous les utiliserez pour mapper les textures correctes sur les surfaces correspondantes lorsque vous effectuez l’ombrage de fragments.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.

Chargement des nuanceursLoading shaders

Les nuanceurs sont des fichiers HLSL (High Level Shader Language) compilés qui sont chargés en mémoire et appelés à des étapes spécifiques du pipeline graphique.Shaders are compiled High Level Shader Language (HLSL) files that are loaded into memory and invoked at specific stages of the graphics pipeline. Les nuanceurs essentiels les plus courants sont les nuanceurs de vertex et les nuanceurs de pixels, qui traitent les vertex particuliers de votre maillage et les pixels dans les fenêtres d’affichage de la scène, respectivement.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. Le code HLSL est exécuté pour transformer la géométrie, appliquer des effets de lumière et des textures et effectuer le post-traitement sur la scène rendue.The HLSL code is executed to transform the geometry, apply lighting effects and textures, and perform post-processing on the rendered scene.

Un jeu Direct3D peut avoir plusieurs nuanceurs différents, chacun d’eux compilé dans un fichier .cso (Compiled Shader Object) distinct.A Direct3D game can have a number of different shaders, each one compiled into a separate CSO (Compiled Shader Object, .cso) file. Normalement, vous n’en avez pas une telle quantité pour qu’il faille les charger dynamiquement ; dans la plupart des cas, vous pouvez simplement les charger quand le jeu commence, ou par niveau (par exemple, un nuanceur pour les effets de pluie).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).

Le code de la classe BasicLoader fournit plusieurs surcharges pour différents nuanceurs, y compris des nuanceurs de vertex, de pixels, de géométrie et de coque.The code in the BasicLoader class provides a number of overloads for different shaders, including vertex, geometry, pixel, and hull shaders. Le code ci-dessous traite de nuanceurs de pixels par exemple.The code below covers pixel shaders as an example. (Vous pouvez consulter l’intégralité du code dans Code complet de 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);
    });
}

Dans cet exemple, vous utilisez l’instance BasicReaderWriter (m _ BasicReaderWriter) pour lire le fichier d’objet de nuanceur compilé (. CSO) fourni en tant que flux d’octets.In this example, you use the BasicReaderWriter instance (m_basicReaderWriter) to read in the supplied compiled shader object (.cso) file as a byte stream. Une fois cette tâche terminée, la lambda appelle ID3D11Device::CreatePixelShader avec les données d’octets chargées à partir du fichier.Once that task completes, the lambda calls ID3D11Device::CreatePixelShader with the byte data loaded from the file. Votre rappel doit définir un indicateur signalant que le chargement a réussi, et votre code doit vérifier cet indicateur avant l’exécution du nuanceur.Your callback must set some flag indicating that the load was successful, and your code must check this flag before running the shader.

Les nuanceurs de vertex sont un peu plus complexes.Vertex shaders are bit more complex. Pour un nuanceur de vertex, vous chargez également un schéma d’entrée distinct qui définit les données de vertex.For a vertex shader, you also load a separate input layout that defines the vertex data. Le code suivant permet de charger de façon asynchrone un nuanceur de vertex, ainsi qu’un schéma d’entrée de vertex personnalisé.The following code can be used to asynchronously load a vertex shader along with a custom vertex input layout. N’oubliez pas que les informations de vertex que vous chargez à partir de vos maillages peuvent être correctement représentées par ce schéma d’entrée !Be sure that the vertex information that you load from your meshes can be correctly represented by this input layout!

Nous allons créer le schéma d’entrée avant de charger le nuanceur de vertex.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);
    }
}

Dans ce schéma particulier, le nuanceur de vertex traite les données suivantes de chaque vertex :In this particular layout, each vertex has the following data processed by the vertex shader:

  • une position de coordonnées 3D (x, y, z) dans l’espace de coordonnées du modèle, représentée par un trio de valeurs à virgule flottante 32 bits ;A 3D coordinate position (x, y, z) in the model's coordinate space, represented as a trio of 32-bit floating point values.
  • un vecteur normal pour le vertex, également représenté par trois valeurs à virgule flottante 32 bits ;A normal vector for the vertex, also represented as three 32-bit floating point values.
  • une valeur de coordonnées de texture 2D (u, v) transformée, représentée par une paire de valeurs flottantes 32 bits.A transformed 2D texture coordinate value (u, v) , represented as a pair of 32-bit floating values.

Ces éléments d’entrée par vertex sont appelés sémantique HLSL ; il s’agit d’ensembles de registres définis, utilisés pour transmettre des données en direction et à partir de votre objet nuanceur compilé.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. Votre pipeline exécute le nuanceur de vertex une fois pour chaque vertex du maillage que vous avez chargé.Your pipeline runs the vertex shader once for every vertex in the mesh that you've loaded. La sémantique définit l’entrée (et la sortie) du nuanceur de vertex pour son exécution et fournit ces données pour vos calculs par vertex dans le code HLSL de votre nuanceur.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.

Maintenant, chargez l’objet nuanceur de vertex.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);   
        }
    });
}

Dans ce code, une fois que vous avez lu les données d’octet pour le fichier CSO du nuanceur vertex, vous créez le nuanceur vertex en appelant ID3D11Device :: CreateVertexShader.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. Après cela, vous créez votre schéma d’entrée pour le nuanceur dans la même lambda.After that, you create your input layout for the shader in the same lambda.

D’autres types de nuanceurs, comme les nuanceurs de coque et de géométrie, peuvent également nécessiter une configuration spécifique.Other shader types, such as hull and geometry shaders, can also require specific configuration. Le code complet d’une variété de méthodes de chargement de nuanceur est fourni dans Code complet de BasicLoader et dans l’exemple de chargement de ressources Direct3D.Complete code for a variety of shader loading methods is provided in Complete code for BasicLoader and in the Direct3D resource loading sample.

RemarquesRemarks

À ce stade, vous devez savoir comment créer ou modifier les méthodes de chargement asynchrone des ressources et éléments multimédias de jeu communs, tels que les maillages, les textures et les nuanceurs compilés.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.