前一篇实现木箱贴图时,木箱的六个面都正好用一整张纹理图,即六个面的纹理坐标均在[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_MAXsamplerDesc.MaxLOD= 3.402823466e+38F; // FLT_MAXDX::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 开发学习笔记(十一)地形纹理贴图相关推荐

  1. Windows 8 Directx开发学习笔记(一)应用基本框架

    Windows 8系统10月25日就要正式发布,其应用可与Windows Phone 8应用兼容,所以打算转到Windows 8系列的开发.之前虽然开发过应用,但对游戏开发更感兴趣,随意开始学习Met ...

  2. Windows 8 Directx 开发学习笔记(十二)利用混合实现浮在水面的木箱

    在场景中绘制多个不透明物体时很简单,哪个物体离得近,看到的就是哪个物体.但如果加入一个透明的物体,像玻璃,如何渲染就有些麻烦.拿一块红色的玻璃挡住眼睛,看到的物体都偏红,换成蓝色的玻璃,物体都偏蓝.D ...

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

    纹理贴图映射(texturemapping)是可以显著提高场景细节和真实感的一种技术,基本原理是将图像数据映射到3D三角形表面(之前的文章提到过,三维模型其实是由很多个三角形拼接而成).当使用纹理资源 ...

  4. Windows 8 Directx 开发学习笔记(十三)利用模板实现木箱镜像

    假设墙上有一面镜子,镜子前面有个木箱.如果观察角度合适,整个木箱镜像都会在镜子里,计算起来还比较简单:而变换个角度,木箱的镜像可能只有一部分在镜子里,这时单纯依靠计算来实现就很麻烦.DirectX提供 ...

  5. Windows 8 Directx 开发学习笔记(二)建立模型及初始化设备

    上一篇中介绍的DirectxApp类给整个应用搭建了一个框架,而这篇文章涉及的CubeRenderer类则是负责填充框架,呈现实际内容:一个旋转的彩色立方体.CubeRenderer类中的方法通过名称 ...

  6. Windows 8 DirectX 开发学习笔记(十六)使用Terragen生成自然环境贴图

    DirectX 游戏编程入门中提到一个Terragen软件可以生成环境贴图,所以登陆它的官方网站看了下.没想到Terragen生成的图片和照片一样,效果非常好,很多电影里有用到,所以下载免费版尝试一下 ...

  7. Windows 8 Directx 开发学习笔记(七)水波纹的实现

    使用DirectX实际开发中,模型的形状不可能都是一成不变,只依靠移动摄像机去实现动画.这里用实时更新顶点缓冲的方式生成一个水波模型,最终效果类似向水面扔石子时出现的水波纹.有了上一篇建立好的模型,实 ...

  8. Windows 8 Directx 开发学习笔记(五)山峰河谷模型的简单实现

    通过之前对DirectX示例程序代码的研究,基本了解DirectX最简单的工作过程,为了更好的理解整个过程,基于示例程序做一个山峰河谷的简单模型.首先还是根据模板创建正方体的示例程序,Visual C ...

  9. Windows 8 Directx 开发学习笔记(四)示例程序小结

    VS2012的示例程序包括了最基本的DirectX流程,涉及许多知识,所以看完代码之后还是感觉没有头绪,打算对整个过程再整理一下,方便进一步的学习. 正方体在空间中可以用它的8个顶点定义,而每个顶点可 ...

最新文章

  1. 链表问题14——在单链表种删除指定值的节点(方法二)
  2. 【计算理论】图灵机 ( 非确定性图灵机 | 非确定性图灵机指令分析 | 计算过程 | 非确定性指令出现多个分支 | 非确定性图灵机转为计算树 | 计算树 )
  3. 一个原生态ajax过程,提交表单的例子
  4. 35、Power Query-分组中国式排名
  5. Invalid bound statement (not found)
  6. php://input和php://output
  7. openmp并行编程_OpenMP实现生产者消费者问题
  8. TabBarController创建及使用方法简介
  9. 制作安装媒体来部署额外域控制器
  10. 面试官:使用SpringBoot如何开发邮件发送系统?
  11. libsvm2.89在matlab,libsvm-mat-2.89-3工具箱,方便实用
  12. Swift教程_零基础学习Swift完整实例(八)_swift完整实例(添加View的动画效果、添加View的阴影)...
  13. 项目拉去某个版本的_项目管理的“背后军师”——高层领导
  14. 修改XP/Win7开机动画
  15. navicat激活已过期
  16. 压紧力变化的平均值matlab,基于MATLAB遗传算法的汽车拉式离合器膜片弹簧结构参数优化设计...
  17. python 折线图 百分比_Python数据可视化 - 使用Matplotlib库绘制点图、折线图、条状图与饼图...
  18. 十二属相配对与最佳配偶
  19. 基于封锁的并发控制机制
  20. java与jasper模板_ireport中使用java进行pdf转化,jasper作为模板

热门文章

  1. antd upload手动上传_SpringBoot 如何上传大文件?
  2. 安装sw时出现sldim停止工作_脉冲滤筒除尘器的安装有哪些小常识
  3. 淘口令二合一生成php_3淘口令接口的封装 工具类的打造
  4. php 创建文本文件,php-创建文本文件以进行实时下载
  5. mysql数据类型内存_mysql 存储金额类型,用什么数据类型比较可靠,一般企业数据用什么数据类型?...
  6. 全方位带你解读软件测试分类
  7. 如何root安卓手机_如何从我的字体里面提取TTF并阉割成未Root安卓手机能用的?...
  8. java json传值到前台_json前后台传值
  9. 存储系统的三种分类及KV模型
  10. 亲密关系-【认知情绪】-每一次生气的背后有什么