braynzarsoft-D3D11
+ -

11.纹理

2024-06-12 10 0

本文实现从文件加载纹理并将其映射到几何图形!

源码下载地址:

介绍

在这里,我们将学习如何将纹理映射到我们的对象。
在 Direct3D 中,我们使用 2D (u,v) 坐标系将纹理映射到对象上。
u 轴水平延伸到图像,v 轴垂直延伸,其中 u 为 0-1(0 表示图像长度的起点,1 表示图像长度的终点)。因此,即使实际图像长度为 256 像素,图像水平长度的一半也是 0.5。
143709754767

现在,如果我们将 u 和 v 的值改为大于 1,会发生什么?比如说 2?你猜对了!它将重复纹理,如下所示。
143721801863

变量声明

第一个新接口是一个对象,它将保存我们从文件加载的纹理。第二个接口将保存我们的采样器状态信息。

ID3D11ShaderResourceView* CubesTexture;
ID3D11SamplerState* CubesTexSamplerState;

顶点结构/输入布局

我们看一下我们的顶点结构。我们删除了颜色成员,并用纹理坐标成员替换它。2D 纹理的纹理坐标只需要 u 和 v 值,如果对于使用 3D 纹理作为天空图,它使用额外的“w”值。
我们还修改了输入布局,为纹理坐标包含两个浮点元素,它替换了我们的颜色元素。

struct Vertex    //Overloaded Vertex Structure
{
    Vertex(){}
    Vertex(float x, float y, float z,
        float u, float v)
        : pos(x,y,z), texCoord(u, v){}

    XMFLOAT3 pos;
    XMFLOAT2 texCoord;
};

D3D11_INPUT_ELEMENT_DESC layout[] =
{
    { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },  
    { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },  
};

顶点结构/缓冲区、索引列表

如果我们不为每个三角形顶点单独设置纹理坐标,则只有顶部和底部可能被正确映射,这就是为什么我们为立方体中的每个三角形添加顶点,以便我们可以设置每个顶点的纹理坐标。您可以尝试仅使用我们在上一课中使用的 8 个顶点来设置纹理坐标,但就像我说的那样,纹理将无法在所有六个侧面正确映射。

由于我们添加了更多顶点,我们还需要更新顶点缓冲区以保存 24 个顶点,而不是之前的 8 个。

        Vertex v[] =
        {
            // Front Face
            Vertex(-1.0f, -1.0f, -1.0f, 0.0f, 1.0f),
            Vertex(-1.0f,  1.0f, -1.0f, 0.0f, 0.0f),
            Vertex( 1.0f,  1.0f, -1.0f, 1.0f, 0.0f),
            Vertex( 1.0f, -1.0f, -1.0f, 1.0f, 1.0f),

            // Back Face
            Vertex(-1.0f, -1.0f, 1.0f, 1.0f, 1.0f),
            Vertex( 1.0f, -1.0f, 1.0f, 0.0f, 1.0f),
            Vertex( 1.0f,  1.0f, 1.0f, 0.0f, 0.0f),
            Vertex(-1.0f,  1.0f, 1.0f, 1.0f, 0.0f),

            // Top Face
            Vertex(-1.0f, 1.0f, -1.0f, 0.0f, 1.0f),
            Vertex(-1.0f, 1.0f,  1.0f, 0.0f, 0.0f),
            Vertex( 1.0f, 1.0f,  1.0f, 1.0f, 0.0f),
            Vertex( 1.0f, 1.0f, -1.0f, 1.0f, 1.0f),

            // Bottom Face
            Vertex(-1.0f, -1.0f, -1.0f, 1.0f, 1.0f),
            Vertex( 1.0f, -1.0f, -1.0f, 0.0f, 1.0f),
            Vertex( 1.0f, -1.0f,  1.0f, 0.0f, 0.0f),
            Vertex(-1.0f, -1.0f,  1.0f, 1.0f, 0.0f),

            // Left Face
            Vertex(-1.0f, -1.0f,  1.0f, 0.0f, 1.0f),
            Vertex(-1.0f,  1.0f,  1.0f, 0.0f, 0.0f),
            Vertex(-1.0f,  1.0f, -1.0f, 1.0f, 0.0f),
            Vertex(-1.0f, -1.0f, -1.0f, 1.0f, 1.0f),

            // Right Face
            Vertex( 1.0f, -1.0f, -1.0f, 0.0f, 1.0f),
            Vertex( 1.0f,  1.0f, -1.0f, 0.0f, 0.0f),
            Vertex( 1.0f,  1.0f,  1.0f, 1.0f, 0.0f),
            Vertex( 1.0f, -1.0f,  1.0f, 1.0f, 1.0f),
        };

        DWORD indices[] = {
            // Front Face
            0,  1,  2,
            0,  2,  3,

            // Back Face
            4,  5,  6,
            4,  6,  7,

            // Top Face
            8,  9, 10,
            8, 10, 11,

            // Bottom Face
            12, 13, 14,
            12, 14, 15,

            // Left Face
            16, 17, 18,
            16, 18, 19,

            // Right Face
            20, 21, 22,
            20, 22, 23
        };

        D3D11_BUFFER_DESC indexBufferDesc;
        ZeroMemory( &indexBufferDesc, sizeof(indexBufferDesc) );

        indexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
        indexBufferDesc.ByteWidth = sizeof(DWORD) * 12 * 3;
        indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
        indexBufferDesc.CPUAccessFlags = 0;
        indexBufferDesc.MiscFlags = 0;

        D3D11_SUBRESOURCE_DATA iinitData;
        iinitData.pSysMem = indices;

        d3d11Device->CreateBuffer(&indexBufferDesc, &iinitData, &squareIndexBuffer);
        d3d11DevCon->IASetIndexBuffer( squareIndexBuffer, DXGI_FORMAT_R32_UINT, 0);


        D3D11_BUFFER_DESC vertexBufferDesc;
        ZeroMemory( &vertexBufferDesc, sizeof(vertexBufferDesc) );

        vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
        vertexBufferDesc.ByteWidth = sizeof( Vertex ) * 24;
        vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
        vertexBufferDesc.CPUAccessFlags = 0;
        vertexBufferDesc.MiscFlags = 0;

