多阶段纹理是在纹理缓存(多阶段可以有0~7个纹理缓存)和顶点多边形之间的决定一个物体的颜色的。
单个纹理基本要素有:顶点UV; SetTexture纹理使用和切换; 采样方式SetSamplerState(mag放大min缩小插值,Mip多幅渐进纹理选择算法,纹理寻址)。
如果需要空间纹理包装和多阶层的纹理等,那么就是高级纹理技术了,需要SetTextureStageState和SetRenderState的一些设置和比较多的逻辑,现在先看单个纹理的操作。
Device->SetTexture(0, Tex);
Device->SetTexture(0, Tex1);
Device->SetTexture(0, 0);
-
D3DTADDRESS_CLAMP
-
Texture coordinates outside the range [0.0, 1.0] are set to the texture color at 0.0 or 1.0, respectively.
D3DTADDRESS_BORDER
-
Texture coordinates outside the range [0.0, 1.0] are set to the border color.
一、纹理映射的基本概念
IDirect3DTexture9来表示,纹理是类似于表面的像素矩阵,与表面不同的是它可以被映射到三角形单元中。
1.纹理坐标系是2D纹理图像中的uv坐标系,因为直到三角形顶点光栅化时,三角形已经被映射到屏幕坐标系了,纹理映射才会真正进行,所以uv坐标系类似屏幕坐标系,坐标系原点在图像的左上角,u往右递增,v往下递增,uv大小都在[0,1]的值域范围内。
2.三角形顶点中定义uv坐标,uv坐标系在图像中,但是只有顶点中需要指定uv坐标,图像只需要读取进来即可。
包含纹理坐标的顶点格式:
struct Vertex
{
Vertex(){}
Vertex(
float x, float y, float z,
float nx, float ny, float nz,
float u, float v)
{
_x = x; _y = y; _z = z;
_nx = nx; _ny = ny; _nz = nz;
_u = u; _v = v;
}
float _x, _y, _z;
float _nx, _ny, _nz;
float _u, _v; // texture coordinates
static const DWORD FVF;
};
3.读取纹理
IDirect3DTexture9* Tex = 0;
D3DXCreateTextureFromFile(
Device,
"dx5_logo.bmp", //该函数加载的格式,可以是.bmp, .dds, .dib, .jpg, .png和.tga
&Tex);
4.设置纹理
Device->SetTexture(0, Tex);
HRESULT SetTexture( [in] DWORD Sampler,// 多层纹理的层级阶段,0-7,层级阶段越多越精致,不启用多层纹理用0 [in] IDirect3DBaseTexture9 *pTexture // 纹理 );不启用纹理时用:
Device->SetTexture(0, 0);
如果在场景中各个三角形使用的纹理不相同,那么setTexture(0,tex0)绘制三角形,再setTexture(0, tex1)绘制下一个三角形。
5.纹理过滤器
通常图像纹理三角形 和屏幕顶点三角形大小不一致,当纹理三角形比顶点三角形小时候,纹理三角形会被放大。当纹理三角形比顶点三角形大时候,纹理三角形会被缩小。
直接贴上去会发生畸形,因此需要纹理过滤器,D3D定义了三种纹理过滤器:
D3DTEXF_POINT,D3DTEXF_LINEAR, D3DTEXF_ANISOTROPIC。
D3DTEXF_POINT最快效果比较差,D3DTEXF_LINEAR现在硬件水平也较快效果比较好(推荐使用),D3DTEXF_ANISOTROPIC最慢但效果最好。
D3DTEXF_ANISOTROPIC各向异性纹理过滤时,需要对D3DSAMP_MAXANISOTROPIC水平值进行设定,该值越大效果越好,该值用D3DCAPS9获得合法范围。
实例:
Device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
Device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
Device->SetSamplerState(0, D3DSAMP_MAXANISOTROPIC, 4); // 4是D3DSAMP_MAXANISOTROPIC的值
6.多级渐进纹理过滤器
DX9中用多级渐进纹理,DX11中已经使用Tessellation技术。多级渐进纹理是创建纹理的时候生成一个纹理链包含256x256,128x128,64x64,32x32,16x16等的系列逐渐减小的图片。将相同的纹理选择恰当的大小到给定大小的光栅化三角形中。
创建:如果硬件支持多级渐进纹理,D3DXCreateTextureFromFile将会创建一个多级渐进纹理链,runtime默认是用D3DTEXF_POINT选择最近的纹理。时间和空间性能都很好,因此多级渐进纹理mip map技术被广泛运用。
多级纹理过滤器主要用于控制D3D使用多级纹理的方式:
Device->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_POINT);// D3DTEXF_POINT是最近的,D3DTEXF_LINEAR是最近的两个线性组合,D3DTEXF_NONE是不使用多级纹理过滤器。
7.纹理坐标超出的寻址模式
如果指定的顶点的坐标超出了纹理坐标[0, 1],那么可以用wrap重复寻址模式,border边界颜色寻址模式,clamp箝位寻址模式,mirror镜像寻址模式。
其中wrap表现好于mirror(都是重复), clamp表现好于border(都是填充颜色).
Device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP);
Device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP);
// 也可以用D3DTADDRESS_MIRROR模式,D3DTADDRESS_CLAMP,D3DTADDRESS_BORDERCOLOR模式。
二、应用 实例
Device->CreateVertexBuffer(
6 * sizeof(Vertex),D3DUSAGE_WRITEONLY,
Vertex::FVF,
D3DPOOL_MANAGED,
&Quad,
0);
Vertex* v;
Quad->Lock(0, 0, (void**)&v, 0);
// quad built from two triangles, note texture coordinates:
v[0] = Vertex(-1.0f, -1.0f, 1.25f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f);
v[1] = Vertex(-1.0f, 1.0f, 1.25f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f);
v[2] = Vertex( 1.0f, 1.0f, 1.25f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f);
v[3] = Vertex(-1.0f, -1.0f, 1.25f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f);
v[4] = Vertex( 1.0f, 1.0f, 1.25f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f);
v[5] = Vertex( 1.0f, -1.0f, 1.25f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f);
Quad->Unlock();
//
// Create the texture and set filters.
//
D3DXCreateTextureFromFile(
Device,
"dx5_logo.bmp",
&Tex);
Device->SetTexture(0, Tex);
// 纹理过滤器
Device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
// 多级渐进纹理过滤器
//
// Don't use lighting for this sample.
//
Device->SetRenderState(D3DRS_LIGHTING, false);
// 超出寻址 模式
// set wrap address mode
if( ::GetAsyncKeyState('W') & 0x8000f )
{
Device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP);
Device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP);
}
// set border color address mode
if( ::GetAsyncKeyState('B') & 0x8000f )
{
Device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_BORDER);
Device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_BORDER);
Device->SetSamplerState(0, D3DSAMP_BORDERCOLOR, 0x000000ff);
}
// set clamp address mode
if( ::GetAsyncKeyState('C') & 0x8000f )
{
Device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
Device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
}
// set mirror address mode
if( ::GetAsyncKeyState('M') & 0x8000f )
{
Device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_MIRROR);
Device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_MIRROR);
}