源代码下载:dx10tut05.zip。
本教程介绍如何在DirectX 10中使用纹理。纹理通过在多边形表面施加一张图片让我们的场景变得更真实。本教程中使用的纹理如下图所示:
然后将这张纹理施加到上一个教程显示的三角形上,如下图所示:
纹理的格式为.dds。它是DirectX 使用的Direct Draw Surface格式,生成.dds文件的工具位于DirectX SDK中,在DirectX Utilities文件夹中,叫做DirectX Texture Tool。你可以将图像粘贴到这个程序然后将它储存为.dds文件,使用起来非常简单。
在编写代码前我们需要讨论一下纹理映射的原理。要将.dds图形映射到像素我们用的是Texel坐标系统。这个系统将将整数值的像素变换到0.0f和1.0f之间的浮点数。例如,如果一张纹理宽为256个像素,那么第一个像素映射为0.0f,第256个像素映射为1.0f,中间的第128个像素映射为0.5f。
在texel坐标系统中,水平方向的值名为“U"”,垂直方向为“V”。水平方向左边为0.0,右边为1.0。竖直方向顶部为0.0,底部为1.0。例如,左上角为U 0.0、V 0.0,右下角为U 1.0、 V 1.0。下图就表示了纹理坐标系统:
理解了纹理映射的基本概念后,让我们看一下更新后的框架:
相对于上一个教程,框架的变化在于ModelClass下有了一个新的类TextureClass,TextureShaderClass取代了ColorShaderClass。
首先讨论新的HLSL纹理shader。
Texture.fx
// Filename: texture.fx
texture shader中的变量与上一教程的color.fx shader类似。在顶点着色器中仍有三个矩阵用于变换。新的变量是类型为Texture2D的shaderTexture,这就是纹理资源。本例中我们在ModelClass中加载了一个.dds纹理,然后将这个纹理链接到shader中的纹理资源。这样shader就可以访问这个纹理并用于绘制了。
/
// GLOBALS //
/
matrix worldMatrix;
matrix viewMatrix;
matrix projectionMatrix;
Texture2D shaderTexture;
Shader中的另一个新东西是采样状态。采样状态让我们可以调整像素写入到多边形的方式。例如,如果多边形离相机很远,在屏幕上只占8个像素,那么我们使用采样状态判断原始纹理中的哪些像素或像素的组合会实际显示在屏幕上,原始纹理可能是256像素×256 像素,因此决定哪些像素被绘制是非常重要的,它能让多边形上的纹理看起来仍是合适的。
///
// SAMPLE STATES //
///
SamplerState SampleType
{
Filter = MIN_MAG_MIP_LINEAR;
AddressU = Wrap;
AddressV = Wrap;
};
本例中定义的采样状态只有三个元素,但都很重要。如果你想有更多的控制权,可以定义地更加详细。下面是DirectX中采样状态的描述:
//
// TYPEDEFS //
//
struct VertexInputType
{
float4 position : POSITION;
float2 tex : TEXCOORD0;
};
struct PixelInputType
{
float4 position : SV_POSITION;
float2 tex : TEXCOORD0;
};
最重要的元素是Filter。Filter决定哪些像素会被使用或进行组合创建最终的颜色。本例中我使用的是MIN_MAG_MIP_LINEAR,它需要耗费更多的资源但效果最好。它告诉采样器为缩小、放大和mip-level采样使用线性插值。本教程我不打算详细解释,你可以看一下MSDN上的D3D10_FILTER说明,看看产生不同效果的其他选项。AddressU和AddressV设置为Wrap确保纹理坐标位于0.0f和1.0f范围之内。
VertexInputType和PixelInputType需要修改,顶点着色器中不再使用颜色,取而代之的是纹理坐标。纹理坐标有U和V两个浮点数分量,因此类型为float2。TEXCOORD[N]语义让你可以定义纹理坐标的设置数字。本例中我们只使用一个纹理坐标,所以数字设为0,表示这是第一个也是唯一一个纹理坐标。
// Vertex Shader
PixelInputType TextureVertexShader(VertexInputType input)
{
PixelInputType output;
// Change the position vector to be 4 units for proper matrix calculations.
input.position.w = 1.0f;
// Calculate the position of the vertex against the world, view, and projection matrices.
output.position = mul(input.position, worldMatrix);
output.position = mul(output.position, viewMatrix);
output.position = mul(output.position, projectionMatrix);
与上一个教程中color.fx的顶点着色器代码的唯一区别在于我们复制了纹理坐标而不是颜色。
// Store the texture coordinates for the pixel shader.
output.tex = input.tex;
return output;
}
像素着色器也改变了。现在我们使用了sample方法,参数是前面定义的采样状态和输入的纹理坐标。处理之后返回从纹理中采样的像素进行绘制。
// Pixel Shader
float4 TexturePixelShader(PixelInputType input) : SV_Target
{
float4 textureColor;
// Sample the pixel color from the texture using the sampler at this texture coordinate location.
textureColor = shaderTexture.Sample(SampleType, input.tex);
return textureColor;
}
除了shader的名称,technique保持不变。
// Technique
technique10 TextureTechnique
{
pass pass0
{
SetVertexShader(CompileShader(vs_4_0, TextureVertexShader()));
SetPixelShader(CompileShader(ps_4_0, TexturePixelShader()));
SetGeometryShader(NULL);
}
}
Textureclass.h
TextureClass封装了加载、卸载和访问一个纹理资源的代码。每个纹理都需要这个类的实例化对象
// Filename: textureclass.h
#ifndef _TEXTURECLASS_H_
#define _TEXTURECLASS_H_
//
// INCLUDES //
//
#include <d3d10.h>
#include <d3dx10.h>
// Class name: TextureClass
class TextureClass
{
public:
TextureClass();
TextureClass(const TextureClass&);
~TextureClass();
第一个方法根据文件名加载纹理,第二个方法在纹理不使用后卸载纹理。
bool Initialize(ID3D10Device*, WCHAR*);
void Shutdown();
GetTexture返回一个指向纹理资源的指针,被shader使用。
ID3D10ShaderResourceView* GetTexture();
private:
下面是私有的纹理资源变量。
ID3D10ShaderResourceView* m_texture;
};
#endif
Textureclass.cpp
// Filename: textureclass.cpp
#include "textureclass.h"
构造函数中将纹理指针设置为null。
TextureClass::TextureClass()
{
m_texture = 0;
}
TextureClass::TextureClass(const TextureClass& other)
{
}
TextureClass::~TextureClass()
{
}
Ini