从文件加载纹理

在我们的初始化场景函数的底部,我们使用函数 D3DX11CreateShaderResourceViewFromFile() 从文件中加载纹理,该函数定义如下:

HRESULT D3DX11CreateShaderResourceViewFromFile(
  __in   ID3D11Device *pDevice,
  __in   LPCTSTR pSrcFile,
  __in   D3DX11_IMAGE_LOAD_INFO *pLoadInfo,
  __in   ID3DX11ThreadPump *pPump,
  __out  ID3D11ShaderResourceView **ppShaderResourceView,
  __out  HRESULT *pHResult
);

其中每个参数的描述如下:

  • pDevice—— 指向我们的 D3D 设备的指针。
  • pSrcFile—— 我们的文件的名称(如果它与 exe 不在同一文件夹中,则为位置)。
  • pLoadInfo - 指向 D3DX11_IMAGE_LOAD_INFO 结构的指针,该结构定义应如何加载纹理。我们可以将其设置为 NULL。
  • pPump - 指向 ID3DX11ThreadPump 接口的指针,仅在我们想要多线程时使用,并且即使在加载此文件时也让程序继续运行。在此处输入 NULL 会使此函数仅在完成时返回。
  • ppShaderResourceView - 这是一个指向着色器资源视图 (ID3D11ShaderResourceView) 的指针,它将保存来自此纹理的数据。
  • pHResult - 这是返回的指针,用于存储此函数的结果。我们之前讨论过 HRESULT。
hr = D3DX11CreateShaderResourceViewFromFile( d3d11Device, L"braynzar.jpg",
    NULL, NULL, &CubesTexture, NULL );

描述采样状态

这里我们可以描述采样器状态,或者着色器如何渲染纹理。我们创建一个 D3D11_SAMPLER_DESC 对象:

typedef struct D3D11_SAMPLER_DESC {
  D3D11_FILTER               Filter;
  D3D11_TEXTURE_ADDRESS_MODE AddressU;
  D3D11_TEXTURE_ADDRESS_MODE AddressV;
  D3D11_TEXTURE_ADDRESS_MODE AddressW;
  FLOAT                      MipLODBias;
  UINT                       MaxAnisotropy;
  D3D11_COMPARISON_FUNC      ComparisonFunc;
  FLOAT                      BorderColor[4];
  FLOAT                      MinLOD;
  FLOAT                      MaxLOD;
} D3D11_SAMPLER_DESC;

