借助纹理映射技术,我们可将图像数据映射到三角形单元中,这种功能可以显著地增加所绘制场景的细节和真实感,例如创建一个立方体然后为其每个面映射一个板条纹理,从而将该立方体变为一个板条箱,在Direct3D中纹理用接口IDirect3DTexture9来表示,纹理是类似于表面的一个像素矩阵,与表面不同的是它可被映射到三角形单元中。
纹理坐标
Direct3D锁使用的纹理坐标系由沿水平方向的u轴和沿垂直方向的v轴构成,用坐标对(u,v)标识的纹理元素称为纹理元,注意v轴的正方向是竖直向下的
为了能够处理不同尺度的纹理,Direct3D将纹理坐标做了规范化处理,使之限定在区间[0,1]内,对于每个3D三角形单元,我们都可以在纹理中定义一个相应的三角形区域,然后将该三角形区域内的纹理映射到该3D三角形单元中,为了实现该映射,需要再次修改顶点结构,为之添加一个纹理坐标对以标识纹理中的顶点
struct TextureVertex
{
TextureVertex(){}
TextureVertex()
{
}
float _x, _y, _z;
float _nx, _ny, _nz;
float _u, _v; //纹理坐标
static const DWORD FVF;
};
const DWORD TextureVertex::FVF = D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1; //D3DFVF_TEX1表示顶点结构中包含了一对纹理坐标
现在由3个顶点对象构成的每个三角形都在该纹理坐标系中定义了一个相应的纹理三角形,虽然指定了,但直到光栅化时,即该3D三角形已被变换至屏幕坐标系时,纹理映射才会进行。
创建并启用纹理
纹理数据通常从磁盘中的图像文件读入,然后再加载到IDirect3DTexture9对象中,可使用D3DXCreateTextureFromFile函数来实现,该函数可以加载下列格式的图像:BMP、DDS、DIB、JPG、PNG和TGA
HRESULT D3DXCreateTextureFromFile(
LPDIRECT3DDEVICE9 pDevice,
LPCSTR pSrcFile,
LPDIRECT3DTEXTURE9* ppTexture
);
IDirect3DTexture9* _stonewall;
D3DXCreateTextureFromFile(Device, "stonewall.bmp", &_stonewall);
HRESULT IDirect3DDevice9::SetTexture(
DWORD Stage, //纹理层
IDirect3DBaseTexture9* pTexture
);
//禁用某一纹理层,可将pTexture参数设为0,绘制物体时若不想使用纹理可以这样做
Device->SetTexture(0, 0);
renderObjectWithoutTexture();
//如果场景中的各个三角形所使用的纹理均不相同
Device->SetTexture(0, tex0);
drawTrisUsingTex0();
Device->SetTexture(0, tex1);
drawTrisUsingTex1();
在Direct3D中,最多可设置8层纹理,可以对这些纹理进行组合以创建一幅更细致的图像,这称为多重纹理。
纹理过滤器
通常纹理三角形与屏幕三角形的的大小并不一致,当纹理三角形与屏幕三角形大小不一致时,为适应后者,纹理三角形需要被放大或者缩小,这俩种情况都有畸变发生,为了从某种程度克服这类畸变,Direct采用了一项称为纹理过滤的技术。
Direct3D提供了3种类型的纹理过滤器,过滤器的质量越高,运算开销越大,处理速度越慢。纹理过滤方式可用方法SetSamplerState来设置。
最近点采样:Direct3D默认使用的过滤方式,该方式的处理速度最快,但效果最差,下面代码分别表示将最近点采样方式设置为放大过滤器和缩小过滤器
线性纹理过滤器:该类型的过滤方式可以产生相当好的结果,而且以目前的硬件配置水平也可获得较快的处理速度
各向异性纹理过滤:该类型的过滤方式可以产生最好的结果,但是处理速度也是最慢的,使用该过滤时,必须对D3DSAMP_MAXANISOTROPIC水平值进行设定,该值决定了各向异性过滤的质量水平。该值越大图像效果越好,请调用IDirect3DDevice::GetDeviceCaps函数检查返回的D3DCAPS9结构参数,以获得硬件支持的值的合法取值范围。
//设置最近点采样纹理过滤
Device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
Device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT);
//设置线性纹理过滤
Device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
Device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
//设置各向异性纹理过滤
Device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_ANISOTROPIC);
Device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_ANISOTROPIC);
//将各向异性过滤的质量水平设置为4
Device->SetSamplerState(0, D3DSAMP_MAXANISOTROPY, 4);
多级渐进纹理
屏幕上的三角形通常与纹理三角形大小不一致,为了尽量消除由二者尺寸差异带来的影响,我们可为纹理创建一个多级渐进纹理链,方法是由某一个纹理创建一系列分辨率逐渐减小的纹理图像并且每种分辨率下的纹理所采用的过滤房还是进行定制,以便保留那些较重要的细节,在Direct3D中使用多级渐进纹理十分容易,如果硬件支持多级渐进纹理D3DXCreateTextureFromFile函数将创建一个多级渐进纹理链,此外Direct3D还将自动从纹理链中选择与屏幕三角形最匹配的那个一级纹理,所以由于可被Direct3D自动设置,而且处理时的时间和空间代价都比较低,多级渐进纹理得到了广泛应用。
多级渐进纹理过滤器
多级渐进纹理过滤器主要用于控制Direct3D使用多级渐进纹理的方式,可这样对多级渐进纹理过滤器进行如下设置
Device->SetSamplerState(0, D3DSAMP_MIPFILTER, Filter);
Filter可取以下值
D3DTEXF_NONE:禁用多级渐进纹理过滤器
D3DTEXF_POINT:通过使用该过滤器,Direct3D将选择尺寸与屏幕三角形最接近的那一级纹理,一旦选择了某一级纹理,Direct3D就会用指定的放大过滤器和缩小过滤器对该级纹理进行过滤
D3DTEXF_LINEAR:通过使用该过滤器,Direct3D将取与屏幕三角形尺寸最接近的俩个纹理级,用指定的放大过滤器和缩小过滤器对每级纹理进行过滤,然后再将这俩级纹理进行线性组合,从而形成最终的颜色值。
寻址模式
前面部分中,我们说过纹理坐标必须限制在区间[0,1]内,从技术角度讲是有问题的,因为有时坐标可能超出该范围,Direct3D定义了4种用来处理纹理坐标值超出[0,1]区间的纹理映射模式。分别是重复寻址模式(wrap)、边界颜色寻址模式(border color)、箝位寻址模式(clamp)、镜像寻址模式(mirror)。
//set wrap address mode
if (::GetAsyncKeyState('W') & 0x8000f)
{
//表示对纹理的u方向或v方向设置纹理寻址模式
Device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP);
Device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP);
//D3DTADDRESS_CLAMP、D3DTADDRESS_MIRROR
}
//set border color address mode
if (::GetAsyncKeyState('B') & 0x8000f)
{
//表示对纹理的u方向或v方向设置纹理寻址模式
Device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_BORDER);
Device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_BORDER);
Device->SetSamplerState(0, D3DSAMP_BORDERCOLOR, 0x000000ff);
}
纹理四边形例程
1.构造组成物体的顶点,并为其指定纹理坐标
2.用函数D3DXCreateTextureFromFile为IDirect3DTexture9接口加载一种纹理
3.设置缩小/放大过滤器、和多级渐进纹理过滤器
4.绘制物体前,用函数IDirect3DDevice9::SetTexture来设定与该物体关联的纹理
bool SetUpTextureQuad()
{
Device->CreateVertexBuffer(6 * sizeof(TextureVertex), D3DUSAGE_WRITEONLY, TextureVertex::FVF, D3DPOOL_MANAGED, &Quad, 0);
TextureVertex* v;
Quad->Lock(0, 0, (void**)&v, 0);
v[0] = TextureVertex(-1.0f, -1.0f, 1.25f, 0.0f, 0.0f, -1.0f, 0.0f, 3.0f);
v[1] = TextureVertex(-1.0f, 1.0f, 1.25f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f);
v[2] = TextureVertex(1.0f, 1.0f, 1.25f, 0.0f, 0.0f, -1.0f, 3.0f, 0.0f);
v[3] = TextureVertex(-1.0f, -1.0f, 1.25f, 0.0f, 0.0f, -1.0f, 0.0f, 3.0f);
v[4] = TextureVertex(1.0f, 1.0f, 1.25f, 0.0f, 0.0f, -1.0f, 3.0f, 0.0f);
v[5] = TextureVertex(1.0f, -1.0f, 1.25f, 0.0f, 0.0f, -1.0f, 3.0f, 3.0f);
Quad->Unlock();
D3DXCreateTextureFromFile(Device, "dx5_logo.bmp", &Tex);
//启用纹理
Device->SetTexture(0, Tex);
//设置放大/缩小过滤器
Device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
Device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
//设置渐进纹理过滤器
Device->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_POINT);
//不使用光照
Device->SetRenderState(D3DRS_LIGHTING, false);
D3DXMATRIX proj;
D3DXMatrixPerspectiveFovLH(&proj, D3DX_PI * 0.5f, (float)Width / (float)Height, 1.0f, 1000.0f);
Device->SetTransform(D3DTS_PROJECTION, &proj);
return true;
}
bool DisplayTextureQuad(float timeDelta)
{
if (Device)
{
Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffffffff, 1.0f, 0);
Device->BeginScene();
Device->SetStreamSource(0, Quad, 0, sizeof(TextureVertex));
Device->SetFVF(TextureVertex::FVF);
Device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 2);
Device->EndScene();
Device->Present(0, 0, 0, 0);
}
return true;
}