如果要从一张纹理中获取Alpha值,应将D3DTA_TEXTURE作为Alpha参数。
如果要使用来自顶点中的Alpha值,应将D3DTA_DIFFUSE作为Alpha参数,并确保渲染状态D3DRS_DIFFUSEMATERIALSOURCE被设置为D3DMCS_COLOR1(这也是默认状态)。
如果要使用来自材质中的Alpha值,应将D3DTA_DIFFUSE作为Alpha参数,并确保渲染状态D3DRS_DIFFUSEMATERIALSOURCE被设置为D3DMCS_MATERIAL。
如果未用SetRenderState()设置D3DRS_DIFFUSEMATERIALSOURCE参数,则从默认来源(即顶点)获取漫反射颜色。
//设置纹理颜色和光照顶点颜色进行融合
Device->SetTexture( 0, g_pTexture );
Device->SetTextureStageState( 0, D3DTSS_TEXCOORDINDEX, 0 );
Device->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
Device->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );
Device->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_MODULATE );
// 选择纹理alpha作为第0阶层纹理的颜色,这里也就是多边形顶点颜色的alpha值
Device->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );
Device->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1 );
//Alpha混合设置, 设置混合系数
pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, true );
pd3dDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
pd3dDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );
一般应用:
用于天空盒贴图。
用于物体反射周围环境颜色。
用于天空盒贴图。
使用法线和相机到vertex的向量,推导出入射光向量作为uv坐标 (GL_REFLECTION_MAP)
用于物体反射周围环境颜色。
然后自动或者手动shader计算立体纹理uv坐标,然后渲染物体就可以了。
Direct3D的凹凸纹理被用来表示物体表面相邻像素的高度差,它的每个纹理元素由表示水平相邻像素高度差的Du、表示垂直相邻像素高度差的Dv以及表示该点亮度的L组成(某些凹凸纹理像素格式可以不包含L)。下表列出了Direct3D支持的凹凸纹理像素格式:
凹凸纹理像素格式 | 说明 |
D3DFMT_V8U8 | 每个像素由16位整数表示,分别由8位整数表示Du和Dv |
D3DFMT_L6V5U5 | 每个像素由16位整数表示,6位整数表示L,分别由5位整数表示Du和Dv |
D3DFMT_X8L8V8U8 | 每个像素由32位整数表示,包括8位保留位、8位L、8位Du、8位Dv |
D3DFMT_V16U16 | 每个像素由32位整数表示,分别由16位整数表示Du和Dv |
D3DFMT_Q8W8V8U8 | 每个像素由32位整数表示,分别由8位整数表示Q、W、V、U |
D3DFMT_CxV8U8 | 压缩像素格式,每个像素由16位整数表示,即8位Du和8位Dv,另外C = sqrt(1 - Du2 - Dv2 ) |
通常情况下,可以载入一张表示物体表面图像高度的纹理图,通过计算高度图水平相邻和垂直相邻元素的高度差来生成凹凸纹理,也可以通过程序生成凹凸纹理,这里根据高度纹理图,用程序来生成凹凸纹理,代码如下:
HRESULT CreateBumpTexture(IDirect3DDevice9* device, IDirect3DTexture9* pHeightMapTexture, IDirect3DTexture9* pBump_map_texture)
{
HRESULT hr;
D3DSURFACE_DESC surface_desc;
// pHeightMapTexture是高度纹理图,每个像素存放的是高度,虽然大小是4个字节,但是只用一个字节也就是大小为[0,255]
// 从纹理的表面数据中获取纹理的具体数值
pHeightMapTexture->GetLevelDesc(0, &surface_desc);
// 创建一个凹凸纹理图,凹凸纹理的每个像素存放的是D3DFMT_X8L8V8U8,
// 值为:预留值X8、亮度值L8、相邻上高度减去下高度的值V8、左高度减去右高度的值U8
device->CreateTexture(surface_desc.Width, surface_desc.Height, 1, 0, D3DFMT_X8L8V8U8, D3DPOOL_MANAGED,
&pBump_map_texture, NULL);
D3DLOCKED_RECT locked_rect;
pHeightMapTexture->LockRect(0, &locked_rect, NULL, 0);
// 一行的宽度
DWORD src_pitch = (DWORD) locked_rect.Pitch;
BYTE* src_row_top = (BYTE*) locked_rect.pBits;
BYTE* src_row_cur = src_row_top;
BYTE* src_row_bot = src_row_top + src_pitch * (surface_desc.Height - 1);
pBump_map_texture->LockRect(0, &locked_rect, NULL, 0);
DWORD dest_pitch = (DWORD) locked_rect.Pitch;
BYTE* dest_row_top = (BYTE*) locked_rect.pBits;
BYTE* dest_row_cur = dest_row_top;
// iterate through all lines
for(DWORD y = 0; y < surface_desc.Height; y++)
{
// 源像素
BYTE* src_pixel_cur;
// 源上一行的像素
BYTE* src_pixel_up;
// 源下一行的像素
BYTE* src_pixel_below;
// 目标像素
BYTE* dest_pixel;
// 取当前行做为源像素
src_pixel_cur = src_row_cur;
// 当y为0第一行时候,源像素的上一行为最后一行像素
if(y == 0)
{
src_pixel_up = src_row_bot;
}
// 否则上一行的像素等于当前行的像素减去一行的宽度
else
{
src_pixel_up = src_row_cur - src_pitch;
}
// 最末一行的下一行像素,是第一行像素
if(y == surface_desc.Height - 1)
{
src_pixel_below = src_row_top;
}
// 否则下一行像素,等于当前行加上行宽
else
{
src_pixel_below = src_row_cur + src_pitch;
}
// 目标行等于当前目标行
dest_pixel = dest_row_cur;
// iterate through all columns in current line
for(DWORD x = 0; x < surface_desc.Width; x++)
{
BYTE src_pixel_left, src_pixel_right;
if(x == 0)
// 第0列,左边一个像素等于最后一个像素,4个字节等于D3DFMT_X8L8V8U8大小
src_pixel_left = *(src_row_cur + (surface_desc.Width - 4));
else
// 其它列,左边一个像素就是当前像素减去4个字节等于D3DFMT_X8L8V8U8大小
src_pixel_left = *(src_pixel_cur - 4);
if(x == surface_desc.Width - 1)
// 最后一列,右边一个像素等于第一列的像素
src_pixel_right = *src_row_cur;
else
// 其它像素等于,右边一个像素
src_pixel_right = *(src_pixel_cur + 4);
// du是当前像素的左边第一个像素高度值(小端存储),减去右边一个像素的高度值
BYTE du = BYTE(src_pixel_left - src_pixel_right);
// dv是当前像素列的上一列减去下一列的高度值
BYTE dv = BYTE(src_pixel_up - src_pixel_below);
// the luminance bump value
// 目标当前行中的迭代像素值(高度),如果大于1那么亮度取63,否则取127
BYTE u_lumi = (*src_pixel_cur > 1) ? 63 : 127;
// 一个4字节的数值里面,格式为D3DFMT_X8L8V8U8,低数值存放在低地址,所以是先赋值du
*dest_pixel++ = du;
*dest_pixel++ = dv;
*dest_pixel++ = u_lumi;
*dest_pixel++ = 0;
// move one pixel to the right
// 当前源像素跳过4字节
src_pixel_cur += 4;
// 源的下一行加上了4字节
src_pixel_below += 4;
// 源的上一行也应该加4字节[by jiayuan 2015.8.2]
src_pixel_up +=4;
// 当前目标像素跳过4字节
src_pixel_up += 4;
}
// move to the next line
// 一行结束,源像素行加宽度
src_row_cur += src_pitch;
// 一行结束,目标像素加宽度
dest_row_cur += dest_pitch;
}
pBump_map_texture->UnlockRect(0);
pHeightMapTexture->UnlockRect(0);
return S_OK;
}
3)
凹凸纹理设置
凹凸纹理映射通常使用3层纹理:物体原始纹理、由原始纹理高度图生成的凹凸纹理、环境纹理,对应于多层纹理混合的0、1、2层。指定当前纹理层状态 为D3DTOP_BUMPENVMAP或D3DTOP_BUMPENVMAPLUMINANCE可设置当前纹理层为凹凸纹理,例如:
pd3dDevice->SetTexture(1, g_bump_map_texture);
// D3DTOP_BUMPENVMAP 没有亮度值,默认设置为1,和下一阶层纹理分量乘法输出是下一阶层的颜色
pd3dDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_BUMPENVMAP);
或
pd3dDevice->SetTexture(1, g_bump_map_texture);
// D3DTOP_BUMPENVMAPLUMINANCE 有亮度值需要和下阶层分量乘法输出颜色
pd3dDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_BUMPENVMAPLUMINANCE);
纹理状态D3DTOP_BUMPENVMAP和D3DTOP_BUMPENVMAPLUMINANCE表示两种不同的凹凸纹理映射方法。纹理状态D3DTOP_BUMPENVMAPLUMINANCE表示在凹凸纹理中包含凹凸纹理亮度值L,L将与下一纹理层的纹理颜色相乘作为最后输出的纹理颜色。纹理状态D3DTOP_BUMPENVMAP默认亮度值L为1,即不改变下一纹理层的纹理颜色。
4) 凹凸纹理的扰动矩阵和亮度系数亮度偏移的设置
Direct3D可设置凹凸纹理矩阵,对凹凸纹理中的每个纹理元素值(Dv、Du,即对于下一纹理层中的每个纹理坐标的扰动值)进行坐标变换:
Du' = Du * M00 + Dv * M10
Dv' = Dv * M01 + Dv * M11
凹凸纹理的每个纹理元素Dv、Du与一个2 x 2的凹凸矩阵相乘,其结果Dv、Du对下一纹理层中的每个纹理元素的纹理坐标产生该数值代表的坐标扰 动。2 x 2的凹凸矩阵值可由函数IDirect3DDevice9::SetTextureStageState()设置,将它的第一个参数设置为纹理层序号,第 二个参数设置为D3DTSS_BUMPENVMAT00或D3DTSS_BUMPENVMAT01或D3DTSS_BUMPENVMAT10或 D3DTSS_BUMPENVMAT11,分别表示凹凸矩阵的4个矩阵元素,第三个参数设置为该矩阵元素的值。纹理坐标扰动Dv、Du或Dv'、Du'的范围介于-1 ~ +1之间。
如果使用D3DTOP_BUMPENVMAPLUMINANCE计算凹凸纹理,Direct3D将使用下列方程计算凹凸纹理的亮度值L'(L'为0~255的整数,它将被用来与下一纹理层的颜色相乘,作为最后输出的纹理颜色值)。
L' = L * S + O
其中,L为凹凸纹理元素中的亮度L值,比例系数S和偏移系数O可改变最终的亮度值。S和O可由函数 IDirect3DDevice9::SetTexureStageState()设置,将它的第一个参数设置为纹理层序号,第二个参数设置为 D3DTSS_BUMPENVLSCALE或D3DTSS_BUMPENVLOFFSET,分别表示凹凸纹理映射的比例系数S和偏移系数O,将第三个参数 设置为相应的系数值。
设置凹凸纹理状态的代码如下所示:
// set texture color blend method for stage 0 (base texture)
pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
pd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
// set texture color blend method for stage 1 (bump map texture)
pd3dDevice->SetTexture(1, g_bump_map_texture);
pd3dDevice->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 0);
pd3dDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_BUMPENVMAPLUMINANCE);
pd3dDevice->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE);
pd3dDevice->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT);
pd3dDevice->SetTextureStageState(1, D3DTSS_BUMPENVMAT00, F2DW(0.8f));
pd3dDevice->SetTextureStageState(1, D3DTSS_BUMPENVMAT01, F2DW(0.0f));
pd3dDevice->SetTextureStageState(1, D3DTSS_BUMPENVMAT10, F2DW(0.0f));
pd3dDevice->SetTextureStageState(1, D3DTSS_BUMPENVMAT11, F2DW(0.8f));
pd3dDevice->SetTextureStageState(1, D3DTSS_BUMPENVLSCALE, F2DW(4.0f));
pd3dDevice->SetTextureStageState(1, D3DTSS_BUMPENVLOFFSET, F2DW(0.0f));
pd3dDevice->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
pd3dDevice->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
// set texture color blend method for stage 2 (environment map texture)
5)凹凸纹理使用环境纹理的话,那么需要设置环境纹理的自动纹理坐标,和自动纹理坐标转换矩阵
D3DXMATRIX mat;
mat._11 = 0.5f; mat._12 = 0.0f; mat._13 = 0.0f; mat._14 = 0.0f;
mat._21 = 0.0f; mat._22 = -0.5f; mat._23 = 0.0f; mat._24 = 0.0f;
mat._31 = 0.0f; mat._32 = 0.0f; mat._33 = 1.0f; mat._34 = 0.0f;
mat._41 = 0.5f; mat._42 = 0.5f; mat._43 = 0.0f; mat._44 = 1.0f;
pd3dDevice->SetTransform(D3DTS_TEXTURE2, &mat);
pd3dDevice->SetTexture(2, g_env_map_texture);
pd3dDevice->SetTextureStageState(2, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2);
// 第0层第1层都是采用顶点uv或者用网格DrawSubset绘制,而第2阶层纹理采用了环境映射纹理用了自动生成坐标系
pd3dDevice->SetTextureStageState(2, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_SPHEREMAP);
pd3dDevice->SetTextureStageState(2, D3DTSS_COLORARG1, D3DTA_TEXTURE);
pd3dDevice->SetTextureStageState(2, D3DTSS_COLORARG2, D3DTA_CURRENT);
pd3dDevice->SetTextureStageState(2, D3DTSS_COLOROP, D3DTOP_ADD);
pd3dDevice->SetSamplerState(2, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
pd3dDevice->SetSamplerState(2, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
// check whether device support volume texture
if((pCaps->TextureCaps & D3DPTEXTURECAPS_VOLUMEMAP) == 0)
return false;
立体纹理(volume texture)是一组应用到二维图元(如一个三角形或一条直线)的三维纹理元素的集合,可以使用立体纹理实现一些特殊效果,如迷雾、爆炸等。当对一个图 元使用立体纹理时,它的每个顶点都需要一组三元纹理坐标。当绘制该图元时,它中间的每个像素都将用立体纹理中的一些纹理元素的颜色值进行填充,这与二维纹 理映射的情况相似。
立体纹理以薄片为单元组织起来,可以把它想象成将(宽 x 高)的二维表面转换成(宽 x 高 x 深)的三维立体,每个薄片是单独的一行,立体纹理可以有一系列级别,每一级都较上一级缩小一倍,类似二维的多幅渐进纹理,只不过这里是三维的。
1) 声明顶点格式,使用立体纹理坐标接着,我们需要在灵活顶点格式中指定每个顶点都需要三个纹理坐标,如下所示:
struct sCustomVertex
{
float x, y, z;
// 一个立体纹理,是有x,y,z三个维度的,每个维度都是一个像素一层
float u, v, w;
};
#define D3DFVF_CUSTOM_VERTEX (D3DFVF_XYZ | D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE3(0))
2) 填充顶点缓存,为了显示立体纹理在不同深度上的表现,顶点纹理坐标的w值还是需要变化的
接着,为每个顶点指定数据,创建顶点缓冲区并填充它:
// create vertex buffer and fill data
sCustomVertex vertices[] =
{
{ -3.0f, -3.0f, 0.0f, 0.0f, 1.0f, 0.0f},
{ -3.0f, 3.0f, 0.0f, 0.0f, 0.0f, 0.0f},
{ 3.0f, -3.0f, 0.0f, 1.0f, 1.0f, 0.0f},
{ 3.0f, 3.0f, 0.0f, 1.0f, 0.0f, 0.0f}
};
pd3dDevice->CreateVertexBuffer(sizeof(vertices), 0, D3DFVF_CUSTOM_VERTEX, D3DPOOL_MANAGED, &g_vertex_buffer, NULL);
void* ptr;
g_vertex_buffer->Lock(0, sizeof(vertices), (void**)&ptr, 0);
memcpy(ptr, vertices, sizeof(vertices));
g_vertex_buffer->Unlock();
为了生成立体纹理,还必须改变纹理的w坐标:
//--------------------------------------------------------------------------------------
// Handle updates to the scene
//--------------------------------------------------------------------------------------
void CALLBACK OnFrameMove( IDirect3DDevice9* pd3dDevice, double fTime, float fElapsedTime, void* pUserContext )
{
// change w coordinate of volume texturefloat angle = (float) fTime / 2.0f;
sCustomVertex* ptr;
g_vertex_buffer->Lock(0, 0, (void**)&ptr, 0);
// 改变w值,也就是w在[0,1]的纹理坐标内切换,显示立体纹理中不同维度的平面纹理
for(int i = 0; i < 4; i++)
ptr[i].w = sin(angle) * sin(angle);g_vertex_buffer->Unlock();
}
3) 创建和填充立体纹理
在示例程序中,立体纹理是由程序生成的,比较复杂。Direct3D提供了辅助函数D3DXCreateVolumeTextureFromFile()来从图片文件中生成立体纹理,该函数的声明如下:
Creates a volume texture from a file.
HRESULT D3DXCreateVolumeTextureFromFile(通过文件创建立体纹理时文件格式必须是立体纹理格式,对于普通格式的图片文件,可以通过DirectX提供的纹理工具"DirectX Texture Tool"转换成立体纹理格式的文件。
LPDIRECT3DDEVICE9pDevice,
LPCTSTRpSrcFile,
LPDIRECT3DVOLUMETEXTURE9 *ppVolumeTexture
);
程序创建立体纹理
//创建立体纹理
hr = pd3dDevice->CreateVolumeTexture( 16, 16, 16, 1, 0,
D3DFMT_A8R8G8B8, D3DPOOL_MANAGED,
&g_pVolumeTexture, NULL );
if( FAILED(hr) )
return hr;
//填充立体纹理
D3DLOCKED_BOX LockedBox;
hr = g_pVolumeTexture->LockBox( 0, &LockedBox, 0, 0 );
if( FAILED(hr) )
return hr;
for( UINT w=0; w<16; w++ )
{
BYTE* pSliceStart = (BYTE*)LockedBox.pBits;
for( UINT v=0; v<16; v++ )
{
for( UINT u=0; u<16; u++ )
{
// du,dv,dw从[-1,1]的浮点数
FLOAT du = (u-7.5f)/7.5f;
FLOAT dv = (v-7.5f)/7.5f;
FLOAT dw = (w-7.5f)/7.5f;
FLOAT fScale = sqrtf( du*du + dv*dv + dw*dw ) / sqrtf(1.0f);
if( fScale > 1.0f )
{
fScale = 0.0f;
}
// 取反是因为越近,颜色越明亮,越远越暗淡
else
{
fScale = 1.0f - fScale;
}
// rgb颜色
DWORD r = (DWORD)((w<<4)*fScale);
DWORD g = (DWORD)((v<<4)*fScale);
DWORD b = (DWORD)((u<<4)*fScale);
// 填充好二维的
((DWORD*)LockedBox.pBits)[u] = 0xff000000 + (r<<16) + (g<<8) + (b);
}
// 下一行
LockedBox.pBits = (BYTE*)LockedBox.pBits + LockedBox.RowPitch;
}
// 深度上面也是要逐像素计算颜色的,也是和2维一样分作了16像素
// 那么最终得到的立体纹理是一个 16X16X16的纹理内存,故可以模拟迷糊、爆炸效果.
LockedBox.pBits = pSliceStart + LockedBox.SlicePitch;
}
g_pVolumeTexture->UnlockBox( 0 );
接着,为每个顶点指定数据,创建顶点缓冲区并填充它:
// create vertex buffer and fill data
sCustomVertex vertices[] =
{
{ -3.0f, -3.0f, 0.0f, 0.0f, 1.0f, 0.0f},
{ -3.0f, 3.0f, 0.0f, 0.0f, 0.0f, 0.0f},
{ 3.0f, -3.0f, 0.0f, 1.0f, 1.0f, 0.0f},
{ 3.0f, 3.0f, 0.0f, 1.0f, 0.0f, 0.0f}
};
4)渲染立体纹理
最后设置要渲染的纹理层和立体纹理,纹理阶段混合方法,顶点数据,并渲染它:
// set textue and stream source and vertex format
pd3dDevice->SetTexture(0, g_volume_texture);
pd3dDevice->SetStreamSource(0, g_vertex_buffer, 0, sizeof(sCustomVertex));
pd3dDevice->SetFVF(D3DFVF_CUSTOM_VERTEX);// set texture color blend method
pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
pd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);// Clear the render target and the zbuffer
V( pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_ARGB(0, 0, 0, 0), 1.0f, 0) );// Render the scene
if( SUCCEEDED( pd3dDevice->BeginScene() ) )
{
pd3dDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
RenderText();V(g_button_dlg.OnRender(fElapsedTime));
立体纹理和普通二维纹理的主要区别是:顶点纹理坐标是三维的,纹理对象指针的类型不同,立体纹理的创建也不同,而立体纹理在使用上和普通二维纹理基本相同。
V( pd3dDevice->EndScene() );
}
要渲染看起来真实的场景,最好是使用高分辨率而且颜色丰富的纹理,但这样的纹理可能会耗费大量的内存,例如,一张每像素16位颜色的256x 256纹理将使用128KB的内存。如果在该纹理中使用多级渐进纹理,还需要额外的43KB内存。一个使用50张这种纹理的场景将需要8MB的内存,如果需要更强的真实性,可以使用每像素32位颜色的512x 512纹理,但那就需要8倍的内存。为了减少纹理消耗的系统带宽和内存空间,Direct3D支持纹理压缩和实时解压,即DXT纹理压缩。压缩后的纹理被存储在Direct3D纹理指针中,当Direct3D渲染物体时,Direct3D引擎自动对纹理进行解压。应用DXT压缩纹理不仅可以节省内存空间,而且能有效地降低纹理传输带宽,提高图形系统的整体性能。
随着DirectX对纹理压缩格式的推广,目前大部分显卡都支持DXT压缩纹理,而且DXT压缩纹理在图形质量和运行速度之间取得了很好的平衡。
DXT纹理压缩格式
DXT是一种DirectDraw表面,它以压缩形式存储图形数据,该表面可以节省大量的系统带宽和内存。即使不直接使用DXT表面渲染,也可以通 过DXT格式创建纹理的方法节省磁盘空间。Direct3D提供了D3DFMT_DXT1~D3DFMT_DXT5共5种压缩纹理格式。其中,D3DFMT_DXT1支持15位RGB和1位alpha图形格式,D3DFMT_DXT2、D3DFMT_DXT3支持12位RGB和4位alpha,D3DFMT_DXT4、D3DFMT_DXT5则采取了线性插值方式生成alpha。
有3种DXTC的格式可供使用,分别是DXT1,DXT3和DXT5。DXT1 压缩比例:1:8 压缩比最高,它只有1Bit Alpha,Alpha通道信息几乎完全丧失。一般将不带Alpha通道的图片压缩成这种格式。如Worldwind 用的卫星图片。DXT3 压缩比例:1:4 使用了4Bit Alpha,可以有16个Alpha值,可很好地用于alpha通道锐利、对比强烈的半透和镂空材质。DXT5 压缩比例:1:4 使用了线形插值的4Bit Alpha,特别适合Alpha通道柔和的材质,比如高光掩码材质。大部分3D游戏引擎都可以使用DDS格式的图片用作贴图,也可以制作法线贴图。
DXT纹理压缩的应用:
1)检查是否支持纹理压缩
// check whether device support DXT compressed texture
if(FAILED(pD3D->CheckDeviceFormat(pCaps->AdapterOrdinal, pCaps->DeviceType, AdapterFormat, 0, D3DRTYPE_TEXTURE, D3DFMT_DXT1)))
return false;如果当前设备支持DXT压缩纹理,就可以直接创建DXT压缩纹理,并可以通过IDirect3DDevice9::SetTexture()函数设置 DXT压缩纹理,将其映射到物体表面。如果当前设备不支持DXT压缩纹理,仍然可以DXT压缩格式存储纹理,但在使用该纹理渲染图形前,必须将DXT压缩 格式的纹理转换为当前设备支持的格式。
2) 创建dds格式文件,非dds格式文件也是可以使用压缩纹理的,但是dds格式可以压缩贴图大小且加载更快
ps查看和导出dds插件:https://developer.nvidia.com/nvidia-texture-tools-adobe-photoshop
3dsmax现在的版本都应该支持dds贴图作为才材质的了, 不过通过http://www.nvidia.cn/object/3dsmax_dds_plugins.html下载的原码可以窥探一下nvidia写的导入 dds的 插件代码。dds文件格式编辑 DirectX Texture Tool 可以编辑dds文件使用的压缩格式DXT1,DXT3,DXT5等,可以编辑文件的mipMap/CubeMap/VolumeMap纹理特性。可以查看alpha通道信息等,具体使用见 https://msdn.microsoft.com/en-us/library/bb219744.aspx 。
3)创建压缩纹理
一般使用DXT1,DXT3和DXT5类型的压缩纹理。
在使用函数IDirect3DDevice9::CreateTexture()创建纹理时,将参数Format设置为压缩纹理格式D3DFMT_DXT1 ~ D3DFMT_DXT5中的任意值,可以创建指定压缩格式的纹理,也可以通过函数D3DXCreateTextureFromFileEx()从磁盘文件中生成DXT压缩纹理:
D3DXCreateTextureFromFileExW(pd3dDevice, L"texture.jpg", 0, 0, 5, 0, D3DFMT_DXT1, D3DPOOL_MANAGED,D3DX_DEFAULT, D3DX_DEFAULT, 0xFF000000, NULL, NULL, &g_texture);
4)使用压缩纹理
在创建好压缩纹理之后,就可以和前面一样设置纹理及各项纹理渲染状态,最后进行渲染。如果当前设备支持DXT压缩纹理,那么在使用DXT纹理渲染图形时,Direct3D引擎会自动进行纹理压缩,如果当前设备不支持DXT压缩纹理,这时使用DXT压缩纹理和使用普通的非压缩纹理完全相同。当使用大量纹理贴图渲染复杂场景时,通过使用DXT压缩纹理可以有效提高程序运行性能。
13.纹理管理
如果显卡中只有64MB的视频内存,就应该考虑将哪些纹理加载和保留在内存中。可以编写一个算法让计算机来自动考虑这些问题,那么这个算法的任务是什么呢?它需要跟踪可用的纹理内存总数,并了解哪些纹理是经常用到的,而哪些纹理是很少用到的。至少,这个纹理管理算法必须能决定哪些现有的纹理资源可以通过一张纹理图来重新加载,以及应该销毁哪些表面,然后由新的纹理资源取而代之。
Direct3D有一个自动的纹理管理系统,当通过CreateTexture()创建一个纹理对象时,将Pool参数指定为D3DPOOL_MANAGED,就是向Direct3D请求该系统的支持。纹理管理器通过一个时间戳来跟踪纹理的使用情况,这个时间戳用于记录每个纹理 对象最后被使用的时间。纹理管理器通过"最近最少使用"算法来决定哪些纹理应从视频内存中移除,若有两个纹理对象同时满足移除条件,则根据纹理属性来做进一步的判断。如果它们具有相同的优先级,则移除最近最少使用的纹理,如果它们具有相同的时间戳,则移除低优先级的纹理。
通过为纹理表面调用IDirect3DResource9::SetPriority()函数,可以给托管的纹理赋予一个优先级,该函数声明如下:
Assigns the resource-management priority for this resource.
DWORD SetPriority(
DWORDPriorityNew
);纹理优化方式
0)使用dds格式文件,使用D3DFMT_DXTn来创建内存压缩纹理。
1)将多个小图片,用TexturePacker合并为一张大图和xml配置文件,读取小图时候根据xml截取矩形区域即可。对称的纹理只需要一张即可,然后通过旋转变换。符合九宫格的图片也是只需要一张即可进行放大。太大的图片也可以切割然后进行拼接得到需要的大图片。2)使用D3DPOOL_DEFAULT常驻显存中,避免每次都调度提交,将静态不经常变更的纹理放置在显存中(例如光照贴图,地图,建筑等资源),渲染效率会更高,丢失的时候IDirect3DDevice9::Reset后要重新创建恢复起来要不断的传输到显存中有点慢有时候如果是比较大量的贴图建议使用D3DPOOL_MANAGED,并且显存中的纹理不能锁定更新需要IDirect3DDevice9::UpdateSurface, IDirect3DDevice9::UpdateTexture;和纹理不同显存中的swapchain back buffers, render targets, vertex buffers和indexbuffers都是可以锁定更新的。常驻内存,有频繁更新的数据,例如粒子数据,骨骼蒙皮动画等也是可以声明为D3DPOOL_DEFAULT的,同时用D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY 使用方式标记,这意味着此资源会被创建在AGP Memory中。锁定的时候用D3DLOCK_NOOVERWRITE或D3DLOCK_DISCARD可以瞒天过海避免同步,且可以修改一部分渲染一部分提高CPU和GPU的并行效率。使用D3DPOOL_MANAGED交给D3D托管,渲染效率一般(系统RAM/AGP内存/显存中都有)
D3DPOOL_MANAGED的资源可以自由的锁定。
但是只有系统内存中的副本资源数据可以直接的修改,当需要渲染的时候D3D会自动的拷贝到AGP内存和显存中。
适用情况:由于D3D自己的资源管理方案很高效、使用简单,游戏中大部分资源都可以使用此标识创建。
例子:游戏中使用的大部分纹理贴图,静态模型。
D3DPOOL_SYSTEMMEM:
直接放置在系统中,渲染效率较差需要用UpdateSurface或UpdateTexture拷贝到D3DPOOL_DEFAULT创建的资源,设备丢失时候不需要重新创建资源,锁定很方便CPU处理较快。这些资源可以被锁定(读写),可以通过IDirect3DDevice9::UpdateSurfaceor IDirect3DDevice9::UpdateTexture函数,将该类型的资源数据,设置为用D3DPOOL_DEFAULT创建的资源的数据来源。
使用:对于需要高频CPU读写处理的数据,推荐使用这种方式。
D3DPOOL_SCRATCH:
直接放置在系统中,不能直接的交给D3D渲染,设备丢失时候不需要创建资源,不被设备像素格式限制可以创建锁定拷贝。
这些资源放置在系统RAM中,当设备丢失时不需要重新创建数据。这些数据不被设备大小和像素格式的限制。正因为如此,
这些资源不能被Direct3D访问,不能够设置为纹理或者设置为渲染目标。然而,这些资源之间总是可以被创建,锁定和拷贝。
Directx 9引入这种内存池的原因是由于它从API中删除了CreateImageSurface()的函数。通常,D3DPOOL_SCRATCH是连同CreateOffscreenPlainSurface()一起使用的,后者将返回一个与之前通过CreateImageSurface()来创建的、具有相同特征的表面。因此,它主要用于创建图像表面。4)使用内存池管理纹理,避免每次都从磁盘IO加载纹理,当不需要的时候及时释放,过关卡时候释放掉管理的一些场景静态纹理内存,建筑纹理,特效等等。对于纹理这时可以调用IDirect3D3::EvictManagedTextures方法逐出所有的被管理纹理。在调用这个方法时,Direct3D会将所有的本地与非本地显存纹理销毁掉,只留下原始系统内存拷贝。
5)异步读取纹理,提高IO性能,但是一定要采用异步线程机制,唤醒回调的机制,避免不断的循环浪费CPU时间。
6)暂时没有想到其它减少cpu draw call的技巧,减少内存和飙升大小的技巧,减少IO读取和提高IO读取性能,减少GPU调度和渲染纹理的技巧。