其中每个成员的描述如下:

  • Filter - 一个 D3D11_FILTER 枚举类型,描述要使用的过滤方法。
  • AddressU - 一个 D3D11_TEXTURE_ADDRESS_MODE 枚举类型,描述如果 u 值大于 1 或小于 0 时该怎么做。
  • AddressV - 一个 D3D11_TEXTURE_ADDRESS_MODE 枚举类型,描述如果 v 值大于 1 或小于 0 时该怎么做。
  • AddressW - 一个 D3D11_TEXTURE_ADDRESS_MODE 枚举类型,描述如果 w 值大于 1 或小于 0 时该怎么做。
  • MipLODBias - 与计算出的 mipmap 级别的偏移。例如,如果 Direct3D 计算出纹理应在 mipmap 级别 3 处采样,而 MipLODBias 为 2,则该纹理将在 mipmap 级别 5 处采样。

  • MaxAnisotropy - 如果在 Filter 中指定了 D3D11_FILTER_ANISOTROPIC 或 D3D11_FILTER_COMPARISON_ANISOTROPIC,则使用钳制值。有效值介于 1 到 16 之间。

  • ComparisonFunc - 枚举类型 D3D11_COMPARISON_FUNC 。这将比较此纹理的采样 mipmap 数据与另一个 mipmaps 采样数据。

  • BorderColor[4] - 如果为 AddressU、V 或 W 中的任意一个指定了 D3D11_TEXTURE_ADDRESS_BORDER,那么如果 u、v 或 w 大于 1 或小于 0,则这是纹理和三角形边缘之间的空间的颜色。
  • MinLOD - 这是要使用的最低 mipmap 级别,其中 0 表示最详细且最大的级别。
  • MaxLOD - 这是要使用的最大 mipmap 级别,其中 0 表示最详细且最大。要使用所有 mipmap,您需要指定一个非常大的数字,例如 FLT_MAX。

如果任何成员未填写,则将使用默认值:

Filter            MIN_MAG_MIP_LINEAR
AddressU        Clamp
AddressV        Clamp
AddressW        Clamp
MinLOD            -3.402823466e+38F (-FLT_MAX)
MaxLOD            3.402823466e+38F (FLT_MAX)
MipMapLODBias    0.0f
MaxAnisotropy    16
ComparisonFunc    Never
BorderColor        float4(0.0f,0.0f,0.0f,0.0f)

D3D11_SAMPLER_DESC sampDesc;
ZeroMemory( &sampDesc, sizeof(sampDesc) );
sampDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
sampDesc.ComparisonFunc = D3D11_COMPARISON_NEVER;
sampDesc.MinLOD = 0;
sampDesc.MaxLOD = D3D11_FLOAT32_MAX;

顶点结构/输入布局

描述完采样器后,我们需要创建它。我们可以通过使用 CreateSamplerState() 方法来实现这一点,其中第一个参数是我们描述的采样器状态,第二个参数是将采样器状态放入的接口。

hr = d3d11Device->CreateSamplerState( &sampDesc, &CubesTexSamplerState );

将采样器状态和纹理发送到着色器

如果我们愿意,我们只需要对使用相同采样器状态和纹理的每组对象将采样器状态和纹理发送到像素着色器一次,但很多时候对象会有多个纹理,因此您无法对每个对象仅设置一次。无论如何,由于 PS 将使用此信息,我们将把它发送到那里。我们可以通过调用方法 ID3D11DeviceContext::PSSetShaderResources() 和 ID3D11DeviceContext::PSSetSamplers() 来执行此操作。

PSSetShaderResources() 方法的第一个参数是我们将着色器发送到的槽号。第二个参数是纹理数组中的元素数。我们将其设置为 1,因为我们只发送一个纹理,但实际上我们可以将一个纹理数组发送到着色器。第三个参数是 ID3D11ShaderResourceView 数组。

PSSetSamplers 类似,只不过我们发送的不是 ID3D11ShaderResourceView 数组,而是 ID3D11SamplerState 数组。不过我们只有一个采样器状态,因此只需发送一个即可。

