(转)学习directx遇到的问题

1 MaxSimultaneousTextures 纹理同时叠加最大数目
device->GetDeviceCaps( &Caps );
int maxActiveTextures = Caps.MaxSimultaneousTextures;
即在 DrawPrimitive  顶点之前,最多设置的纹理数量。如果你的机器显卡较差,这个数为2,就是刚刚好支持多纹理。
如下代码所示如果MaxSimultaneousTextures < 4 将不能输出图形。注意:即使相同的纹理也算1个数量。如果做的纹理效果多于MaxSimultaneousTextures ,可以拆成多次调用DrawPrimitive 函数,达到相同效果。不过对于速度有影响
例如 quake3 最多有10次渲染之多
 pd3dDevice->SetTexture( 0, g_pBackgroundTexture );
  pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
  pd3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP,   D3DTOP_SELECTARG1 );
  pd3dDevice->SetTexture( 1, g_pWallTexture);
  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->SetTexture(2, g_pEnvTexture);
  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->SetTexture(3, g_pEnvTexture);
  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);
 
2 DrawPrimitive()函数的 D3DPT_TRIANGLESTRIP  标志用的顶点第一个三角形为顺时针,第二个逆时针。。依次迭代。
例如:记住uv 顺序即可 (0,0)->(1,0)->(0,1)->(1,1)
VERTEX Vertices[] =
 {
  {  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,},
 };
 
3 获取窗口高度和宽度 IDirect3DSurface9,注意IDirect3DSurface9 Height 和 Width成员都是1。 #ifdef D3D_DEBUG_INFO这个宏时包围了这些成员。应该通过GetDesc获得表面属性,D3DSURFACE_DESC里面的height和width 才正确。
 IDirect3DSurface9* m_d3dsdBackBuffer;
 pd3dDevice->GetBackBuffer(0,0,D3DBACKBUFFER_TYPE_MONO,&m_d3dsdBackBuffer);
 //int height = m_d3dsdBackBuffer->Height;
 //int width = m_d3dsdBackBuffer->Width;
 m_d3dsdBackBuffer->GetDesc(pBackBufferSurfaceDesc)
 int height = pBackBufferSurfaceDesc->Height;
 int width = pBackBufferSurfaceDesc->Width;
 m_d3dsdBackBuffer->Release();  //一定要释放。
 
4 创建设备时指定D3DCREATE_PUREDEVICE标志。可以参考D3DPRESENT_PARAMETERS参数说明
 
5 无效纹理区域
  通过给纹理指定无效区域,应用程序可以对需要复制纹理的哪些子集进行优化,只有那些被标记为无效的区域才会被IDirect3DDevice9::UpdateTexture方法更新。当创建纹理时,整个纹理被标记为无效的。只有以下几种操作可以改变纹理的无效状态。
  • 给一个纹理添加一个无效区域。
  • 锁定纹理中的一些区域。此操作会把被锁定的区域添加到无效区域中,如果应用程序明确知道哪些是真正的无效区域,那么也可以关闭对无效区域的自动更新。
  • 将纹理作为目标表面进行更新的话会把整个纹理标记为无效的。
  • 对纹理调用IDirect3DDevice9::UpdateTexture方法会清除该纹理的所有无效区域。
  • 为了得到设备上下文(device context)而调用IDirect3DDevice9::GetDC

对于mipmap纹理而言,无效区域被设在最高一级的纹理上,为了最小化对mipmap纹理中每一级的纹理更新所需复制的字节数,IDirect3DDevice9::UpdateTexture方法可以扩展无效区域并沿mipmap链更新子纹理。注意子级中无效区域的纹理坐标被向上舍入,也就是说,它们的小数部分被向上取整到纹理中最近的像素。

因为每种类型的纹理具有不同类型的无效区域,所以每种类型的纹理都有相应的方法表示无效区域。二维纹理使用矩形,立体纹理使用立方体。

  • IDirect3DCubeTexture9::AddDirtyRect
  • IDirect3DTexture9::AddDirtyRect
  • IDirect3DVolumeTexture9::AddDirtyBox

把以上方法的pDirtyRectpDirtyBox参数设置为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 ) );
 }

posted @ 2010-10-11 17:48  oayx  阅读(2015)  评论(0编辑  收藏  举报