int maxActiveTextures = Caps.MaxSimultaneousTextures;
如下代码所示如果MaxSimultaneousTextures < 4 将不能输出图形。注意:即使相同的纹理也算1个数量。如果做的纹理效果多于MaxSimultaneousTextures ,可以拆成多次调用DrawPrimitive 函数,达到相同效果。不过对于速度有影响
pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
pd3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_SELECTARG1 );
pd3dDevice->SetTextureStageState( 1, D3DTSS_TEXCOORDINDEX, 0);
pd3dDevice->SetTextureStageState( 1, D3DTSS_COLOROP, D3DTOP_MODULATE );
pd3dDevice->SetTextureStageState( 1, D3DTSS_COLORARG1, D3DTA_TEXTURE );
pd3dDevice->SetTextureStageState( 1, D3DTSS_COLORARG2, D3DTA_CURRENT );
pd3dDevice->SetTextureStageState( 2, D3DTSS_TEXCOORDINDEX, 0);
pd3dDevice->SetTextureStageState( 2, D3DTSS_COLORARG1, D3DTA_TEXTURE );
pd3dDevice->SetTextureStageState( 2, D3DTSS_COLORARG2, D3DTA_CURRENT );
pd3dDevice->SetTextureStageState( 2, D3DTSS_COLOROP, D3DTOP_ADD );
pd3dDevice->SetTextureStageState( 3, D3DTSS_TEXCOORDINDEX, 0);
pd3dDevice->SetTextureStageState( 3, D3DTSS_COLORARG1, D3DTA_TEXTURE );
pd3dDevice->SetTextureStageState( 3, D3DTSS_COLORARG2, D3DTA_CURRENT );
pd3dDevice->SetTextureStageState( 3, D3DTSS_COLOROP, D3DTOP_ADD );
pd3dDevice->SetFVF(FVF );
pd3dDevice->SetStreamSource( 0, g_pVB, 0, sizeof(VERTEX) );
pd3dDevice->DrawPrimitive (D3DPT_TRIANGLESTRIP, 0, 2);
{ 0.0f, 0.0f, 0.5f, 1.0f, 0.0f, 0.0f,},
{ width,0.0f, 0.5f, 1.0f, 1.0f,0.0f, },
{ 0.0f,height, 0.5f, 1.0f, 0.0f, 1.0f,}, // x, y, z, rhw, tu, tv
{ width,height, 0.5f, 1.0f, 1.0f,1.0f,},
};
//int height = m_d3dsdBackBuffer->Height;
//int width = m_d3dsdBackBuffer->Width;
int height = pBackBufferSurfaceDesc->Height;
int width = pBackBufferSurfaceDesc->Width;
m_d3dsdBackBuffer->Release(); //一定要释放。
通过给纹理指定无效区域,应用程序可以对需要复制纹理的哪些子集进行优化,只有那些被标记为无效的区域才会被IDirect3DDevice9::UpdateTexture方法更新。当创建纹理时,整个纹理被标记为无效的。只有以下几种操作可以改变纹理的无效状态。
- 给一个纹理添加一个无效区域。
- 锁定纹理中的一些区域。此操作会把被锁定的区域添加到无效区域中,如果应用程序明确知道哪些是真正的无效区域,那么也可以关闭对无效区域的自动更新。
- 将纹理作为目标表面进行更新的话会把整个纹理标记为无效的。
- 对纹理调用IDirect3DDevice9::UpdateTexture方法会清除该纹理的所有无效区域。
- 为了得到设备上下文(device context)而调用IDirect3DDevice9::GetDC。
对于mipmap纹理而言,无效区域被设在最高一级的纹理上,为了最小化对mipmap纹理中每一级的纹理更新所需复制的字节数,IDirect3DDevice9::UpdateTexture方法可以扩展无效区域并沿mipmap链更新子纹理。注意子级中无效区域的纹理坐标被向上舍入,也就是说,它们的小数部分被向上取整到纹理中最近的像素。
因为每种类型的纹理具有不同类型的无效区域,所以每种类型的纹理都有相应的方法表示无效区域。二维纹理使用矩形,立体纹理使用立方体。
- IDirect3DCubeTexture9::AddDirtyRect
- IDirect3DTexture9::AddDirtyRect
- IDirect3DVolumeTexture9::AddDirtyBox
把以上方法的pDirtyRect或pDirtyBox参数设置为NULL会扩大无效区域并使之覆盖整个纹理。
每种锁定方法都有D3DLOCK_NO_DIRTY_UPDATE标志,使用这个标志可以防止对纹理无效区域的改变。更多信息,请参阅锁定资源。
如果在锁定操作时可以得到已改变区域的完整集合,那么应用程序应该使用D3DLOCK_NO_DIRTY_UPDATE标志。注意,对纹理一个的子级的锁定或复制操作(也就是说,未对纹理的最高一级进行锁定或复制操作)不会更新该纹理的无效区域。当应用程序锁定了纹理的子级而没有锁定纹理的最高一级时,它同样有责任对无效区域进行更新。
6 固定管线怎么没有使用 SetFVF
看了一个程序dxquake3中没有使用SetFVF,但也没有顶点或者像素shader。其实FVF在内部被DX转换为VertexDeclaration,所以用FVF或是VertexDeclaration都行。不使用SetFVF而是使用SetVertexDeclaration和SetVertexShader代替,SetVertexDeclaration声明数据存储格式,SetVertexShader提供数据处理方法。SetFVF 是 dx9 新函数为了使写法更简单。dxquake3 中使用SetVertexDeclaration来提供的顶点格式,函数参数类似于
D3DVERTEXELEMENT9 decl[] =
{
{ 0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },
{ 0, 12, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0 },
D3DDECL_END()
};
但没有使用SetVertexShader设置着色器。相对于使用SetFVF,使用SetVertexDeclaration更有利于以后的扩展,实现可编程shader
7 使用mesh调用ConvertToBlendedMesh 失败, 如果创建设备时设置 D3DCREATE_HARDWARE_VERTEXPROCESSING 很可能因为顶点不够用而失败
使用 D3DCREATE_MIXED_VERTEXPROCESSING 参数会更好(使用skinmesh noindex 例子时候)。
8 加入d3dx9dt.lib 而不是d3dx9.lib 这样对于没有 Release() 的资源会才报内存泄漏(调用 _CrtSetDbgFlag ) 。而 vld 之类的内存泄漏库才能检测到没有Release 的对象
9 关键色
加载图片的时候:
D3DXCreateTextureFromFileEx(m_pd3dDevice, "test.bmp", D3DX_DEFAULT, D3DX_DEFAULT,1,0, D3DFMT_UNKNOWN,
D3DPOOL_DEFAULT,D3DX_FILTER_LINEAR, D3DX_FILTER_LINEAR,0xffff00ff,NULL,NULL,&m_texture);
~~~~~~~~~~ColorKey
渲染的时候:
渲染前
m_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE,TRUE);
m_pd3dDevice->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_SRCALPHA); //使用加载图片 srcalpha
m_pd3dDevice->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA); //使用 1-srcalpha
渲染
render()
渲染后恢复
m_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE,FALSE);
m_pd3dDevice->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_ONE);
m_pd3dDevice->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_ZERO);
估计是加载的时候判断颜色相同。设置该像素 alpha 为 0
10
Lock VertexBuffer 时。使用偏移地址要同时指标值 D3DLOCK_NOOVERWRITE .这样除了更新的一小部分。其他部分不会被丢弃掉,当然是用默认参数0也可以。但用0效率很差
对于动态缓存。使用偏移地址并不能增加多效效率。因为偏移的时候不能使用 DISCARD.不然没被更新的部分也会被丢弃。而往往希望没有更新的保留。不使用偏移地址将更新缓存所有地方。(特别是更新完整个缓存才unlock,反倒能快一些)
上面认识错误。DISCARD 表示自己不再用那块显存。马上给我一块新的。原来用的位置可以被设备使用(如果还用完里面的顶点数据)。数据不会丢弃里面的内容。"丢弃"意味开发人员不再用了。至于何时扔掉有directx 管理
同样NOOVERWRITE 也不是不覆盖的意思。只是表示开发者认为写入的位置不会覆盖被dx正在使用的顶点(draw),不用等待显卡draw结束(默认参数0表示等待结束),直接覆盖掉相应位置顶点数据(即使该顶点正在用来画图也会被强制覆盖)。这样反倒是表现起来截然"相反"了
如果缓存数据没有变换。不要更新他.(只更新变化的数据能提高速度).一段缓存需要几次更新。可以开始锁定一次。最后unlock 就好。这个似乎不太影响性能
11 D3DXLoadSurfaceFromMemory 的使用。可以把一种格式转换为另一种格式
IDirect3DSurface9* temp;
d3dOK( m_aTex[tex].m_pTex->GetSurfaceLevel(level,&temp) );
int w = Get2PowerHigh(sz.cx); //即 pitch 对齐到32位置
//源缓存的尺寸
RECT srcRc = { 0, 0, w, sz.cy };
RECT dstRc = rt;
//pixelFormatBits 得到的是每种表面格式的字节数. 可以参考dxtex 程序 如 D3DFMT_A8R8G8B8 返回 32
unsigned char bytes = PixelFormatBits(pf)/8;
HRESULT hr;
//pitch = w * bytes 必须用纹理的宽度。这个宽度不一定等于参数width。指纹理pitch而不是buffer缓存数组的宽度
//IDirect3DSurface9 是目标区域的 surface,第二个可以是NULL(256色的时代早就过去了). rt 是目标纹理需要更新的区域(2的幂次方可以有效防止失真)
//data 是颜色缓冲数据。每个元素(a8r8g8b8)代表一个颜色值。pf data 的颜色格式(如 D3DFMT_A8R8G8B8)
//NULL 为调色板,srcRc 为缓冲区的一块区域。D3DX_FILTER_NONE 过滤模式
//最后参数为关键色
if( FAILED(hr = D3DXLoadSurfaceFromMemory(temp,NULL,&rt,data,pf,w*bytes,NULL,&srcRc,D3DX_FILTER_NONE,0)))
{
ri->Error(ERR_D3D,_T("更新纹理,转换表面格式时失败:%0x\n"),hr);
}
if(m_aTex[tex].m_bmipmap)
{
d3dOK( D3DXFilterTexture( m_aTex[tex].m_pTex,NULL,0,D3DX_DEFAULT ) );
}