void DrawScene()
{
    //Clear our backbuffer
    float bgColor[4] = {(0.0f, 0.0f, 0.0f, 0.0f)};
    d3d11DevCon->ClearRenderTargetView(renderTargetView, bgColor);

    //Refresh the Depth/Stencil view
    d3d11DevCon->ClearDepthStencilView(depthStencilView, D3D11_CLEAR_DEPTH|D3D11_CLEAR_STENCIL, 1.0f, 0);

    //Set the WVP matrix and send it to the constant buffer in effect file
    WVP = cube1World * camView * camProjection;
    cbPerObj.WVP = XMMatrixTranspose(WVP);    
    d3d11DevCon->UpdateSubresource( cbPerObjectBuffer, 0, NULL, &cbPerObj, 0, 0 );
    d3d11DevCon->VSSetConstantBuffers( 0, 1, &cbPerObjectBuffer );
    ///////////////**************new**************////////////////////
    d3d11DevCon->PSSetShaderResources( 0, 1, &CubesTexture );
    d3d11DevCon->PSSetSamplers( 0, 1, &CubesTexSamplerState );
    ///////////////**************new**************////////////////////

    //Draw the first cube
    d3d11DevCon->DrawIndexed( 36, 0, 0 );

    WVP = cube2World * camView * camProjection;
    cbPerObj.WVP = XMMatrixTranspose(WVP);    
    d3d11DevCon->UpdateSubresource( cbPerObjectBuffer, 0, NULL, &cbPerObj, 0, 0 );
    d3d11DevCon->VSSetConstantBuffers( 0, 1, &cbPerObjectBuffer );
    ///////////////**************new**************////////////////////
    d3d11DevCon->PSSetShaderResources( 0, 1, &CubesTexture );
    d3d11DevCon->PSSetSamplers( 0, 1, &CubesTexSamplerState );
    ///////////////**************new**************////////////////////

    //Draw the second cube
    d3d11DevCon->DrawIndexed( 36, 0, 0 );

    //Present the backbuffer to the screen
    SwapChain->Present(0, 0);
}

0 篇笔记 写笔记

11.纹理
本文实现从文件加载纹理并将其映射到几何图形!源码下载地址:https://www.braynzarsoft.net/file/11DX11_Lesson_11_Textures_zip.zip介绍在这里,我们将学习如何将纹理映射到我们的对象。在 Direct3D 中,我们使用 2D (u,v......
2.2.2纹理资源
正如我们在最后几节中所看到的,有无数不同的缓冲区资源,以及为各种用途配置它们的许多不同方法。然而,缓冲区只是资源故事的一半。纹理为基于GPU作为渲染处理器的资源提供了不同的概念。“纹理”一词指的是一种在性质上与图像或类似图像的内存资源。这是一个非常松散的定义,因为有一系列不同的纹理类型,具有不同的维......
2D纹理
我们将探索的第二种纹理类型是2D纹理。由于这种纹理类型与标准2D图像布局密切相关,因此它通常是使用最广泛的纹理类型之一。此资源被组织为二维元素网格,其中每个元素都是DXGI_FORMAT枚举的成员。2D纹理的各种子资源形式如图2.31所示。如图2.31所示,这些纹理以与ID纹理类似的方式支持mip贴......
gs_texture纹理
gs_texture结构体是gs_texture_2d和gs_texture_3d的基类。struct gs_texture : gs_obj { gs_texture_type type; //纹理类型:2d,2d(cube),3d uint32_t levels; //纹理级......
gs_texture_2d
按照上节,gs_texture_2d有两种类型的纹理,分别为2D和2D-CUBE纹理类型。其从数据组织上来讲,一个是一拖一,一个是一拖6。struct gs_texture_2d : gs_texture { ComPtr texture; ......
gs_texture_2d成员函数
2d纹理可创建的类型是多样的。有是2d的,也有的是2d-cube的。有的是需要绑定的是渲染目标视图,有的是渲染着色器资源视图。其它的一些配置参数多样。 InitTexture(data);//根据纹理数据创建纹理 InitResourceView();//创建着色器资源视图 ......
6.4.2 HLSL纹理缓冲区
由于常量缓冲区针对小尺寸和统一的访问模式进行了优化,因此在某些情况下,它们可能具有未知期望的性能特性。一种常见的情况是用于蒙皮的骨骼矩阵阵列。在这种情况下,每个顶点都包含一个或多个索引,指示阵列中的哪个骨骼应用于变换位置和法线。在许多硬件类型上,这种访问模式将导致执行着色器程序的线程序列化对骨骼数据......
作者信息
站长漫谈
取消
感谢您的支持,我会继续努力的!
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

您的支持,是我们前进的动力!