Windows 八 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
- 2楼gaolongtou前天 19:34
- 初次看到这个东东。
- 1楼qxe888前天 14:15
- 佩服