纹理寻址模式
Direct3D应用程序可以为任何图元的任何顶点指定纹理坐标,通常使用的 u、v
纹理坐标的取值范围是[0.0, 1.0],但是通过设置该范围外的坐标值,可以得到纹理映射的特殊效果。
虽然系统允许纹理坐标取[0.0, 1.0]范围外的值,但硬件极限常常影响纹理坐标的取值范围。当调用函数IDirect3DDevice9::GetDeviceCaps()得到设备性能后,一个渲染设备将此极限值放在结构D3DCAPS的成员MaxTextureRepeat中。这个成员的值表示该设备能允许的纹理坐标取值范围。例如,该值是128,那么输入的纹理坐标必须在范围[-128,
128]中,使用这个范围之外的纹理坐标是无效的。
对MaxTextureRepeat的解释也受D3DPTEXTURECAPS_TEXREPEATNOTSCALEDBYSIZE的影响。当设置了该标志位,那么结构D3DCAPS9的成员MaxTextureRepeat的使用就像前面所讲的一样;但如果没有设置该标志位,纹理坐标范围就根据纹理的大小和MaxTextureRepeat的值而定。假定一个纹理的大小为32
x 32像素,MaxTextureRepeat的值是512,那么512/32
= 16,有效的纹理坐标范围就是[-16, 16]。
Direct3D定义了4种纹理寻址模式来处理纹理坐标超出[0,
1]范围的纹理映射方法,它们分别是重叠映射寻址(wrap texture address mode)、镜像纹理寻址(mirror
texture address mode)、夹取纹理寻址(clamp texture address
mode)、边框颜色纹理寻址(border color texture address mode)。
重叠纹理寻址模式
使用重叠纹理寻址模式时,Direct3D会在每个整数纹理坐标连接处自动重复纹理。例如,应用程序创建了一个正方形图元,并指定4个顶点的纹理坐标为(0.0,
0.0)、(0.0, 3.0)、(3.0, 3.0)、(3.0,
0.0)。使用重叠纹理寻址,Direct3D就会在u、v方向各复制3遍原始纹理,如下图所示:
可以调用函数IDirect3DDevice9::SetSamplerState()设置纹理寻址模式。设置第一个参数为纹理层序号,第二个参数为D3DSAMP_ADDRESSU或D3DSAMP_ADDRESSV,表示对纹理的u方向或v方向设置纹理寻址模式,第三个参数设为相应的纹理寻址模式,可以取枚举类型D3DTEXTUREADDRESS中的一个。
Defines constants that describe the supported
texture-addressing modes.
typedef enum D3DTEXTUREADDRESS
{
D3DTADDRESS_WRAP = 1,
D3DTADDRESS_MIRROR = 2,
D3DTADDRESS_CLAMP = 3,
D3DTADDRESS_BORDER = 4,
D3DTADDRESS_MIRRORONCE = 5,
D3DTADDRESS_FORCE_DWORD = 0x7fffffff,
} D3DTEXTUREADDRESS, *LPD3DTEXTUREADDRESS;
Constants
- D3DTADDRESS_WRAP
- Tile the texture at every integer junction. For
example, for u values between 0 and 3, the texture is repeated three times;
no mirroring is performed.
- D3DTADDRESS_MIRROR
- Similar to D3DTADDRESS_WRAP, except that the
texture is flipped at every integer junction. For u values between 0 and 1,
for example, the texture is addressed normally; between 1 and 2, the texture
is flipped (mirrored); between 2 and 3, the texture is normal again; and so
on.
- 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.
- D3DTADDRESS_MIRRORONCE
- Similar to D3DTADDRESS_MIRROR and
D3DTADDRESS_CLAMP. Takes the absolute value of the texture coordinate (thus,
mirroring around 0), and then clamps to the maximum value. The most common
usage is for volume textures, where support for the full
D3DTADDRESS_MIRRORONCE texture-addressing mode is not necessary, but the
data is symmetric around the one axis.
- D3DTADDRESS_FORCE_DWORD
- Forces this enumeration to compile to 32 bits in
size. Without this value, some compilers would allow this enumeration to
compile to a size other than 32 bits. This value is not used.
下列示例代码设置纹理层0的u, v方向寻址模式为重叠纹理寻址。
g_device->SetSamplerState(0,
D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP);
g_device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP);
重叠纹理寻址是Direct3D中缺省的寻址模式,也是三维系统中最常用的寻址模式之一。在渲染具有诸如砖墙之类纹理的物体时,如果使用包含一整张砖墙的纹理贴图会占用较多的内存,通常只需载入一张具有一块或多块砖瓦的较小的纹理贴图,再把它按照重叠纹理寻址模式在物体表面映射多次,就可以达到和使用整张砖墙贴图同样的效果。
镜像纹理寻址模式
使用镜像纹理寻址模式时,Direct3D会在每个整数纹理坐标连接处自动复制并翻转纹理。例如,应用程序创建了一个正方形图元,并指定4个顶点的纹理坐标为(0.0,
0.0)、(0.0, 3.0)、(3.0, 3.0)、(3.0,
0.0)。采用镜像纹理寻址模式,Direct3D就会在u、v方向各复制3遍并翻转原始纹理图,所有的行和列都是前一行或列的镜像,如下图所示:
用枚举类型D3DTEXTUREADDRESS的成员D3DTADDRESS_MIRROR指定镜像纹理寻址模式。下面的示例代码设置纹理层0的u、v方向寻址模式为镜像纹理寻址模式:
g_device->SetSamplerState(0,
D3DSAMP_ADDRESSU, D3DTADDRESS_MIRROR);
g_device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_MIRROR);
夹取纹理寻址模式
夹取纹理寻址模式将纹理坐标夹取在[0.0, 1.0]范围之内。也就是说,它将纹理复制一遍,然后将纹理边缘像素的颜色延伸。例如,应用程序创建了一个正方形图元,并指定4个顶点的纹理坐标为(0.0,
0.0)、(0.0, 3.0)、(3.0, 3.0)、(3.0,
0.0)。将u、v方向上的纹理寻址模式都设置为夹取纹理寻址模式时的效果如下图所示:
原纹理
|
使用夹取纹理寻址模式后的效果图
|
用枚举类型D3DTEXTUREADDRESS的成员D3DTADDRESS_CLAMP指定夹取纹理寻址模式。下面的示例代码设置纹理层0的u、v方向寻址模式为夹取纹理寻址模式:
g_device->SetSamplerState(0,
D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
g_device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
边框颜色纹理寻址模式
边框颜色纹理寻址模式用枚举类型D3DTEXTUREADDRESS的成员D3DTADDRESS_BORDER指定,当纹理坐标超出[0.0,
1.0]范围时,Direct3D使用边框颜色代替纹理颜色。
边框颜色通过调用函数IDirect3DDevice9::SetSamplerState()设置,第一个参数设为纹理层序号,第二个参数设为D3DSAMP_BORDERCOLOR,第三个参数设为所需的边框颜色,为D3DCOLOR类型,以32位整数表示A、R、G、B颜色。下面的示例代码指定边框颜色为红色,并设置纹理层0的u、v方向寻址模式为边框颜色纹理寻址模式。
g_device->SetSamplerState(0,
D3DSAMP_BORDERCOLOR, 0xFFFF0000);
g_device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_BORDER);
g_device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_BORDER);
渲染的结果如下图所示:
原纹理
|
使用边框颜色纹理寻址模式后的效果图
|
纹理寻址模式示例程序
示例程序演示了重叠纹理寻址、镜像纹理寻址、夹取纹理寻址和边框颜色纹理寻址4种不同的纹理寻址模式的效果,其效果图如上面贴图所示。
#include <d3dx9.h>
#pragma warning(disable : 4127)
#define TEXTURE_ADDRESS_WRAP 1
#define TEXTURE_ADDRESS_MIRROR 2
#define TEXTURE_ADDRESS_CLAMP 3
#define TEXTURE_ADDRESS_BORDER 4
#define CLASS_NAME "GameApp"
#define release_com(p) do { if(p) { (p)->Release(); (p) = NULL; } } while(0)
IDirect3D9* g_d3d;
IDirect3DDevice9* g_device;
IDirect3DVertexBuffer9* g_vertex_buffer;
IDirect3DTexture9* g_texture1;
IDirect3DTexture9* g_texture2;
int g_texture_address_mode = TEXTURE_ADDRESS_WRAP;
struct sCustomVertex
{
float x, y, z;
float u, v;
};
#define D3DFVF_CUSTOM_VERTEX (D3DFVF_XYZ | D3DFVF_TEX1)
void setup_matrices()
{
// build world matrix
D3DXMATRIX mat_world;
D3DXMatrixIdentity(&mat_world);
g_device->SetTransform(D3DTS_WORLD, &mat_world);
// setup view matrix
D3DXVECTOR3 eye(0.0f, 0.0f, -10.0f);
D3DXVECTOR3 at(0.0f, 0.0f, 0.0f);
D3DXVECTOR3 up(0.0f, 1.0f, 0.0f);
D3DXMATRIX mat_view;
D3DXMatrixLookAtLH(&mat_view, &eye, &at, &up);
g_device->SetTransform(D3DTS_VIEW, &mat_view);
// setup projection matrix
D3DXMATRIX mat_proj;
D3DXMatrixPerspectiveFovLH(&mat_proj, D3DX_PI/4, 1.0f, 1.0f, 100.0f);
g_device->SetTransform(D3DTS_PROJECTION, &mat_proj);
}
bool init_graphics()
{
if(FAILED(D3DXCreateTextureFromFile(g_device, "texture1.bmp", &g_texture1)))
{
MessageBox(NULL, "Create texture failed!", "ERROR", MB_OK);
return false;
}
if(FAILED(D3DXCreateTextureFromFile(g_device, "texture2.bmp", &g_texture2)))
{
MessageBox(NULL, "Create texture failed!", "ERROR", MB_OK);
return false;
}
sCustomVertex vertices[] =
{
{ -3, -3, 0.0f, 0.0f, 3.0f},
{ -3, 3, 0.0f, 0.0f, 0.0f},
{ 3, -3, 0.0f, 3.0f, 3.0f},
{ 3, 3, 0.0f, 3.0f, 0.0f }
};
g_device->CreateVertexBuffer(sizeof(vertices), 0, D3DFVF_CUSTOM_VERTEX, D3DPOOL_MANAGED, &g_vertex_buffer, NULL);
void* ptr;
g_vertex_buffer->Lock(0, 0, (void**)&ptr, 0);
memcpy(ptr, vertices, sizeof(vertices));
g_vertex_buffer->Unlock();
return true;
}
bool init_d3d(HWND hwnd)
{
g_d3d = Direct3DCreate9(D3D_SDK_VERSION);
if(g_d3d == NULL)
return false;
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
if(FAILED(g_d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&d3dpp, &g_device)))
{
return false;
}
if(! init_graphics())
return false;
setup_matrices();
g_device->SetRenderState(D3DRS_LIGHTING, FALSE);
g_device->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_POINT);
return true;
}
void cleanup()
{
release_com(g_texture1);
release_com(g_texture2);
release_com(g_vertex_buffer);
release_com(g_device);
release_com(g_d3d);
}
void setup_texture()
{
switch(g_texture_address_mode)
{
case TEXTURE_ADDRESS_WRAP:
g_device->SetTexture(0, g_texture1);
g_device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP);
g_device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP);
break;
case TEXTURE_ADDRESS_MIRROR:
g_device->SetTexture(0, g_texture1);
g_device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_MIRROR);
g_device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_MIRROR);
break;
case TEXTURE_ADDRESS_CLAMP:
g_device->SetTexture(0, g_texture2);
g_device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
g_device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
break;
case TEXTURE_ADDRESS_BORDER:
g_device->SetTexture(0, g_texture2);
g_device->SetSamplerState(0, D3DSAMP_BORDERCOLOR, 0xFFFF0000);
g_device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_BORDER);
g_device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_BORDER);
break;
}
}
void render()
{
g_device->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(5, 5, 5), 1.0f, 0);
g_device->BeginScene();
setup_texture();
g_device->SetStreamSource(0, g_vertex_buffer, 0, sizeof(sCustomVertex));
g_device->SetFVF(D3DFVF_CUSTOM_VERTEX);
g_device->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
g_device->EndScene();
g_device->Present(NULL, NULL, NULL, NULL);
}
LRESULT WINAPI WinProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_KEYDOWN:
switch(wParam)
{
case VK_ESCAPE:
DestroyWindow(hwnd);
break;
case 49: // press key "1"
g_texture_address_mode = TEXTURE_ADDRESS_WRAP;
break;
case 50: // press key "2"
g_texture_address_mode = TEXTURE_ADDRESS_MIRROR;
break;
case 51: // press key "3"
g_texture_address_mode = TEXTURE_ADDRESS_CLAMP;
break;
case 52: // press key "4"
g_texture_address_mode = TEXTURE_ADDRESS_BORDER;
break;
}
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE inst, HINSTANCE, LPSTR, INT)
{
WNDCLASSEX wc;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_CLASSDC;
wc.lpfnWndProc = WinProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = inst;
wc.hIcon = NULL;
wc.hCursor = NULL;
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
wc.lpszClassName = CLASS_NAME;
wc.hIconSm = NULL;
if(! RegisterClassEx(&wc))
return -1;
HWND hwnd = CreateWindow(CLASS_NAME, "Direct3D App", WS_OVERLAPPEDWINDOW, 200, 100, 480, 480,
NULL, NULL, wc.hInstance, NULL);
if(hwnd == NULL)
return -1;
if(init_d3d(hwnd))
{
ShowWindow(hwnd, SW_SHOWDEFAULT);
UpdateWindow(hwnd);
MSG msg;
ZeroMemory(&msg, sizeof(msg));
while(msg.message != WM_QUIT)
{
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
render();
Sleep(10);
}
}
cleanup();
UnregisterClass(CLASS_NAME, wc.hInstance);
return 0;
}