Windows 8 Directx 开发学习笔记(十一)地形纹理贴图
来源:互联网 发布:淘宝抢购秒杀怎么抢手机软件 编辑:程序博客网 时间:2024/05/14 03:28
前一篇实现木箱贴图时,木箱的六个面都正好用一整张纹理图,即六个面的纹理坐标均在[0,1]内。然而在为比较大的模型贴图时,像山峰河谷模型,如果只用一张纹理图,那么每个三角形只得到几个纹理元素,无法为提供足够高的分辨率。这时可以在模型表面上平铺纹理贴图,像给墙面贴磁砖一样,只需要知道一个单位的贴图,就能铺满整个表面,从而获得较高的分辨率。实现起来其实很简单,只要变换纹理坐标的范围,同时将纹理寻址模式设置为重复即可。
纹理坐标范围变换有两种方式,很简单,如图1:
其中x,y是原始纹理坐标,为了能与4*4的变换矩阵相乘,将其扩展为4元向量。等号左边是一种变换方式,使用变换矩阵进行纹理坐标变换,可用GPU计算。等号右边是第二种变换方式,直接生成变换后的纹理坐标,由CPU计算。本文选择的是第一种变换方式。
下面就开始给学习笔记(九)实现的山峰和水面模型贴图。
依然是先修改HLSL代码。在顶点着色器的输入输出结构中添加用于存储纹理坐标的成员tex。并在ModelViewProjectionConstantBuffer里添加texTransform成员,用于存储缩放和平移变换矩阵。
struct VertexShaderInput{ float3 pos : POSITION; float3 normal : NORMAL; float2 tex : TEXCOOD;}; struct VertexShaderOutput{ float4 posH : SV_POSITION; float3 posW : POSITION; float3 normal : NORMAL; float2 tex : TEXCOOD;}; cbuffer ModelViewProjectionConstantBuffer : register(b0){ matrix model; matrix view; matrix projection; matrix texTransform;};
变换纹理坐标时只要在main方法中添加下面的语句即可:
// 纹理坐标变换output.tex = mul(float4(input.tex, 0.0f, 1.0f),texTransform).xy;
在像素着色器中要定义采样器和纹理资源,并更改输入结构体,与顶点着色器的输出对应。
SamplerState samplerLinear : register(s0);Texture2D texDiffuse : register(t0); struct PixelShaderInput{ float4 posH : SV_POSITION; float3 posW : POSITION; float3 normal : NORMAL; float2 tex : TEXCOOD;};
为了减少更改,在main方法中还是使用Material结构进行光照计算,只是Diffuse成员由纹理采样获得。
Material textureMat = gMaterial;textureMat.Diffuse = texDiffuse.Sample(samplerLinear,input.tex);
HLSL代码修改至此完成,接下来就要修改C++代码。
结构先行,修改Directl3DBase.h中的顶点结构体和常量缓冲区定义,保证它们的结构和顶点着色器中定义的一致。
struct VertexPosition{ DirectX::XMFLOAT3 pos; DirectX::XMFLOAT3 normal; DirectX::XMFLOAT2 tex;};struct ModelViewProjectionConstantBuffer{ DirectX::XMFLOAT4X4 model; DirectX::XMFLOAT4X4 view; DirectX::XMFLOAT4X4 projection; DirectX::XMFLOAT4X4 gTexTransform;};
由于顶点结构体改变,HillModel和WaterModel里顶点初始化代码需要修改。HillModel的Initialize方法里,顶点初始化代码修改如下:
const float dx = 1.0f;const float du = 1.0f/(xRange-1);const float dv = 1.0f/(zRange-1); VertexPosition *Vertices = new VertexPosition[xRange*zRange]; for(int row=0; row<zRange; ++row){ float zPos = row*dx; for(int col=0;col<xRange; ++col) { float xPos = col*dx; float yPos = 0.3f *(zPos*sinf(0.1f*xPos) + xPos*cosf(0.1f*zPos)); Vertices[xRange*row+ col].pos = XMFLOAT3(xPos, yPos, zPos); Vertices[xRange*row+ col].normal = GetHillNormal(xPos, zPos); Vertices[xRange*row+ col].tex.x = row*du; Vertices[xRange*row+ col].tex.y = col*dv; }}
du和dv可保证生成的纹理坐标正好在[0,1]范围内,目的是方便理解缩放比例,并不是必须的。WaterModel生成纹理坐标的过程与上面类似,是在Update方法中进行的。
// 更新顶点缓冲区const float du = 1.0f/xRange;const float dv = 1.0f/zRange; for(uint32 i = 0; i < m_vertexCount; ++i){ vertex[i].pos =mCurrSolution[i]; vertex[i].tex.x= (i/xRange)*du; vertex[i].tex.y= (i%zRange)*dv; vertex[i].normal= mNormal[i];}
这里虽然每次更新都要重新计算一样的纹理坐标,效率很低,不过鉴于规模小,方便理解就直接修改了。
完成以上工作后就可以开始载入纹理。与木箱贴图一样,向工程中添加WICTextureLoader的头文件和源文件。然后在Renderer类里添加以下成员,保存两种纹理资源,分别代表陆地和水面。
ID3D11Resource* tex;Microsoft::WRL::ComPtr<ID3D11ShaderResourceView> m_LandSRV;Microsoft::WRL::ComPtr<ID3D11ShaderResourceView> m_WaterSRV;Microsoft::WRL::ComPtr<ID3D11SamplerState> m_Sampler;
然后在CreateDeviceResources方法中载入并初始化纹理资源,初始化采样器,当然还要修改输入布局:
const D3D11_INPUT_ELEMENT_DESC vertexDesc[] = { { "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 }, { "TEXCOOD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0 }, }; …… auto createTexTask = (createPSTask &&createVSTask).then([this] () { // 载入纹理资源 DX::ThrowIfFailed( CreateWICTextureFromFile( m_d3dDevice.Get(), m_d3dContext.Get(), L"Texture/grass.jpg", &tex, m_LandSRV.GetAddressOf() ) ); DX::ThrowIfFailed( CreateWICTextureFromFile( m_d3dDevice.Get(), m_d3dContext.Get(), L"Texture/water.jpg", &tex, m_WaterSRV.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.MinLOD= -3.402823466e+38F; // -FLT_MAX samplerDesc.MaxLOD= 3.402823466e+38F; // FLT_MAX DX::ThrowIfFailed( m_d3dDevice->CreateSamplerState( &samplerDesc, m_Sampler.GetAddressOf() ) );});
初始化完成后,就能在Render方法设置并使用纹理资源。
// 设置纹理采样器m_d3dContext->PSSetSamplers( 0, 1, m_Sampler.GetAddressOf() ); // 设置陆地纹理m_d3dContext->PSSetShaderResources( 0, 1, m_LandSRV.GetAddressOf() ); // 设置陆地纹理缩放XMMATRIX landScale = XMMatrixScaling(5.0f, 5.0f, 0.0f);XMStoreFloat4x4(&m_constantBufferData.gTexTransform,XMMatrixTranspose(landScale)); // 设置陆地材质m_constantLightBufferData.gMaterial= m_landMat; …… // 设置水面纹理m_d3dContext->PSSetShaderResources( 0, 1, m_WaterSRV.GetAddressOf() ); // 设置水面纹理缩放XMMATRIX waterScale = XMMatrixScaling(5.0f, 5.0f, 0.0f);XMStoreFloat4x4(&m_constantBufferData.gTexTransform,XMMatrixTranspose(waterScale)); // 设置水面材质m_constantLightBufferData.gMaterial = m_wavesMat;
DirectX里包含多个生成变换矩阵的方法,如上面的XMMatrixScaling方法生成缩放变换矩阵,后面将用到的XMMatrixTranslation方法可以生成平移变换矩阵。另外注意每次设置好纹理材质后,不但要调用UpdateSubresource方法更新m_constantLightBuffer,还要用它更新m_constantBuffer,因为纹理缩放矩阵是ModelViewProjectionConstantBuffer的成员。
图1载入的是grass纹理:
图2载入的是rock纹理:
以上两个模型中,水面纹理是不变的,而且没有流动的效果。简单的流动效果可以通过纹理坐标随时间偏移实现。
在Renderer类里添加偏移量成员:
XMFLOAT2 m_waterTexOffset;
在Update方法里添加代码,让偏移量随时间变化。
// 更新纹理偏移量m_waterTexOffset.y += 0.1f*timeDelta;m_waterTexOffset.x += 0.1f*timeDelta;
然后在Render方法中使用偏移量生成平移变换矩阵,并与缩放变换矩阵相乘,这样水面纹理坐标就会保持平铺效果,并随时间平移,实现类似流动的效果:
// 设置水面纹理缩放和偏移量XMMATRIX waterScale = XMMatrixScaling(5.0f, 3.0f, 0.0f);XMMATRIX waterOffset = XMMatrixTranslation(m_waterTexOffset.x,m_waterTexOffset.y, 0.0f);XMStoreFloat4x4(&m_constantBufferData.gTexTransform,XMMatrixTranspose(waterScale*waterOffset));
本篇文章的源代码
Direct3DApp_HillWaveTexture
- Windows 8 Directx 开发学习笔记(十一)地形纹理贴图
- Windows 8 Directx 开发学习笔记(十)纹理贴图实现旋转的木箱
- Windows 8 DirectX 开发学习笔记(十五)使用Billboard实现树木贴图
- Windows 8 DirectX 开发学习笔记(十六)使用Terragen生成自然环境贴图
- Windows 8 Directx 开发学习笔记(八)要有光
- 如何自制地形纹理贴图
- DOTA2地形纹理贴图整理
- DirectX学习笔记(十四):三维地形系统的实现
- openGL之纹理贴图---openGL学习笔记(十三)
- Sandy引擎学习笔记:纹理贴图
- Opengl ES 学习笔记 3:纹理贴图
- OpenGL学习笔记之纹理贴图
- OpenGL学习笔记——纹理贴图
- OpenGL学习笔记——纹理贴图
- Windows 8 Directx开发学习笔记(一)应用基本框架
- Windows 8 Directx 开发学习笔记(二)建立模型及初始化设备
- Windows 8 Directx 开发学习笔记(三)摄像机设置及控制正方体旋转
- Windows 8 Directx 开发学习笔记(四)示例程序小结
- 6
- 排序算法之冒泡排序(JAVA)
- 如果
- android组件介绍:textview、button、radiobutton、checkbox、chronometer、spinner、progressbar、seekbar等
- C++调用Python(3)
- Windows 8 Directx 开发学习笔记(十一)地形纹理贴图
- 网页HTML右侧浮动DIV层广告或在线客服图片框最佳代码(支持Firefox&IE)
- 服务器必看书目
- 排序算法之选择排序(JAVA)
- Android仿计算器布局代码
- ASP.NET 弹出提示框所有种类
- Spring MVC中的页面跳转
- 集合映射
- 链接错误小结(转)