Direct3D中 SetTextureStageState 函数
Posted 米罗西
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Direct3D中 SetTextureStageState 函数相关的知识,希望对你有一定的参考价值。
先说明几个概念:
1, Multipass(多通道)
将一个任务划分成几个阶段,由多个pass处理不同阶段,后续pass总是处理前一个pass的结果。例如复杂的光照方程可以分成几个pass来计算。
用不同的纹理通过多个pass来多次渲染一个图元,这样可以实现许多很酷的特效。例如LightMap,它就是用不同的纹理来表示复杂的光、影效果。
2, Multitexture(多纹理)
很显然,pass越多,效率越低。为了降低pass的数量,有些硬件加速卡支持在一个pass中渲染两个或更多的纹理,这种技术就叫做multitexture。D3D在一个pass中最多支持8个纹理的混合。
3, Pipeline(管道)
可以将管道想像成一条流水线,它完成某项任务。
4, Stage
一个管道可以由多个stage组成,这些stage同时运行,所以管道的速度取决于最慢的stage。
5, Texture blending cascade
Texture blending cascade是一个pipeline,它完成在一个pass中混合多个纹理这个任务。
6, Texture Stage(也叫做texture unit)
Texture blending cascade由许多Texture Stage构成,每个Texture Stage混合两个纹理(或经过计算的顶点集),通常是RGB和Alpha值,并将结果(经过计算的顶点集)传递给下一个Texture Stage。
理解了上面的那些概念,理解SetTextureStageState函数就很容易了。
HRESULT SetTextureStageState(
DWORD Stage,
D3DTEXTURESTAGESTATETYPE Type,
DWORD Value
);
1, Stage参数,D3D支持8个Texture Unit,索引值由0~7,通过此参数你可以指定是哪一个Texture Unit。
2, Type参数,用来选择Texture Stage不同的状态,如D3DTSS_COLOROP代表颜色混合操作,D3DTSS_ALPHAOP代表ALPHA值混合操作。
3, Value参数,根据不同的Type,来设置其状态值。如:SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_ADD ),就是指在第一个Texture Unit中,将两个颜色值的混合操作设定为累加。
D3DTSS_COLORARG1等其余的参数在D3D Document里写的很详细,就不多说了。
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
HRESULT SetTextureStageState(
DWORD Stage,
D3DTEXTURESTAGESTATETYPE Type,
DWORD Value
);
stage这个参数是指第几层纹理,1.2.3...9,, 这个版本的dx最多支持9层纹理。
Type:Defines the type of operation that a texture stage will perform.//定义对该纹理的哪个属进行设置,值很多。。。
Value: 指的是前面所选属性的值
type:
D3DTSS_ALPHAOP = 4, //alpha通道的运算,
D3DTSS_COLOROP = 1, //颜色的运算
//这里的op 是operations,指对前面设置的颜色进行运算
//既后面的2个type:D3DTSS_COLORARG1,D3DTSS_COLORARG2
//或D3DTSS_ALPHAARG1,D3DTSS_ALPHAARG2 = 6
value:
D3DTOP_DISABLE = 1, //该纹理无效,既不显示
D3DTOP_SELECTARG1 = 2, //Arg1
D3DTOP_SELECTARG2 = 3, //Arg2
D3DTOP_MODULATE = 4, //Arg1 * Arg2
D3DTOP_MODULATE2X = 5, //Arg1 * Arg2 * 2
D3DTOP_MODULATE4X = 6, //Arg1 * Arg2 * 4
D3DTOP_ADD = 7, // Arg1 + Arg2
D3DTOP_ADDSIGNED = 8, //(Arg1 + Arg2 - 0.5)
D3DTOP_ADDSIGNED2X = 9, //(Arg1 + Arg2 - 0.5)*2
D3DTOP_SUBTRACT = 10, //Arg1 - Arg2
D3DTOP_ADDSMOOTH = 11, //Arg1 + Arg2 – Arg1 * Arg2
D3DTOP_BLENDDIFFUSEALPHA = 12, //将Arg1与Arg2使用Alpha值进行线性插值。这是标准的Alpha混合效果。纹理参数常数 TextureArgumentConstants同样可以设置到Value里
D3DTOP_BLENDTEXTUREALPHA = 13,
D3DTOP_BLENDFACTORALPHA = 14,
D3DTOP_BLENDTEXTUREALPHAPM = 15,
D3DTOP_BLENDCURRENTALPHA = 16,
D3DTOP_PREMODULATE = 17,
D3DTOP_MODULATEALPHA_ADDCOLOR = 18,
D3DTOP_MODULATECOLOR_ADDALPHA = 19,
D3DTOP_MODULATEINVALPHA_ADDCOLOR = 20,
D3DTOP_MODULATEINVCOLOR_ADDALPHA = 21,
D3DTOP_BUMPENVMAP = 22,
D3DTOP_BUMPENVMAPLUMINANCE = 23,
D3DTOP_DOTPRODUCT3 = 24,
D3DTOP_MULTIPLYADD = 25, //SRGBA = Arg1 + Arg2 * Arg3
D3DTOP_LERP = 26,
D3DTOP_FORCE_DWORD = 0x7fffffff,
type:
D3DTSS_COLORARG1 = 2,
D3DTSS_COLORARG2 = 3,
D3DTSS_ALPHAARG1 = 5,
D3DTSS_ALPHAARG2 = 6,
D3DTSS_COLORARG0 = 26,
D3DTSS_ALPHAARG0 = 27,
D3DTSS_RESULTARG = 28,
value:
这里的TA指的是texture arguments ,
D3DTA_CURRENT 表示上一个Stage混合的结果。Stage=0时,这个值表示的是D3DTA_DIFFUSE
D3DTA_CONSTANT //给当前纹理一个固定的值;
D3DTA_DIFFUSE; //diffuse的值作为参数 diffuse 可能有多个来源。。比如材质,vertex
D3DTA_SELECTMASK //Mask value for all arguments; not used when setting texture arguments 这句话不理解啊,为什么要伪装呢
D3DTA_SPECULAR //取spercular 的值作为参数 来源同diffuse
D3DTA_TEMP //待定。。
D3DTA_TEXTURE //用纹理的颜色值作为参数
D3DTA_TFACTOR //待定。。
D3DTSS_BUMPENVMAT00 = 7,
D3DTSS_BUMPENVMAT01 = 8,
D3DTSS_BUMPENVMAT10 = 9,
D3DTSS_BUMPENVMAT11 = 10,
D3DTSS_TEXCOORDINDEX = 11,
D3DTSS_BUMPENVLSCALE = 22,
D3DTSS_BUMPENVLOFFSET = 23,
D3DTSS_TEXTURETRANSFORMFLAGS = 24,
D3DTSS_CONSTANT = 32,
D3DTSS_FORCE_DWORD = 0x7fffffff,
m_pD3DDevice->SetTexture( 0, m_pTexture0 ); // 土地材質
m_pD3DDevice->SetTextureStageState( 0, D3DTSS_TEXCOORDINDEX, 0 );
// TextureStage 0 (不做任何改變)
m_pD3DDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_SELECTARG1 );
m_pD3DDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
m_pD3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_DISABLE );
//------------
m_pD3DDevice->SetTexture( 1, m_pTexture1 ); // 草地材質
m_pD3DDevice->SetTextureStageState( 1, D3DTSS_TEXCOORDINDEX, 0 );
// TextureStage 1 (使用頂點alpha值計算混合)
m_pD3DDevice->SetTextureStageState( 1, D3DTSS_COLOROP, D3DTOP_BLENDDIFFUSEALPHA );
m_pD3DDevice->SetTextureStageState( 1, D3DTSS_COLORARG1, D3DTA_TEXTURE );
m_pD3DDevice->SetTextureStageState( 1, D3DTSS_COLORARG2, D3DTA_CURRENT );
m_pD3DDevice->SetTextureStageState( 1, D3DTSS_ALPHAOP, D3DTOP_DISABLE );
//------------
// TextureStage 2 (使用光影遮罩圖) // 光影材質
m_pD3DDevice->SetTexture( 2, m_pTexture2 );
m_pD3DDevice->SetTextureStageState( 2, D3DTSS_TEXCOORDINDEX, 1 );
m_pD3DDevice->SetTextureStageState( 2, D3DTSS_COLOROP, D3DTOP_MODULATE2X );
m_pD3DDevice->SetTextureStageState( 2, D3DTSS_COLORARG1, D3DTA_TEXTURE );
m_pD3DDevice->SetTextureStageState( 2, D3DTSS_COLORARG2, D3DTA_CURRENT );
m_pD3DDevice->SetTextureStageState( 2, D3DTSS_ALPHAOP, D3DTOP_DISABLE );
//------------
// TextureStage 3 (使用頂點值計算陰影)
m_pD3DDevice->SetTextureStageState( 3, D3DTSS_COLOROP, D3DTOP_MODULATE );
m_pD3DDevice->SetTextureStageState( 3, D3DTSS_COLORARG1, D3DTA_DIFFUSE );
m_pD3DDevice->SetTextureStageState( 3, D3DTSS_COLORARG2, D3DTA_CURRENT );
m_pD3DDevice->SetTextureStageState( 3, D3DTSS_ALPHAOP, D3DTOP_DISABLE );
黑暗纹理
通过纹理映射来模拟逐像素光照效果,通常是将第一层纹理设置为物体原来的表面纹理,将第二层纹理设置为光照纹理,然后将两张纹理的颜色相乘,所以有时将两张纹理的颜色相乘称为光照映射(light mapping)。由于这种技术经常被用于使一张纹理变暗,有时也称为黑暗映射(dark mapping)。
混合纹理与顶点漫反射颜色
当很强的阳光照射在物体表面上时,会使它表面的颜色变得更加明亮,这可以通过将纹理与顶点的漫反射颜色相混合来模拟这种效果。当一个白色材质反射一个方向光时,反射量越多,就意味着纹理颜色在最终显示结果中所占的成分越少。因此,那些被光直接照射到表面会呈现出白色。示例代码如下:
// setup light
ZeroMemory(&g_light, sizeof(D3DLIGHT9));
g_light.Type = D3DLIGHT_DIRECTIONAL;
g_light.Diffuse.r = 0.5f;
g_light.Diffuse.g = 0.5f;
g_light.Diffuse.b = 0.5f;
D3DXVECTOR3 light_dir(0, 0, 10);
D3DXVec3Normalize((D3DXVECTOR3*) &g_light.Direction, &light_dir);
// setup material
ZeroMemory(&g_material, sizeof(D3DMATERIAL9));
g_material.Ambient.r = 1.0f;
g_material.Ambient.g = 1.0f;
g_material.Ambient.b = 1.0f;
g_material.Ambient.a = 1.0f;
g_material.Diffuse.r = 0.7f;
g_material.Diffuse.g = 0.7f;
g_material.Diffuse.b = 0.7f;
g_material.Diffuse.a = 0.5f;
pd3dDevice->SetRenderState(D3DRS_LIGHTING, TRUE);
pd3dDevice->SetRenderState(D3DRS_AMBIENT, 0x00808080);
pd3dDevice->SetLight(0, &g_light);
pd3dDevice->LightEnable(0, TRUE);
pd3dDevice->SetMaterial(&g_material);
pd3dDevice->SetTexture(0, g_base_texture);
pd3dDevice->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_ADD);
pd3dDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
发光映射
发光映射(glowing mapping)与黑暗映射正好相反,它对于模拟那些具有独立于基础贴图的发光部分的物体很有用,比如模拟发光二极管、按钮、建筑物内的灯光、太空船上的灯光等。发光映射应仅影响基础贴图上的发光区域,而不影响其他部分。因此需要对发光效果做加法,而不是做乘法。
pd3dDevice->SetTexture(0, g_base_texture);
pd3dDevice->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
pd3dDevice->SetTexture(1, g_dark_texture);
pd3dDevice->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 0);
pd3dDevice->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE);
pd3dDevice->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT);
pd3dDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_ADD);
细节映射
如果要模拟一块粗糙的石灰泥墙壁,可以通过细节映射(detail mapping)来实现。实现过程是:将基础贴图(也就是第一张纹理)的颜色未经修改便作为第二个纹理操作阶段中的第二个参数,然后通过D3DTOP_ADDSIGNED将灰色的细节纹理与基础贴图相加。这个操作本质上是做了一个加法,只是使用了有符号的颜色值来代替平时使用的无符号值。在对两张纹理的像素颜色进行D3DTOP_ADDSIGNED操作时,它将参数的每个成分相加后再减去偏移量0.5,从而使有效值域变为-0.5 ~ 0.5。对一些比较旧的显卡,当其不能支持D3DTOP_ADDSIGNED操作时,可以使用D3DTOP_MODULATE2X代替D3DTOP_ADDSIGNED操作进行模拟。
在细节贴图中较亮的灰色纹理元素会使基础贴图变得更亮,而较暗的灰色纹理元素会使基础贴图变得更暗。由此可使物体呈现出粗糙的表面,从而使之看上去更为真实。示例代码如下:
pd3dDevice->SetTexture(0, g_base_texture);
pd3dDevice->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
pd3dDevice->SetTexture(1, g_detail_texture);
pd3dDevice->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 0);
pd3dDevice->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE);
pd3dDevice->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT);
hr = pd3dDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_ADDSIGNED);
if(FAILED(hr))
pd3dDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_MODULATE2X);
ALPHA混合操作
Direct3D在渲染一个场景时,它可以结合几种来源的颜色信息:顶点、当前材质、纹理贴图、先前写入渲染目标的颜色信息,然后将其中的一些颜色混合起来。同时也可以使用Alpha来指定Direct3D该以怎样的权重混合这些颜色,Alpha信息可以存储在顶点中、材质中、纹理贴图中。Alpha值为0表示完全透明,Alpha值为1表示不透明,其余0~1之间的值表示不同程度的透明。
如果要从一张纹理中获取Alpha值,应将D3DTA_TEXTURE作为Alpha参数。
如果要使用来自顶点中的Alpha值,应将D3DTA_DIFFUSE作为Alpha参数,并确保渲染状态D3DRS_DIFFUSEMATERIALSOURCE被设置为D3DMCS_COLOR1(这也是默认状态)。
如果要使用来自材质中的Alpha值,应将D3DTA_DIFFUSE作为Alpha参数,并确保渲染状态D3DRS_DIFFUSEMATERIALSOURCE被设置为D3DMCS_MATERIAL。
如果未用SetRenderState()设置D3DRS_DIFFUSEMATERIALSOURCE参数,则从默认来源(即顶点)获取漫反射颜色。
pd3dDevice->SetTexture(0, g_texture);
pd3dDevice->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
纹理坐标自动生成
在Direct3D程序中,不仅可以在模型载入阶段或渲染阶段指定物体的纹理坐标,还可以通过Direct3D渲染引擎自动生成纹理坐标,用于诸如环境映射等特殊的视觉效果。与手动设置纹理坐标相比,纹理坐标自动生成在Direct3D坐标变换和光照流水线中完成,执行速度更快。
Direct3D系统可以使用经过变换的摄像机空间顶点位置坐标、法线信息来生成纹理坐标。如果使用纹理坐标自动生成,那么在顶点中就可以不用包含纹理坐标数据,从而可以降低图形渲染时的数据传输量。纹理坐标自动生成主要用于产生一些特殊效果,在大多数情况下还是手工为每个顶点指定纹理坐标。
通过调用SetTextureStageState()并将第二个参数设置为D3DTSS_TEXCOORDINDEX来控制Direct3D系统如何自动生成纹理坐标。
D3DTSS_TEXCOORDINDEX用于指定特定纹理层使用顶点中的第几组纹理坐标,但如果指定了上表中的成员值,Direct3D将忽略顶点中的纹理坐标,转而使用自动生成的纹理坐标。
D3DTSS_TEXTURETRANSFORMFLAGS用来控制生成的纹理坐标的输出,在大多数情况下纹理坐标是二维的,即将D3DTSS_TEXTURETRANSFORMFLAGS设置为D3DTTFF_COUNT2。但当绘制线段或三维纹理时,纹理坐标可能是一维或三维的。
纹理坐标变换
Direct3D提供了对生成的纹理坐标进行坐标变换的功能,与顶点坐标变换相类似,可以指定一个4x4的纹理坐标变换矩阵,把它与生成的纹理坐标相乘,然后将变换之后的纹理坐标输出至Direct3D渲染流水线。使用纹理坐标变换可以对纹理坐标进行诸如平移、旋转和缩放等三维变换。纹理坐标变换对于生成一些特殊效果是非常有用的,它不用直接修改顶点的纹理坐标。例如可以通过一个简单的平移矩阵对纹理坐标进行变换,从而使物体表面上的纹理不断变换位置,产生动画效果。纹理坐标自动生成在三维图形程序中最广泛的应用是环境映射。
可通过函数IDirect3DDevice9::SetTransform()来设置4x4的纹理坐标变换矩阵,它以D3DTS_TEXTURE0~ D3DTS_TEXTURE7作为第一个参数,表示设置纹理层0~7的纹理矩阵。下列代码对纹理层0设置了一个将纹理坐标u、v缩小到原来一半的纹理矩阵:
D3DXMATRIX mat;
D3DXMatrixIdentity(&mat);
mat._11 = 0.5f;
mat._22 = 0.5f;
pd3dDevice->SetTransform(D3DTS_TEXTURE0, &mat);
下面的代码将原来的纹理坐标平移(1.0, 1.0, 0)个单位。
D3DXMATRIX mat;
D3DXMatrixIdentity(&mat);
mat._41 = 1.0f;
mat._42 = 1.0f;
mat._43 = 0.0f;
pd3dDevice->SetTransform(D3DTS_TEXTURE0, &mat);
示例程序通过下列代码对自动生成的纹理坐标进行变换:
// texture coordinate transform
D3DXMATRIX mat_texture, mat_scale, mat_trans;
D3DXMatrixIdentity(&mat_texture);
D3DXMatrixScaling(&mat_scale, 0.5f, -0.5f, 1.0f);
D3DXMatrixTranslation(&mat_trans, 0.5f, 0.5f, 1.0f);
mat_texture = mat_texture * mat_scale * mat_trans;
pd3dDevice->SetTransform(D3DTS_TEXTURE0, &mat_texture);
立方体环境映射
立方体环境映射图有时又称为立方体映射图,是一组包含物体周围环境图像的纹理贴图,好像物体在立方体的中心。立方体环境图的每个面覆盖水平和垂直方向上各90度视角区域,一共6个面.
球形环境映射
球形环境映射图(或称为球形映射图)和立方体环境映射图类似,也是一幅包含周围场景图像的特殊纹理。和立方体环境映射图不同的是,球形环境映射图不直接代表物体周围的环境。球形映射图就好像通过鱼眼(fish-eye)凸透镜观察到的景象一样,是一个物体周围环境360度全方位视域的三维表现。
首先使用一张球形的背景图作为环境映射纹理,接着在回调函数OnCreateDevice()中加载纹理和网格模型,接着在回调函数OnResetDevice()中自动生成纹理坐标,并设置纹理阶段颜色混合方法,最后在回调函数OnFrameRender()中渲染茶壶:
V_RETURN(D3DXCreateTextureFromFile(pd3dDevice, L"spheremap.bmp", &g_env_texture));
ID3DXBuffer* material_buffer;
V_RETURN(D3DXLoadMeshFromXW(L"teapot.x", D3DXMESH_MANAGED, pd3dDevice, NULL, &material_buffer, NULL, &g_num_materials, &g_mesh));
D3DXMATERIAL* xmaterials = (D3DXMATERIAL*) material_buffer->GetBufferPointer();g_mesh_materials = new D3DMATERIAL9[g_num_materials];
for(DWORD i = 0; i < g_num_materials; i++){ g_mesh_materials[i] = xmaterials[i].MatD3D;
// .x file do not save ambient data, so set it here. g_mesh_materials[i].Ambient = g_mesh_materials[i].Diffuse; }
material_buffer->Release();
pd3dDevice->SetTexture(0, g_env_texture);
pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
pd3dDevice->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_SPHEREMAP);
pd3dDevice->SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2);
// 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() ) )
{ for(DWORD i = 0; i < g_num_materials; i++)
{
pd3dDevice->SetMaterial(&g_mesh_materials[i]);
g_mesh->DrawSubset(i);
}
RenderText();
V(g_button_dlg.OnRender(fElapsedTime));
V( pd3dDevice->EndScene() );
}
DXT纹理压缩
要渲染看起来真实的场景,最好是使用高分辨率而且颜色丰富的纹理,但这样的纹理可能会耗费大量的内存,例如,一张每像素16位颜色的256 x 256纹理将使用128KB的内存。如果在该纹理中使用多级渐进纹理,还需要额外的43KB内存。一个使用50张这种纹理的场景将需要8MB的内存,如果需要更强的真实性,可以使用每像素32位颜色的512 x 512纹理,但那就需要8倍的内存。
为了减少纹理消耗的系统带宽和内存空间,Direct3D支持纹理压缩和实时解压,即DXT纹理压缩。压缩后的纹理被存储在Direct3D纹理指针中,当Direct3D渲染物体时,Direct3D引擎自动对纹理进行解压。应用DXT压缩纹理不仅可以节省内存空间,而且能有效地降低纹理传输带宽,提高图形系统的整体性能。
随着DirectX对纹理压缩格式的推广,目前大部分显卡都支持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。
DXT1格式的压缩比例是4 : 1(4x4块16位RGB纹理元素可压缩为64位,2个16位RGB565值和16个2位索引),这样的压缩比并不很高,但足以有效地将3D加速卡用于存储纹理的容量提高4倍
纹理管理
如果显卡中只有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调度和渲染纹理的技巧。
以上是关于Direct3D中 SetTextureStageState 函数的主要内容,如果未能解决你的问题,请参考以下文章
[翻译]观察变换View Transform (Direct3D 9)