`
xuela_net
  • 浏览: 494386 次
文章分类
社区版块
存档分类
最新评论

Windows 8 Directx 开发学习笔记(十)纹理贴图实现旋转的木箱

 
阅读更多

纹理贴图映射(texturemapping)是可以显著提高场景细节和真实感的一种技术,基本原理是将图像数据映射到3D三角形表面(之前的文章提到过,三维模型其实是由很多个三角形拼接而成)。当使用纹理资源时,只要将每个3D三角形与纹理资源上的三角形对应,就可以实现贴图效果。如图1,有一个立方体模型和纹理贴图,将立方体上的点与纹理贴图上的点对应,就像给一个没有颜色的正方体贴一层木纹包装纸。


Direct3D的纹理坐标系由表示图像水平方向的u轴和表示图像垂直方向的v轴组成。坐标 (u,v) 指定了纹理上的一个元素,该元素称为纹理元素(texture element),其中 0≤u,v≤1。将规范化坐标区间设为[0,1]是因为这样可以使Direct3D拥有一个独立于纹理尺寸的坐标空间。即无论纹理的实际尺寸是 256×256还是512×512,(0.5,0.5)永远表示中间的纹理元素。

另外还有一个问题,纹理资源不管多精细都是由离散的数据点组成,如果指定的纹理坐标(u,v)与任何一个纹理元素点都不对应时该怎么办?如图1中的立方体。假设木头纹理的分辨率为 512×512,显示器的分辨率为 1024×1024,当观察点逐渐靠近立方体,立方体会被放大,甚至能盖住整个屏幕。这时就需要用很少的纹理元素来覆盖很多的像素,称为倍增。与倍增相反的问题是缩减,要用很多的纹理元素来覆盖很少的像素。DirectX为解决这些问题定义了多种过滤器,如点过滤和线性过滤。使用时只要对应好顶点和其纹理坐标,过滤器就能通过插值或抽取估计顶点之间每个像素的颜色。

下面就来实现纹理贴图映射。

使用模版新建Direct3D立方体项目。首先依然是更改HLSL代码。

顶点着色器部分:使用纹理资源时不需要指定颜色,所以用这部分空间存储顶点的法向量,用于计算光照效果。添加纹理坐标成员,它与3D顶点坐标对应。这样,每3个顶点构成的3D三角形在纹理空间中都会有一个对应的2D纹理三角形。代码如下:

cbuffer ModelViewProjectionConstantBuffer : register(b0)
{
    matrix model;
    matrix view;
    matrix projection;
};
 
struct VertexShaderInput
{
    float3 pos : POSITION;
    float3 normal : NORMAL;
    float2 tex : TEXCOOD;
};
 
struct VertexShaderOutput
{
    float4 pos : SV_POSITION;
    float3 normal : NORMAL;
    float2 tex : TEXCOOD;
};
 
VertexShaderOutput main(VertexShaderInput input)
{
    VertexShaderOutputoutput;
    float4 pos = float4(input.pos, 1.0f);
 
    // 转换坐标到投影空间
    pos = mul(pos,model);
    pos = mul(pos,view);
    pos = mul(pos,projection);
    output.pos =pos;
 
    // 转换法向量到世界空间用于光照计算
    float4 normal = float4(normalize(input.normal),0.0f);
    normal =mul(normal, model);
    output.normal =normalize(normal.xyz);
 
    // 纹理坐标不需要改动
    output.tex =input.tex;
 
    return output;
}
像素着色器部分:输入结构体定义必须和顶点着色器的输出结构体格式一致。还要添加纹理资源,并在main方法中添加简单漫反射光的计算。另外,使用过滤器访问纹理资源需要通过采样器。代码如下:

SamplerState samplerLinear : register(s0);
Texture2D woodDiffuse : register(t0);
 
struct PixelShaderInput
{
    float4 pos : SV_POSITION;
    float3 normal : NORMAL;
    float2 tex : TEXCOOD;
};
 
float4 main(PixelShaderInput input) : SV_TARGET
{
    float3 lightDirection =normalize(float3(1, -1, 0));
    float4 texelColor = woodDiffuse.Sample(samplerLinear,input.tex);
 
    // 计算简单漫反射
    float lightMagnitude =0.8f * saturate(dot(input.normal, -lightDirection)) + 0.2f;
   
    return texelColor *lightMagnitude;
}

HLSL代码完成后就要修改主程序。在Windows 8 Store App中载入纹理资源可以使用WICTextureLoader。它支持读取多种图片资源(jpg、png)创建纹理。使用时将.cpp和.h文件加入项目即可。如果想载入DDS格式的资源可以看DirectXTex的说明。

完成后在CubeRenderer类里更改结构体定义,与着色器对应:

struct VertexPositionColor
{
    DirectX::XMFLOAT3 pos;
    DirectX::XMFLOAT3 normal;
    DirectX::XMFLOAT2 tex;
};

然后添加三个成员以使用纹理资源和采样器:

ID3D11Resource* tex;
Microsoft::WRL::ComPtr<ID3D11ShaderResourceView> m_WoodSRV;
Microsoft::WRL::ComPtr<ID3D11SamplerState> m_Sampler;

接着就开始修改初始化部分。在CreateDeviceResources方法中在载入顶点着色器和像素着色器之后添加createWoodTexTask,用于初始化纹理资源和采样器:

auto createWoodTexTask = (createPSTask &&createVSTask).then([this] () {
       DX::ThrowIfFailed(
           CreateWICTextureFromFile(
              m_d3dDevice.Get(),
              m_d3dContext.Get(),
              L"wood.jpg",
              &tex,
              m_WoodSRV.GetAddressOf()
              )
           );
 
       D3D11_SAMPLER_DESC samplerDesc;
       samplerDesc.Filter= D3D11_FILTER_MIN_MAG_MIP_LINEAR;
       samplerDesc.AddressU= D3D11_TEXTURE_ADDRESS_WRAP;
       samplerDesc.AddressV= D3D11_TEXTURE_ADDRESS_WRAP;
       samplerDesc.AddressW= D3D11_TEXTURE_ADDRESS_WRAP;
       samplerDesc.MipLODBias= 0;
       samplerDesc.MaxAnisotropy= 1;
       samplerDesc.ComparisonFunc= D3D11_COMPARISON_NEVER;
       samplerDesc.BorderColor[0 ] = 1.0f;
       samplerDesc.BorderColor[1 ] = 1.0f;
       samplerDesc.BorderColor[2 ] = 1.0f;
       samplerDesc.BorderColor[3 ] = 1.0f;
       samplerDesc.MinLOD= -3.402823466e+38F; // -FLT_MAX
       samplerDesc.MaxLOD= 3.402823466e+38F; // FLT_MAX
 
       DX::ThrowIfFailed(
           m_d3dDevice->CreateSamplerState(
              &samplerDesc,
              m_Sampler.GetAddressOf()
              )
           );
    });

接下来自然是设置顶点缓冲区和索引数组。顶点的数据需要根据结构体的变化修改:

auto createCubeTask = createWoodTexTask.then([this] () {
       VertexPositionColor cubeVertices[] =
       {
           // +x
           {XMFLOAT3( 0.5f,  0.5f, -0.5f), XMFLOAT3( 1.0f, 0.0f, 0.0f),XMFLOAT2(0.0f, 0.0f), },
           {XMFLOAT3( 0.5f,  0.5f, 0.5f), XMFLOAT3( 1.0f, 0.0f, 0.0f), XMFLOAT2(1.0f, 0.0f), },
           {XMFLOAT3( 0.5f, -0.5f,  0.5f), XMFLOAT3( 1.0f, 0.0f, 0.0f),XMFLOAT2(1.0f, 1.0f), },
           {XMFLOAT3( 0.5f, -0.5f,-0.5f), XMFLOAT3( 1.0f, 0.0f, 0.0f), XMFLOAT2(0.0f, 1.0f), },
           // -x
           {XMFLOAT3(-0.5f,  0.5f, -0.5f), XMFLOAT3(-1.0f, 0.0f, 0.0f),XMFLOAT2(0.0f, 0.0f), },
           {XMFLOAT3(-0.5f,  0.5f, 0.5f), XMFLOAT3(-1.0f, 0.0f, 0.0f), XMFLOAT2(1.0f, 0.0f), },
           {XMFLOAT3(-0.5f, -0.5f,  0.5f), XMFLOAT3(-1.0f, 0.0f, 0.0f),XMFLOAT2(1.0f, 1.0f), },
           {XMFLOAT3(-0.5f, -0.5f,-0.5f), XMFLOAT3(-1.0f, 0.0f, 0.0f), XMFLOAT2(0.0f, 1.0f), },
           // +y
           {XMFLOAT3(-0.5f,  0.5f, 0.5f), XMFLOAT3( 0.0f, 1.0f, 0.0f), XMFLOAT2(1.0f, 0.0f), },
           {XMFLOAT3( 0.5f,  0.5f, 0.5f), XMFLOAT3( 0.0f, 1.0f, 0.0f), XMFLOAT2(0.0f, 1.0f), },
           {XMFLOAT3( 0.5f,  0.5f, -0.5f), XMFLOAT3( 0.0f, 1.0f, 0.0f),XMFLOAT2(1.0f, 1.0f), },
           {XMFLOAT3(-0.5f,  0.5f, -0.5f), XMFLOAT3( 0.0f, 1.0f, 0.0f),XMFLOAT2(0.0f, 0.0f), },
           // -y
           {XMFLOAT3(-0.5f, -0.5f,  0.5f), XMFLOAT3( 0.0f, -1.0f,0.0f), XMFLOAT2(1.0f, 0.0f), },
           {XMFLOAT3( 0.5f, -0.5f,  0.5f), XMFLOAT3( 0.0f, -1.0f,0.0f), XMFLOAT2(0.0f, 1.0f), },
           {XMFLOAT3( 0.5f, -0.5f,-0.5f), XMFLOAT3( 0.0f, -1.0f, 0.0f), XMFLOAT2(1.0f, 1.0f), },
           {XMFLOAT3(-0.5f, -0.5f,-0.5f), XMFLOAT3( 0.0f, -1.0f, 0.0f), XMFLOAT2(0.0f, 0.0f), },
           // +z
           {XMFLOAT3(-0.5f,  0.5f, 0.5f), XMFLOAT3( 0.0f, 0.0f, 1.0f), XMFLOAT2(0.0f, 0.0f), },
           {XMFLOAT3( 0.5f,  0.5f, 0.5f), XMFLOAT3( 0.0f, 0.0f, 1.0f), XMFLOAT2(1.0f, 0.0f), },
           {XMFLOAT3( 0.5f, -0.5f,  0.5f), XMFLOAT3( 0.0f, 0.0f, 1.0f),XMFLOAT2(1.0f, 1.0f), },
           {XMFLOAT3(-0.5f, -0.5f,  0.5f), XMFLOAT3( 0.0f, 0.0f, 1.0f),XMFLOAT2(0.0f, 1.0f), },
           // -z
           {XMFLOAT3(-0.5f,  0.5f, -0.5f), XMFLOAT3( 0.0f, 0.0f,-1.0f), XMFLOAT2(1.0f, 0.0f), },
           {XMFLOAT3( 0.5f,  0.5f, -0.5f), XMFLOAT3( 0.0f, 0.0f,-1.0f), XMFLOAT2(0.0f, 0.0f), },
           {XMFLOAT3( 0.5f, -0.5f,-0.5f), XMFLOAT3( 0.0f, 0.0f, -1.0f), XMFLOAT2(0.0f, 1.0f), },
           {XMFLOAT3(-0.5f, -0.5f,-0.5f), XMFLOAT3( 0.0f, 0.0f, -1.0f), XMFLOAT2(1.0f, 1.0f), },
       };
 
       D3D11_SUBRESOURCE_DATA vertexBufferData ={0};
       vertexBufferData.pSysMem= cubeVertices;
       vertexBufferData.SysMemPitch= 0;
       vertexBufferData.SysMemSlicePitch= 0;
       CD3D11_BUFFER_DESC vertexBufferDesc(sizeof(cubeVertices), D3D11_BIND_VERTEX_BUFFER);
       DX::ThrowIfFailed(
           m_d3dDevice->CreateBuffer(
           &vertexBufferDesc,
           &vertexBufferData,
           &m_vertexBuffer
           )
           );
 
       unsigned short cubeIndices[] =
       {
           0,2,1, // +x
           0,3,2,
 
           4,5,6, // -x
           4,6,7,
 
           8,10,9, // +y
           8,11,10,
 
           12,13,14,// -y
           12,14,15,
 
           16,17,18,// +z
           16,18,19,
 
           20,22,21,// -z
           20,23,22,
       };
 
       m_indexCount= ARRAYSIZE(cubeIndices);
 
       D3D11_SUBRESOURCE_DATA indexBufferData ={0};
       indexBufferData.pSysMem= cubeIndices;
       indexBufferData.SysMemPitch= 0;
       indexBufferData.SysMemSlicePitch= 0;
       CD3D11_BUFFER_DESC indexBufferDesc(sizeof(cubeIndices), D3D11_BIND_INDEX_BUFFER);
       DX::ThrowIfFailed(
           m_d3dDevice->CreateBuffer(
           &indexBufferDesc,
           &indexBufferData,
           &m_indexBuffer
           )
           );
    });

完成后就可以在渲染时使用纹理资源了。在Render方法里添加以下代码使纹理资源生效:

// 设置纹理资源
m_d3dContext->PSSetShaderResources(
       0,
       1,
       m_WoodSRV.GetAddressOf()
       );
 
// 设置纹理采样器
m_d3dContext->PSSetSamplers(
       0,
       1,
       m_Sampler.GetAddressOf()
       );

运行后效果如下图:

本篇文章的源代码:

DirectX_CubeTexture

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics