﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-大渊献-随笔分类-DirectX</title><link>http://www.cppblog.com/Leaf/category/10277.html</link><description>天之道,损有余而补不足,是故虚胜实，不足胜有余</description><language>zh-cn</language><lastBuildDate>Tue, 21 Dec 2010 05:37:59 GMT</lastBuildDate><pubDate>Tue, 21 Dec 2010 05:37:59 GMT</pubDate><ttl>60</ttl><item><title>关于骨骼动画及微软示例Skinned Mesh的解析 </title><link>http://www.cppblog.com/Leaf/archive/2009/11/16/101033.html</link><dc:creator>大渊献</dc:creator><author>大渊献</author><pubDate>Sun, 15 Nov 2009 16:47:00 GMT</pubDate><guid>http://www.cppblog.com/Leaf/archive/2009/11/16/101033.html</guid><wfw:comment>http://www.cppblog.com/Leaf/comments/101033.html</wfw:comment><comments>http://www.cppblog.com/Leaf/archive/2009/11/16/101033.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Leaf/comments/commentRss/101033.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Leaf/services/trackbacks/101033.html</trackback:ping><description><![CDATA[<p>&nbsp;</p>
原文链接：<a href="http://www.gameres.com/document.asp?TopicID=87707">http://www.gameres.com/document.asp?TopicID=87707</a><br><br>这是我自个写的,第一次发. 没想到这个贴子编辑器极差. 原文是有字体字色的.现在只能清一色了.<br>版主,发贴的编辑器太难用! 你有必要向上反映一下. 下面的字体是我敲html标记加上的,大家凑和看.
<p>&nbsp;</p>
<p><font size=3><strong>关于骨骼动画及微软示例Skinned Mesh的解析</strong></font></p>
<p>骨骼动画是D3D的一个重要应用。尽管微软DXSDK提供了示例Skinned Mesh，但由于涉及众多概念和技术细节，示例相对于初学者非常复杂，难以看懂。在此，提供一些重要问题评论，以使初学者走出迷局，顺利上手。文中所述都是参照各种资料加上自己的理解，也有可能出些偏差，有则回贴拍砖，无则权当一笑。</p>
<p><br><strong>一 骨骼动画原理</strong><br>原理方面在网上资料比较多，大家都基本明白。在此说一下重点：<br>总体上，绝大部分动画实现原理一致，就是&#8220;提供一种机制，描述各顶点位置随时间的变化&#8221;。有三种方法：<br><font color=#0000ff>1.1 关节动画：</font>由于大部分运动，都是皮肤随骨骼在动，皮肤相对于它的骨骼本身并没有发生运动，所以只要描述清楚骨骼的运动就行了。用矩阵描述各个骨骼的相对于父骨骼运动。(大多运动都是旋转型) 易知，从子骨骼用矩阵乘法累积到最顶层根骨骼，就可以得到每个子骨骼相对于世界坐标系的转换矩阵。<br>&nbsp;&nbsp;这种动画，只须用普通Mesh保存最初始的各顶点坐标，以及一系列后续时刻所对应的各骨骼的运动矩阵。不用保存每时刻的顶点数据，节省了大量存储空间。而且比较灵活，可以利用关键帧插值运算，便于通过运算调节动作。缺点是在两段骨骼交接处，容易产生裂缝，影响效果。</p>
<p><font color=#0000ff>1.2 渐变动画：</font>通过保存一系列时刻的顶点坐标来完成动画。虽然比较逼真，但占用大量空间，灵活性也不高。</p>
<p><font color=#0000ff>1.3 骨骼蒙皮动画(skinned Mesh)</font><br>&nbsp;&nbsp;相当于上面两方法的折中。现在比较流行。<br>&nbsp;&nbsp;在关节动画的基础上，利用顶点混合(Vertex Blend)技术，对于关节附近的顶点，由影响这些顶点的两段（或多段）骨骼运动，分别赋以权值，共同决定顶点位置。相当于在骨骼关节上动态蒙皮，有效解决了裂缝问题。</p>
<p>&nbsp;&nbsp;这里，引入一个D3D技术概念：<font color=#ff0000>&#8220;Vertex Blending&#8221;</font>---顶点混合技术。比如说，你肯定用过SetTransform(D3DTS_WORLD,....)，但SetTransform(<font color=#800080>D3DTS_WORLDMATRIX(i)</font>,....)是不是很奇怪？这个问题后文会讲到。 你也可以在微软的DXSDK的帮助文件中搜索&#8220;Geometry Blending&#8221;主题，有裂缝及其解决办法图示。</p>
<p>&nbsp;</p>
<p><strong>二 X文件如何保存骨骼动画</strong></p>
<p>理解X文件格式，对用好相关的DX函数是非常重要的。</p>
<p>不含动画的普通X文件，有一个Mesh单元，保存了各顶点信息、各三角面的索引信息、材质种类及定义等。</p>
<p>动画X文件，则在这个单元中增加了&#8220;各骨骼蒙皮信息&#8221;、&#8220;骨骼层次及结构信息&#8221;、&#8220;各时刻骨骼矩阵信息&#8221;等。</p>
<p><font color=#0000ff>2.1 网格蒙皮信息：</font>首先，在Mesh{}单元中，在原有的普通网格顶点数据基础上，新增了XSkinMeshHeader{}结构，以及多个SkinWeights{}结构。用以描述各个骨骼的蒙皮信息。</p>
<p>其中，XSkinMeshHeader是总括，举一实例，如下：</p>
<p>XSkinMeshHeader<br>{<br>2,//一个顶点可以受到骨骼影响的最大骨骼数,可用于计算共同作用时减少遍历次数<br>4,//一个三角面可以受到骨骼影响的最大骨骼数。这个数字对硬件顶点混合计算提出了基本要求。<br>35 //当前Mesh的骨骼总数。<br>}</p>
<p>由于每个骨骼的蒙皮信息都需要用SkinWeights结构去描述，所以有多少块骨骼，在Mesh中就有多少个SkinWeights对象。<br>注意，一般把SkinWeights视作Mesh的一部分。这种Mesh又称Skinned Mesh (蒙皮网格)</p>
<p>SkinWeights 结构如下：<br>{<br>&nbsp;&nbsp;STRING&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;transformNodeName;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//骨骼名<br>&nbsp;&nbsp;DWORD&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nWeights;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //权重数组的元素个数，即该骨骼相关的顶点个数<br>&nbsp;&nbsp;array DWORD vertexIndices[nWeights];//受该骨骼控制的顶点索引，实际上定义了该骨骼的蒙皮<br>&nbsp;&nbsp;array float weights[nWeights];&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//蒙皮各顶点的受本骨骼影响的权值<br>&nbsp;&nbsp;Matrix4x4&nbsp;&nbsp; matrixOffset;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //骨骼偏移矩阵，用来从初始Mesh坐标，反向计算顶点在子骨骼坐标系中的初始坐标。<br>}<br>在有的书中，把上面的matrixOffset叫骨骼权重矩阵，是不恰当的。应该称为骨骼偏移矩阵比较合适。</p>
<p><font color=#ff0000>[问题]</font> 在整个动画过程中，子骨骼运动矩阵的数值是不断变化的。上面的骨骼偏移矩阵变化吗？有没有必要重新计算？它在什么时候使用？<br>答：各骨骼的偏移矩阵matrixOffset专门用来从原始Mesh数据计算出各顶点相对于骨骼坐标系的原始坐标。在绘制前，把它与当前变换矩阵相乘，就可以得到该骨骼的当前的最终变换矩阵。 总之，骨骼偏移矩阵是与原始Mesh顶点数值相关联的，在整个动画过程中是不变的，也不应该变。在动画过程中变化是当前骨骼变换矩阵，可由.X中的AnimatonKey中的各时刻矩阵得到。这个矩阵乘法在示例中的对应代码如下：<br>D3DXMatrixMultiply( &amp;matTemp, &amp;pMeshContainer-&gt;pBoneOffsetMatrices[iMatrixIndex], pMeshContainer-&gt;ppBoneMatrixPtrs[iMatrixIndex] );</p>
<p>即，D3DXMatrixMultiply(输出最终世界矩阵, 该骨骼的偏移矩阵, 该骨骼的变换矩阵)</p>
<p><br><font color=#0000ff>2.2 骨骼层次信息</font></p>
<p>在X文件中，Frame是基本的组成单元。又称框架Frame。 一个.x可以有多个Frame。(注意此处的Frame不是帧，与帧没什么关系)</p>
<p>框架Frame允许嵌套，这样就存在父子框架了。而并列的框架，称为兄弟框架。这两种关系组合在一起，即可以纵深，又可以并列，形成一种层次结构。这种结构，可用二叉树描述。</p>
<p>每个框架结构的最前面，有一个FrameTransformMatrix矩阵数据，描述了该框架相对于父框架的变换矩阵。也就是说，该框架中的坐标，与该矩阵相乘，可转换为父框架坐标系的坐标。<br>这种层次结构，使得X文件能描述许多复杂的物体。如地形场景。</p>
<p>在骨骼动画文件中，框架结构可直接拿来描述人物骨骼的层次结构。框架的名字通常为对应的骨骼名。<br>如&#8220;左上臂-&gt;左前臂-&gt;手掌-&gt;手指&#8221;就形成一个父子骨骼链。而左上臂与右上臂是并行关系。</p>
<p>数据示例： D:\D9XSDK\Samples\Media\tiny.x</p>
<p>Frame ...{<br>&nbsp;&nbsp;.....</p>
<p>&nbsp;&nbsp;Frame Bip01_R_Calf { //子骨骼<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FrameTransformMatrix {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1.000000,-0.000691,-0.000000,0.000000,0.000691,1.000000,-0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,119.231522,0.000021,-0.000011,1.000000;;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Frame Bip01_R_Foot {//--孙子骨骼<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FrameTransformMatrix {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0.988831,0.124156,0.082452,0.000000,-0.122246,0.992109,-0.027835,0.000000,-0.085257,0.017445,0.996206,0.000000,119.231476,-0.000039,0.000023,1.000000;;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;....缩进<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></p>
<p><font color=#ff0000>[问题]</font>查看示例tiny.x文件，发现只有根框架下有一个Mesh，包含了所有顶点信息。其它各个Frame都没有Mesh数据。怎么理解？<br>答： 一般来说，每个动画文件只有一个Mesh网格，包含物体所有顶点信息。<br>&nbsp;&nbsp;&nbsp;&nbsp; 其它Frame，只是借用来描述各骨骼的层次信息,没必要再定义骨骼网格。每块骨骼对应的蒙皮顶点信息，由根Mesh中的相应骨骼的SkinWeights中蒙皮顶点索引描述的。在动画过程中，各个顶点的新坐标，要借助SkinWeights中的顶点索引来进行重新计算。</p>
<p><font color=#0000ff>2.3 动画信息：</font><br>由一系列AnimatonKey组成，数据示例如下：</p>
<p>&nbsp;&nbsp;AnimationKey {<br>&nbsp;&nbsp; 4;--动画类型 4表示矩阵<br>&nbsp;&nbsp; 62; --动画帧数，即下面矩阵个数<br>&nbsp;&nbsp; 0;16;1.000000,-0.000691,-0.000000,0.000000,0.000691,1.000000,0.000000,0.000000,0.000000,-0.000000,1.000000,0.000000,119.231514,-0.000005,0.000001,1.000000;;,<br>&nbsp;&nbsp; 80;16;0.992696,-0.120646,-0.000000,0.000000,0.120646,0.992696,0.000000,0.000000,-0.000000,-0.000000,1.000000,0.000000,119.231514,0.000002,-0.000002,1.000000;;,</p>
<p>&nbsp;&nbsp; ..上面红数字表示时刻tick,兰数字表示数值的个数。<br>&nbsp;&nbsp; ...其它各时刻矩阵...</p>
<p>&nbsp;&nbsp; { Bip01_R_Calf }--对应的骨骼对象引用<br>&nbsp;&nbsp;}</p>
<p><br>注意：<br>(1)每块骨骼都有一个AnimationKey{}.<br>(2)在上面数据结构中，主要保存了各典型时刻的该骨骼相对于父的变换矩阵.<br>(3)在0时刻的矩阵，与该骨骼对应的前面的Frame所对应的矩阵是相同的。如Frame Bip01_R_Calf{}中的变换矩阵，与Bip01_R_Calf所对应的AnimationKey 的第0时刻矩阵是一样的。这说明，在以后动画运行时，DX会提供一种功能，用AnimatonKey中的对应数据刷新初始的变换矩阵(也可能启用关键帧插值算法)。这个功能对应于示例中的m_pAnimController-&gt;SetTime(...)语句。</p>
<p><strong>三 怎样从X文件加载骨骼动画信息？</strong><br><font color=#0000ff>3.1 负责加载的函数：</font><br>&nbsp;&nbsp;可能有多种加载方式，在此以SDK中的示例为准，叙述一种标准加载方式，需要用到DX函数D3DXLoadMeshHierarchyFromX(),函数字面意思是读取Mesh层次信息。<br>HRESULT WINAPI <br>&nbsp;&nbsp;&nbsp;&nbsp;D3DXLoadMeshHierarchyFromX(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LPCSTR Filename,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //.x文件名<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DWORD MeshOptions,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //Mesh选项，一般选D3DXMESH_MANAGED<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LPDIRECT3DDEVICE9 pD3DDevice,&nbsp;&nbsp;&nbsp;&nbsp;//指向D3D设备Device<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LPD3DXALLOCATEHIERARCHY pAlloc,&nbsp;&nbsp;//<strong>自定义数据容器</strong><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LPD3DXLOADUSERDATA pUserDataLoader,&nbsp;&nbsp;//一般选NULL<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LPD3DXFRAME *ppFrameHierarchy,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //返回根Frame指针，指向代表整个骨架的Frame层次结构<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LPD3DXANIMATIONCONTROLLER *ppAnimController //返回相应的动画控制器<br>);</p>
<p>这个函数后面的两个输出参数很重要，也很好理解，但输入参数中的自定义数据容器是怎么回事呢？<br>原来，鉴于动画数据的复杂性，需要你配合完成加载过程。比如你是否用到自定义扩展结构，Mesh等数据保存在哪里，怎样使用户自己创建容器，自己决定卸载等等。 <br>DX提供了ID3DXALLOCATEHIERARCHY接口，提供了这个自定义的机会，你重载这个接口的虚函数，在加载过程中，它就像回调函数那样运作。</p>
<p>你需要像下面这样建立一个自定义数据容器类：<br>class CAllocateHierarchy: public ID3DXAllocateHierarchy<br>{<br>public:<br>&nbsp;&nbsp;&nbsp;&nbsp;STDMETHOD(CreateFrame)(THIS_ LPCTSTR Name, LPD3DXFRAME *ppNewFrame);<br>&nbsp;&nbsp;&nbsp;&nbsp;STDMETHOD(CreateMeshContainer)(THIS_ LPCTSTR Name, LPD3DXMESHDATA pMeshData,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LPD3DXMATERIAL pMaterials, LPD3DXEFFECTINSTANCE pEffectInstances, DWORD NumMaterials, <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DWORD *pAdjacency, LPD3DXSKININFO pSkinInfo, <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LPD3DXMESHCONTAINER *ppNewMeshContainer);<br>&nbsp;&nbsp;&nbsp;&nbsp;STDMETHOD(DestroyFrame)(THIS_ LPD3DXFRAME pFrameToFree);<br>&nbsp;&nbsp;&nbsp;&nbsp;STDMETHOD(DestroyMeshContainer)(THIS_ LPD3DXMESHCONTAINER pMeshContainerBase);<br>&nbsp;&nbsp;&nbsp;&nbsp;CAllocateHierarchy(CMyD3DApplication *pApp) :m_pApp(pApp) {}<br>public:<br>&nbsp;&nbsp;&nbsp;&nbsp;CMyD3DApplication* m_pApp;<br>};</p>
<p><font color=#ff0000>[问题]</font>上面的STDMETHOD是什么意思？<br>答：相当于virtual&nbsp;&nbsp; HRESULT&nbsp;&nbsp; __stdcall 的宏。&lt;评论&gt; 因为这种类要与D3D的COM接口打交道，不仅仅在C++内部使用，所以，所有类方法必须做成stdcall的，可对外开放的。<br>#define&nbsp;&nbsp; STDMETHOD(method)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; virtual&nbsp;&nbsp; HRESULT&nbsp;&nbsp; STDMETHODCALLTYPE&nbsp;&nbsp; method&nbsp;&nbsp; <br>#define&nbsp;&nbsp; STDMETHODCALLTYPE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; __stdcall&nbsp;&nbsp; <br>这样当写一个函数STDMETHOD(op1(int&nbsp;&nbsp; i))&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>展开后成为：&nbsp;&nbsp;&nbsp;&nbsp; virtual&nbsp;&nbsp; HRESULT&nbsp;&nbsp; __stdcall&nbsp;&nbsp; op1(int&nbsp;&nbsp; i);&nbsp;&nbsp; </p>
<p><font color=#0000ff>3.2 自定义数据容器以及具体的读取过程:</font><br>根据.X文件，在加载过程中，主要有两方面数据需要保存，一个是骨架Frame信息，一个是网格蒙皮Mesh信息。这两个信息保存在如下结构中。</p>
<p>框架信息(对应于骨骼)<br>typedef struct _D3DXFRAME<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;LPSTR&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Name;<br>&nbsp;&nbsp;&nbsp;&nbsp;D3DXMATRIX&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TransformationMatrix; //本骨骼的转换矩阵</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;LPD3DXMESHCONTAINER&nbsp;&nbsp;&nbsp;&nbsp; pMeshContainer;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //本骨骼所对应Mesh数据</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;struct _D3DXFRAME&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *pFrameSibling;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //兄弟骨骼<br>&nbsp;&nbsp;&nbsp;&nbsp;struct _D3DXFRAME&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *pFrameFirstChild;&nbsp;&nbsp;&nbsp;&nbsp;//子骨骼<br>} D3DXFRAME, *LPD3DXFRAME;</p>
<p>自定义数据容器,其数据来源由上面接口的CreateMeshContainer()函数提供<br>typedef struct _D3DXMESHCONTAINER<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;LPSTR&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Name;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //容器名<br>&nbsp;&nbsp;&nbsp;&nbsp;D3DXMESHDATA&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MeshData;&nbsp;&nbsp; //Mesh数据,可创建SkinMesh取代这个Mesh<br>&nbsp;&nbsp;&nbsp;&nbsp;LPD3DXMATERIAL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pMaterials; //材质数组<br>&nbsp;&nbsp;&nbsp;&nbsp;LPD3DXEFFECTINSTANCE&nbsp;&nbsp;&nbsp;&nbsp;pEffects;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;DWORD&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NumMaterials;//材质数<br>&nbsp;&nbsp;&nbsp;&nbsp;DWORD*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pAdjacency;&nbsp;&nbsp;//邻接三角形数组<br>&nbsp;&nbsp;&nbsp;&nbsp;LPD3DXSKININFO&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pSkinInfo;&nbsp;&nbsp; //蒙皮信息,其中含.x中的各个skinweight蒙皮顶点索引及各骨骼偏移矩阵等。<br>&nbsp;&nbsp;&nbsp;&nbsp;struct _D3DXMESHCONTAINER *pNextMeshContainer;<br>} D3DXMESHCONTAINER, *LPD3DXMESHCONTAINER;</p>
<p><br><font color=#ff0000>[评论]</font><br>.在动画文件中，框架通常用来描述骨骼。可以把Frame视做骨骼，所以不细加区分。<br>.在上面D3DXFRAME结构中,pFrameSibling, pFrameFirstChild两个指针，常用于递归函数中，遍历整个骨架。<br>.在D3DXFRAME结构中有一个pMeshContainer指针，难道框架与Mesh是一一对应的吗？<br>有一个框架(骨骼)就有一个Mesh吗？怎么.X文件中只有一个Mesh?难道加载时拆开存放?<br>答:从D3DXFrame结构上看，每个Frame都有一个pMeshContainer指针。这就有三种解释：<br>&nbsp;&nbsp; 第一种，加载到内存后所有的pMeshContainer都指向同一个全局Mesh<br>&nbsp;&nbsp; 第二种，加载到内存后，只有一个主框架的pMeshContainer不为空，其它Frame的pMeshContainer均为NULL，因为在.X中，它们没有定义自己的Mesh<br>&nbsp;&nbsp; 第三种，加载到内存后，D3D将Mesh拆分，分开到各骨骼所对应的Frame，每个Frame都有自己的Mesh。<br>&nbsp;&nbsp; 这个问题我以前也不是很清楚，通过查看示例源码及跟踪发现，正确解释应该是第2种。唯一的一个全局Mesh存放在Frame "body"下的无名Frame中。而其它Frame由于没有自己专门的Mesh而指向NULL. 应该大致如此。这个问题之所以让人困绕，是因为从后续代码上看，在渲染DrawFrame时，是遍历每一个frame分别绘制它们对应的Mesh. 如果对应于同一个mesh，就绘制多遍。如果对应各自mesh，那么变换矩阵怎么组织运算等等。所以，根据第二种解释，由于只有一个pMeshContainer不为NULL，所以参与绘制及蒙皮的只有这一个MeshContainer，人体所有顶点数据及蒙皮信息都在这个mesh中。<br>所以，读取tiny.x文件后，会产生多个D3DXFRAME对象，但只有一个D3DXMESHCONTAINER对象。</p>
<p>在示例代码的CMyD3DApplication::InitDeviceObjects()中，有:<br>&nbsp;&nbsp;&nbsp;&nbsp;hr = D3DXLoadMeshHierarchyFromX(strMeshPath, D3DXMESH_MANAGED, m_pd3dDevice, &amp;Alloc, NULL, &amp;m_pFrameRoot, &amp;m_pAnimController);<br>&nbsp;&nbsp;&nbsp;&nbsp;if (FAILED(hr))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return hr;<br>其中的Alloc是就自定义的数据容器对象。m_pFrameRoot是根骨骼，对遍历很重要。m_pAnimController是动画控制器，对刷新矩阵很重要。</p>
<p>你在运行完这句话后，下一个断点，观察m_pFrameRoot，会发现如下内容：</p>
<p>m_pFrameRoot 0x00c59380 {Name=0x00c53630 "Scene_Root" .....} //根框架<br>pMeshContainer 0x00000000 <br>pFrameSibling 0x00000000 <br>pFrameFirstChild 0x00c59428 {Name=0x00c53ca8 "body" pMeshContainer=0x00000000...}//子框架 骨骼body<br>&nbsp;&nbsp; +---&nbsp;&nbsp;pMeshContainer 0x00000000 <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; +---&nbsp;&nbsp;pFrameSibling 0x01419f00 {Name=0x00c5ffd8 "Box01" pMeshContainer=0x00000000 ...}//兄弟框架<br>&nbsp;&nbsp; &nbsp;&nbsp; +---&nbsp;&nbsp;pFrameFirstChild 0x00c594d0 {Name=0x00000000 <font color=#804000>pMeshContainer=0x00c59828</font> //子框架---该框架就是.x中含有唯一全局Mesh的无名框架</p>
<p><br>可见,在内存中的Frame布局是与.x中一一对应的。除了pFrameFirstChild 0x00c594d0这个地方的Frame中的pMeshContainer不为空，其它框架的这个mesh指针都是空值。<br>另外一点可以看出，并不是每个Frame都对就一块骨骼，有的是别的用途。也就是说Frame对象的个数可能多于骨骼数。</p>
<p><font color=#0000ff>3.3 分析CAllocateHierarchy类</font><br>下面继续研究自定义数据容器CAllocateHierarchy，顾名思义，该类是在加载过程中自行分配层次数据空间。它有4个成员，都是重载D3D的接口虚函数。<br>它的成员CreateFrame()是用来创建D3DXFrame对象的，而CreateMeshContainer()是用来创建Mesh数据对象的。你可以在这两个函数中下断点，发现CreateFrame会运行多次，而CreateMeshContainer只运行一次，再次验证了上面的说法。</p>
<p>值得注意的是，示例对上面的D3DXFRAME，D3DXMESHCONTAINER两个结构做了扩展，分别代之以D3DXFRAME_DERIVED结构和D3DXMESHCONTAINER_DERIVED结构，以集中存储数据方便程序处理。</p>
<p>CreateFrame()处理比较简单，你只是new一个Frame对象空间，填入传进来的Name，其它内容由DX负责维护填充。</p>
<p>CreateMeshContainer()较为复杂。它的任务一是保存传入的网格数据数据，二是根据这些数据及蒙皮信息调用GenerateSkinnedMesh()函数生成蒙皮网格。只有这个新的BlendMesh才能在Render()时支持顶点混合，完成蒙皮的显示。在D3DXMESHCONTAINER_DERIVED结构中，用pOrigMesh保存旧的Mesh普通网格信息。而Meshdata.Mesh则指向新产生的BlendMesh</p>
<p>在这个函数中，多次用到了<font color=#008000>AddRef()</font>，对COM不熟悉的新手容易困惑。D3D是COM组件，它在服务进程中运行，而不在当前的客户进程中。在DX组件运行过程中，要创建一系列接口对象，如CreateDevice()返回接口指针，这些接口及其占用内存什么时候释放，要通过&#8220;引用计数&#8221;的技术来解决。AddRef()给这个接口指针的计数加1，而Release()会将之减1。一旦减到0，表示没有客户使用了，相关的接口就释放了。 由此可知，每次调用Rlease()后，并不一定会释放内存，而是当引用计数归0时释放内存。<br>这样，对接口指针的使用，就像维护堆栈的平衡一样，要仔细，而且按照某种约定规则使用。</p>
<p>但平时D3D编程中，怎么不用AddRef()呢？这是由于一个接口指针，如ID3DDevice，或VertexBuf指针，都是D3DXCreate出来的,在Create时候，在内部已经事先AddRef()了，你就不需要再做这工作了。只要你在不用时，调用 p指针-&gt;Relase()就释放了。一般编程，特别是小型示例程序，都是初始化时建立一次，关闭时释放，都遵守了这种约定，所以不存在这种问题。</p>
<p>但在CreateMeshContainer()函数中，以多种方式使用了指针，在局部指针变量中来回传递，所以问题复杂化了。在COM编程中约定，任何时候地接口指针赋值(复制)，都要AddRef()，在指针变量结束生命期前，再Release(). 但许多程序员都不是严格这么做。因为在局部变量用完就废了，先AddRef()增加计数再Release()减少，和直接使用最后是等效的。几乎是多此一举。这与编程习惯有关系。一旦引用计数不对，如果没有统一的习惯，不好排查。在CreateMeshContainer()中，对接口指针的使用有三种方式，例举如下：</p>
<p>方式一：不使用AddRef()。和普通指针一样，临时变量是左值，接口指针是右值，直接赋值使用。如:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pMesh = pMeshData-&gt;pMesh; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;这是由于pMesh是局部变量，它只是临时引用一下，没必要为它先AddRef()，后Release()。</p>
<p>方式二：隐式的使用AddRef()。 由于用到了一些内部有AddRef()动作的函数，就要按照COM约定，在子程序结束前Release()<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pMesh-&gt;GetDevice(&amp;pd3dDevice);//此处d3d设备引用计数已经加1<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;....<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SAFE_RELEASE(pd3dDevice);//--此处将引用计数减1，并不是真的释放d3d设备<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在本例中，pd3dDevice在GetDevice()中已经Addref()过了，所以，在退出CreateMeshContainer()前，必须pd3dDevice-&gt;Release()</p>
<p>方式三：显式的使用AddRef()。 如果一个指针值，不是由D3DXCreate出来的，而是通过赋值方式复制给一个全局变量或长期变量的。 所以，<strong>可以通过AddRef()的方式来延迟该对象的释放</strong>。因为，如果不AddRef()，极有可能在函数返回该对象就可能释放了。它就像一个加油站，使得传入对象的寿命延长至自己控制范围内。用了AddRef()，就要在相关的Destroy中添加Release()。</p>
<p>在本函数，有三处这样的语句：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pMeshContainer-&gt;MeshData.pMesh = pMesh;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pMeshContainer-&gt;MeshData.Type = D3DXMESHTYPE_MESH;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pMesh-&gt;AddRef();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ....<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pMeshContainer-&gt;pSkinInfo = pSkinInfo;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pSkinInfo-&gt;AddRef();</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pMeshContainer-&gt;pOrigMesh = pMesh;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pMesh-&gt;AddRef();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ....</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;将来在DestroyMeshContainer()中，要释放这些指针：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;....<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SAFE_RELEASE( pMeshContainer-&gt;MeshData.pMesh );<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SAFE_RELEASE( pMeshContainer-&gt;pSkinInfo );<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SAFE_RELEASE( pMeshContainer-&gt;pOrigMesh );</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;由于这些指针值的创建、更改等都是用户自己经营的，所以务必要加前后吻合，在CreateMeshContainer()中AddRef()，在DestroyMeshContainer()中Release().</p>
<p><br>再来看数据的保存部分。<br>在CreateMeshContainer()的传入参数中，有pMeshData,pMaterials,pEffectInstances,NumMaterials,pAdjacency,pSkinInfo<br>你需要把这些数据保存到自己的D3DXMESHCONTAINER对象中。并且其中的所有数组所需的空间都要在全局堆中new出来。所以在该代码中，有如下new:<br>pMeshContainer = new D3DXMESHCONTAINER_DERIVED;//自定义的扩展数据容器对象<br>memset(pMeshContainer, 0, sizeof(D3DXMESHCONTAINER_DERIVED));//初始化pMeshContainer,清0<br>&nbsp;&nbsp;&nbsp;&nbsp;...<br>pMeshContainer-&gt;pMaterials = new D3DXMATERIAL[pMeshContainer-&gt;NumMaterials];//准备保存材质<br>pMeshContainer-&gt;ppTextures = new LPDIRECT3DTEXTURE9[pMeshContainer-&gt;NumMaterials];//准备创建纹理对象。它声明在扩展部分。<br>pMeshContainer-&gt;pAdjacency = new DWORD[NumFaces*3];//准备保存邻接三角形数组,NumFaces = pMesh-&gt;GetNumFaces();</p>
<p>然后，对数据进行memcpy保存。pEffectInstances由于在绘制中不需要，并没进行保存。对于没有贴图的赋以默认材质属性。<br>值得注意的是，所有这些new，必须在DestroyMeshContainer()时进行delete.</p>
<p>接下来的处理中，如果发现Mesh的FVF中没有法向量，要用CloneMeshFVF()重建Mesh，计算顶点平均法向量。以备光照处理。</p>
<p>最后，我们看看蒙皮信息pSkinInfo的处理。这是重头戏。<br>如果发现pSkinInfo!=NULL，就准备着手从各个蒙皮骨骼信息创建SkinMesh.<br>首先，用扩展容器结构D3DXMESHCONTAINER_DERIVED中的各属性保存原Mesh指针值，pMeshContainer-&gt;pOrigMesh = pMesh, 因为接下来我们要创建SkinMesh替代原Mesh.然后，把SkinInfo中的各骨骼的偏移矩阵保存到pMeshContainer-&gt;pBoneOffsetMatrices中<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cBones = pSkinInfo-&gt;GetNumBones();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pMeshContainer-&gt;pBoneOffsetMatrices = new D3DXMATRIX[cBones];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.....<br>&nbsp;&nbsp;&nbsp;&nbsp; 每个&#8220;骨骼偏移矩阵&#8221;pBoneOffsetMatrices,在将来DrawMeshContainer()中是必须要用的。因为原始Mesh中的顶点数据乘以&#8220;骨骼偏移矩阵&#8221;，再乘以&#8220;变换矩阵&#8221;，才能求得各骨骼顶点在世界坐标系中的坐标。 即： <br>&nbsp;&nbsp;&nbsp;&nbsp;骨骼上各点在世界坐标系中的新坐标=初始网格中的各点坐标*骨骼偏移矩阵*骨骼当前的变换矩阵<br>&nbsp;&nbsp;&nbsp;&nbsp;其中，&#8220;初始网格中的各点坐标*骨骼偏移矩阵&#8221; = 骨骼上各点初始时刻在该骨骼坐标系中的局部坐标</p>
<p>做了以上工作后，调用GenerateSkinnedMesh(pMeshContainer)，创建SkinMesh. 接下来，我们看看GenerateSkinnedMesh()做了哪些工作。</p>
<p><font color=#0000ff>3.4 怎样生成蒙皮网格SkinMesh? GenerateSkinnedMesh()分析</font></p>
<p>由于要重定义pMeshContainer-&gt;MeshData.pMesh，所以先SAFE_RELEASE( pMeshContainer-&gt;MeshData.pMesh ); 释放原pMesh</p>
<p>在这个函数中，是根据当前绘图方式设置进行加载数据的。因为顶点混合，有无索引的顶点混合，有含索引的顶点混合，所使用的函数和对应的SkinMesh数据内容也有所不同。<br>在示例中，自定义了枚举m_SkinningMethod，主要分为D3DNONINDEXED和D3DINDEXED，以有纯软件渲染等。运行示例后，你可以选择菜单中的Options选择不同的渲染方式。</p>
<p>我们着重分析一下带索引的蒙皮网格。在程序中，就是D3DINDEXED相关的部分。<br>if (m_SkinningMethod == D3DINDEXED){ ....}</p>
<p>注意! 示例默认工作在D3DNONINDEXED下,<font color=#a00000>如果要跟踪D3DINDEXED部分的代码,必须选择菜单中的Options选择indexed!</font></p>
<p><br>最主要的，要通过DX的ConvertToIndexedBlendedMesh()函数，生成支持&#8220;索引顶点混合&#8221;的SkinMesh.有关索引顶点混合的技术，你可以在DXSDK帮助文件中搜索&#8220;Indexed Vertex Blending&#8221;主题，对着英文和插图将就看，确有收获。</p>
<p>要想用硬件对顶点进行混合，那么参与混合者不能太多。也就是说同时影响一个顶点的骨骼数不能多。我们假定一个顶点最多同时受4个骨骼的影响（也就是同时最多有4个骨骼矩阵参与加权求和），那么同时影响一个三角形面的骨骼数最多就是3*4=12个。<br>我们用NumMaxFaceInfl表示影响一个三角面的最多骨骼矩阵数，那么，通过调用pSkinInfo-&gt;GetMaxFaceInfluences()获取这个数值，一般也就3-4。如果这个数值太大，我们强制使用NumMaxFaceInfl = min(NumMaxFaceInfl, 12);来最多取值12。</p>
<p>用NumMaxFaceInfl 这个数值干什么呢？ 我们用来它分析当前的显卡倒底行不行。</p>
<p>if (m_d3dCaps.MaxVertexBlendMatrixIndex + 1 &lt; NumMaxFaceInfl)//如果显卡达不到该要求<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//很奇怪。2005年底买的GeForce 6600GT显卡，竟然m_d3dCaps.MaxVertexBlendMatrixIndex=0, 不支持索引顶点混合！是驱动问题还是怎么了？<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//但它支持非索引混合。或者，也许要用HLSL支持混合。看起来，3D编程要多考虑。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ..<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pMeshContainer-&gt;UseSoftwareVP = true;//用软件渲染顶点。显然不实用。<br>}<br>else<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pMeshContainer-&gt;NumPaletteEntries = min( ( m_d3dCaps.MaxVertexBlendMatrixIndex + 1 ) / 2, <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pMeshContainer-&gt;pSkinInfo-&gt;GetNumBones() );//--什么意思？<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pMeshContainer-&gt;UseSoftwareVP = false;//采用硬件顶点混合。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Flags |= D3DXMESH_MANAGED;<br>}</p>
<p><font color=#0000ff>[评论]</font>在上面有一行代码:<br>&nbsp;&nbsp;&nbsp;&nbsp; pMeshContainer-&gt;NumPaletteEntries = min( ( m_d3dCaps.MaxVertexBlendMatrixIndex + 1 ) / 2,pMeshContainer-&gt;pSkinInfo-&gt;GetNumBones() );<br>尽管作者加了大段注释，还是让人一头雾水。其实，我们做一个实验，反尔更能理解它的用途。<br>第一步，你在这句话后面下一个断点，看一下在你机器上这个数值。我的ATI 9550显卡机器上是19。比tiny.x中的骨骼数35少很多。<br>第二步，你将上面=右边瞎填一个大于4的数字，比如6。编译后照样运行。而且效果上几乎看不出任何差别。<br>为什么会这样呢？ 我们在绘制代码部分，看看这个数值起什么作用。<br>在DrawMeshContainer()代码中，我们查找D3DINDEXED相关的部分。在mesh各子集的DrawSubset()之前，有如下代码：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for (iAttrib = 0; iAttrib &lt; pMeshContainer-&gt;NumAttributeGroups; iAttrib++)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// first calculate all the world matrices<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for (iPaletteEntry = 0; iPaletteEntry &lt; pMeshContainer-&gt;NumPaletteEntries; ++iPaletteEntry)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;iMatrixIndex = pBoneComb[iAttrib].BoneId[iPaletteEntry];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (iMatrixIndex != UINT_MAX)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;D3DXMatrixMultiply( &amp;matTemp, &amp;pMeshContainer-&gt;pBoneOffsetMatrices[iMatrixIndex], pMeshContainer-&gt;ppBoneMatrixPtrs[iMatrixIndex] );<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_pd3dDevice-&gt;SetTransform( D3DTS_WORLDMATRIX( iPaletteEntry ), &amp;matTemp );<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>下面仔细评估一下这些代码.<br>先注意看其中奇怪的D3DTS_WORLDMATRIX()宏，我们以前还没这样用过。它是做什么用的呢？通过查DXSDK帮助，我们在Geometry Blending主题中找到相关说明，并在"Indexed Vertex Blending"主题中给出了内部实现原理。原来，当你用m_pd3dDevice-&gt;SetRenderState(D3DRS_INDEXEDVERTEXBLENDENABLE, TRUE);开启了索引顶点混合后，在硬件上就启用了&#8220;palette of matrices&#8221;，即矩阵寄存器组，它最多支持同时256个索引。就像过去用256色调色板来表现彩色一样。D3DTS_WORLDMATRIX()宏就是有256-511这256个数表示矩阵索引号。</p>
<p>这些矩阵参与如下计算：</p>
<p>V最终顶点位置=V*M[索引值1]*权重1 + V*M[索引值2]*权重2 + ....+V*M[索引n]*(1-其它权重和)</p>
<p>这个公式的来源，相信大家在众多资料上见过，不赘述。 当然，我们也可以用程序完成这个蒙皮计算过程，但逐个读顶点却很麻烦。现在是由硬件代劳了。我们只设矩阵就行了。<br>我们用m_pd3dDevice-&gt;SetTransform( D3DTS_WORLDMATRIX( iPaletteEntry ), &amp;matTemp );这种方式设定各索引对应的矩阵。</p>
<p>那么权重呢？我们怎么设？原来在上面所说的DX提供的ConvertToIndexedBlendedMesh()函数中，生成SkinMesh时，各网格顶点格式FVF已经有变化了，增加了新格式，D3DFVF_XYZB2，D3DFVF_LASTBETA_UBYTE4，用以记录顶点对应的权重值以及矩阵索引。如下<br>struct VERTEX<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;float x,y,z;<br>&nbsp;&nbsp;&nbsp;&nbsp;float weight;<br>&nbsp;&nbsp;&nbsp;&nbsp;DWORD matrixIndices;<br>&nbsp;&nbsp;&nbsp;&nbsp;float normal[3];<br>};<br>#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZB2 | D3DFVF_LASTBETA_UBYTE4 |D3DFVF_NORMAL);</p>
<p>D3DFVF_LASTBETA_UBYTE4对应于DWORD数值，用于矩阵索引时，每个字节表示一个索引，最多可以允许4个索引，同时有4个矩阵参于该点的混合。如果一次绘制中涉及了9块骨骼矩阵，你可以把这9个矩阵全部用SetTransform设置到矩阵寄存器中，但每个顶点在渲染时，最多使用其中的4个。由此可知，pMeshContainer-&gt;NumPaletteEntries这个数值，确定了一趟DrawSubset绘制所用到的矩阵个数，个数越多，在一趟绘制中就可以纳入的更多顶点。所以，当我们减少pMeshContainer-&gt;NumPaletteEntries这个数值时，pMeshContainer-&gt;NumAttributeGroups数值就会增加。也就是说，一趟绘制中所允许涉及的骨骼数越少，那么子集的数量NumAttributeGroups就会增加，需要多绘几趟。<br>你可以在此下断点观察，当NumPaletteEntries=19时，NumAttributeGroups=3 当NumPaletteEntries=6时，NumAttributeGroups=12 当NumPaletteEntries=4时，NumAttributeGroups=31，几乎和无索引时的分组一样多了。</p>
<p>顶点中的权重weight存放了它当前骨骼的权重。（一个顶点对应的多个骨骼权重怎么存放？是不是在当前子集中有多个同样的顶点，权重不同，对应的矩阵索引不同，然后混合）</p>
<p><br>由上所述，ConvertToIndexedBlendedMesh()是一个很重要函数，由DX自动将Mesh顶点分组成多个子集，以便DrawSubset. 你必须把它的返回参数都记录下来，在绘制时使用。</p>
<p>&nbsp;</p>
<p><strong>四. 怎样绘制显示动画？</strong></p>
<p>DrawFrame()用来绘制整个X框架。它遍历各个框架，找到Mesh不为空的进行绘制。（其实整个.x中通常只有一个不为空，见上文所述）<br>DrawMeshContainer()是绘制函数。</p>
<p><font color=#0000ff>4.1 怎样开启顶点混合？</font><br>注意应用有关的Vertex Blending技术。如在索引方式的绘制中，<br>m_pd3dDevice-&gt;SetRenderState(D3DRS_VERTEXBLEND, pMeshContainer-&gt;NumInfl - 1);<br>其实是设定了D3DVBF_2WEIGHTS或D3DVBF_3WEIGHTS<br>注意要m_pd3dDevice-&gt;SetRenderState(D3DRS_INDEXEDVERTEXBLENDENABLE, TRUE);</p>
<p><font color=#0000ff>4.2 矩阵的刷新：</font><br>首先，在FrameMove()调用m_pAnimController-&gt;SetTime()设置当前时间(或在DX9.0c中用AdvanceTime()设置时间差)，从而刷新各个pFrame-&gt;TransformationMatrix，即骨骼转换矩阵<br>其次，调用UpdateFrameMatrices()做乘法累积，计算出各骨骼坐标系到根世界转换矩阵。<br>最后，在绘制前，将该转换矩阵左乘偏移矩阵，得到最终的转换矩阵。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;D3DXMatrixMultiply( &amp;matTemp, &amp;pMeshContainer-&gt;pBoneOffsetMatrices[iMatrixIndex], pMeshContainer-&gt;ppBoneMatrixPtrs[iMatrixIndex] );</p>
<p>由此可见，你如果注释掉了m_pAnimController-&gt;SetTime，画面肯定停了。</p>
<p><font color=#0000ff>4.3 绘制输出</font> 是在DrawMeshContainer()中，调用SkinMesh的DrawSubset进行绘制。一些细节内容如D3DTS_WORLDMATRIX(),在上面已经有说明，不再罗嗦。</p>
<p>&nbsp;</p>
<p><font color=#0000ff>4.4 关于示例中多种绘制方式分析</font><br>在示例中，用到了多种渲染方式，包括传统的非索引顶点混合，还有新兴的HLSL方式。而且我发现，ATI RADEON 9550 显卡MaxVertexBlendMatrixIndex=37，而价格更高的Gefoce 6600GT MaxVertexBlendMatrixIndex竟然为0，不支持index vertex blending！<br>所以，还是有必要分析一下该示例中各种vertex blending方式的处理，以便掌握多种绘制方式适应不同显卡。<br>经测试，示例中所涉及的多种方式，由慢到快，依次是以下几种：<br>&nbsp;&nbsp;&nbsp;&nbsp;SOFTWARE,<br>&nbsp;&nbsp;&nbsp;&nbsp;D3DNONINDEXED,<br>&nbsp;&nbsp;&nbsp;&nbsp;D3DINDEXED,<br>&nbsp;&nbsp;&nbsp;&nbsp;D3DINDEXEDVS,<br>&nbsp;&nbsp;&nbsp;&nbsp;D3DINDEXEDHLSLVS,</p>
<p>从最慢的SW到最快的HLSL，大约相差20%，有时会大到40%。 差别不是特别悬殊的原因，主要是顶点混合并不是瓶颈。</p>
<p>关于顶点处理方式，是在创建D3D设备时指定的。共有三种方式：<br>&nbsp;&nbsp; D3DCREATE_SOFTWARE_VERTEXPROCESSING 软件顶点运算&nbsp;&nbsp;(简记 sw vp)<br>&nbsp;&nbsp; D3DCREATE_HARDWARE_VERTEXPROCESSING 硬件顶点运算。必须有这项才支持有HAL (简记 hw vp)<br>&nbsp;&nbsp; D3DCREATE_MIXED_VERTEXPROCESSING 混合顶点运算，即硬件+软件 (简记 mixed vp)</p>
<p>一旦用D3DCREATE_HARDWARE_VERTEXPROCESSING方式创建设备，就只能在硬件方式下进行顶点处理。如果调用m_pd3dDevice-&gt;SetSoftwareVertexProcessing(TRUE)来切换到软件顶点处理，HRESULT会返回失败。<br>&nbsp;&nbsp;所以，如果你对客户的显卡没有足够的信息，就用D3DCREATE_MIXED_VERTEXPROCESSING方式创建设备。它默认工作方式是HAL。一旦发现进行某种绘制时硬件能力不够，就可以调用调用m_pd3dDevice-&gt;SetSoftwareVertexProcessing(TRUE)切换到软件模式。在示例中就是这么做的，启动示例后，运行在mixed模式下。</p>
<p>在Gefoce6600GT显卡中，由于D3DINDEXED方式不支持，采用了软件混合方式，在这种方式下速度甚至比SOFTWARE慢。HLSL还好，还是最快。</p>
<p>要确定设备的硬件顶点处理能力，可以参考D3DCAPS9结构的VertexProcessingCaps成员。可以获取下列属性<br>MaxActiveLights，MaxUserClipPlanes，MaxVertexBlendMatrices，MaxStreams，MaxVertexIndex<br></p>
<p>(1)D3DNONINDEXED方式：</p>
<p>首先看GenerateSkinnedMesh()中怎样创建蒙皮网格的。<br>这种方式下，用ConvertToBlendedMesh()建立蒙皮网格，而不是ConvertToIndexBlendedMesh()</p>
<p>为了绘制蒙皮，在这个函数中对Mesh各子集的顶点再次进行的分组。分组的标准是各顶点（或三角面）所涉及的骨骼矩阵个数不超过pMeshContainer-&gt;NumInfl个。(这个数字是由在ConvertToBlendedMesh()时，由参数pMaxFaceInfl返回的)。一个Mesh子集可能被拆开成多个分组。 最后，分组的属性保存在pBoneCombinationBuf中，如子集ID,该子集的各骨骼ID，起始三角面，三角面个数等供绘制时使用，分组的个数保存在pMeshContainer-&gt;NumAttributeGroups中。</p>
<p>接下来检查每个分组所涉及的骨骼数，是不是超过硬件允许的最大混合矩阵数---MaxVertexBlendMatrices。如果超过了就把所有分组截为两大部分，前一部分用硬件混合，后一部分采用软件混合。而且，一旦发现有需要软件混合，要采用CloneMeshFVF(D3DXMESH_SOFTWAREPROCESSING|...)的方式重新生成网格。</p>
<p>再来看绘制部分DrawMeshContainer()</p>
<p>用pBoneComb指向骨骼分组属性，扫描各分组。找出其中骨骼数满足硬件性能的用进行绘制。<br>然后开启软件顶点渲染m_pd3dDevice-&gt;SetSoftwareVertexProcessing(TRUE)，对那些骨骼数超出硬件性能的进行绘制。<br>SetSoftwareVertexProcessing()需要当前d3d设备以D3DCREATE_MIXED_VERTEXPROCESSING方式创建。</p>
<p>(2)D3DINDEXED，这种方式上面分析过了，从略。用pMeshContainer-&gt;UseSoftwareVP表示是否采用软件绘制。<br>值得注意的是在这种方式下，一旦硬件性能不足，会彻底使用软件顶点渲染，而不是像上面一样拆为两部分。</p>
<p>(3)D3DINDEXEDVS，D3DINDEXEDHLSLVS<br>这种情况下使用了着色器和高级着色语言。超出本文主旨，讨论从略。</p>
<p>(4)SOFTWARE--软件方式？ 让人有些迷惑，与上面的m_pd3dDevice-&gt;SetSoftwareVertexProcessing(TRUE)有何区别？</p>
<p>从代码看，这种方式下反而比较简单。GenerateSkinnedMesh()中，<br>先直接从原始Mesh克隆一个Mesh,然后读取它的材质属性数组。开辟一个空间m_pBoneMatrices，用以存放各块骨骼的转换矩阵。</p>
<p>在绘制时，从pMeshContainer中的变换矩阵乘以偏移矩阵，放在pBoneMatrices中。把这个矩阵数组，以原Mesh的顶点作为源顶点，以新克隆的MeshData.pMesh做为目标顶点，调用pSkinInfo-&gt;UpdateSkinnedMesh()，用软件方式计算各骨骼顶点的新位置(相当于软件计算方式蒙皮)。</p>
<p>然后调用MeshData.pMesh-&gt;DrawSubset()绘制。</p>
<p>可见，在SOFTWARE方式下，最终顶点的渲染还是HAL方式的，只不过蒙皮计算是由软件完成的。它和上面的m_pd3dDevice-&gt;SetSoftwareVertexProcessing(TRUE)直接设置软件顶点渲染还是有区别的。</p>
<img src ="http://www.cppblog.com/Leaf/aggbug/101033.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Leaf/" target="_blank">大渊献</a> 2009-11-16 00:47 <a href="http://www.cppblog.com/Leaf/archive/2009/11/16/101033.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>IDirect3DDevice9::SetClipPlane</title><link>http://www.cppblog.com/Leaf/archive/2009/10/11/98317.html</link><dc:creator>大渊献</dc:creator><author>大渊献</author><pubDate>Sun, 11 Oct 2009 08:34:00 GMT</pubDate><guid>http://www.cppblog.com/Leaf/archive/2009/10/11/98317.html</guid><wfw:comment>http://www.cppblog.com/Leaf/comments/98317.html</wfw:comment><comments>http://www.cppblog.com/Leaf/archive/2009/10/11/98317.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Leaf/comments/commentRss/98317.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Leaf/services/trackbacks/98317.html</trackback:ping><description><![CDATA[突然看到这个函数。<br><strong>HRESULT SetClipPlane(</strong><br>&nbsp; <strong>DWORD</strong> <em>Index</em><strong>,</strong><br>&nbsp; <strong>CONST float *</strong> <em>pPlane</em><br><strong>);<br><br>虽然DX SDK上面有，但还是有很多朋友不喜欢看那些拉丁字母，我也顺便就记录一下吧。<br><br>参数：<br>第一个是索引，不用说了。<br><br>第二个是存着 A B C D的数组。<br>这个数组最后会用来构建 Ax+By+Cz+Dw = 0;平面。<br><br>然后顶点会根据自已的位置(x,y,z,w)来进行判断。如果Ax+By+Cz+Dw &gt;= 0。则表示在平面前方，保留。反之则在后方，被裁剪掉。<br><br>值得注意的时，在固定管线使用平面裁剪的时候，是在世界坐标系中处理的。<br><br><br>而用SHADER的时候，是在裁剪空间中处理的。（即顶点输出的时候的坐标系）<br>貌似还是太抽象。比如顶点输入坐标是pos&nbsp;&nbsp; 此时的坐标变换阵是WVP,则 Output.pos = mul(pos,WVP);&nbsp; 那么，此时的裁剪空间就是Output.pos对应的坐标系空间。</strong> <br><br>另外，默认情况下D3DRS_CLIPPLANEENABLE 是没有打开的，应该在SetRenderState中手工打开。 <br><br>值得注意的是：D3DXPLANE进行矩阵变换的时候，要将需要乘的那个矩阵进行求逆和转置，再相乘。SDK中代码如下<br>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><span style="COLOR: #000000">D3DXPLANE&nbsp;&nbsp;&nbsp;planeNew;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">D3DXPLANE&nbsp;&nbsp;&nbsp;plane(</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">,</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">,</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">,</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">);<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">D3DXPlaneNormalize(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">plane,&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">plane);<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">D3DXMATRIX&nbsp;&nbsp;matrix;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">D3DXMatrixScaling(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">matrix,&nbsp;</span><span style="COLOR: #000000">1.0f</span><span style="COLOR: #000000">,</span><span style="COLOR: #000000">2.0f</span><span style="COLOR: #000000">,</span><span style="COLOR: #000000">3.0f</span><span style="COLOR: #000000">);&nbsp;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">D3DXMatrixInverse(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">matrix,&nbsp;NULL,&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">matrix);<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">D3DXMatrixTranspose(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">matrix,&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">matrix);<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">D3DXPlaneTransform(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">planeNew,&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">plane,&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">matrix);<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span></div>
<br><br>上面的D3DXPLANE plane(0,1,1,0)如果你觉得不直观的话，DX提供了以下一些生成PLANE的函数<br>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><span style="COLOR: #000000">D3DXPLANE&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;D3DXPlaneFromPoints(<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">&nbsp;&nbsp;D3DXPLANE&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;pOut,<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">&nbsp;&nbsp;CONST&nbsp;D3DXVECTOR3&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;pV1,<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">&nbsp;&nbsp;CONST&nbsp;D3DXVECTOR3&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;pV2,<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">&nbsp;&nbsp;CONST&nbsp;D3DXVECTOR3&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;pV3<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">);<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span></div>
<p>上面的PV1 PV2 PV3则是平面上的三个点。这个函数可以很容易地求得一个三角形所在的平面。<br></p>
<p>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><span style="COLOR: #000000">D3DXPLANE&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;D3DXPlaneFromPointNormal(<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">&nbsp;&nbsp;D3DXPLANE&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;pOut,<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">&nbsp;&nbsp;CONST&nbsp;D3DXVECTOR3&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;pPoint,<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">&nbsp;&nbsp;CONST&nbsp;D3DXVECTOR3&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;pNormal<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">);<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span></div>
<p>&#160;</p>
<p>pPoint为平面上的一个点。 pNormal是平面的法线方向。<br>比如，你想创建一个水平平面，并且朝上。 则可以将pPoint传入0，0，0&nbsp; 而pNormal传入0,1,0即可。<br><br></p>
<img src ="http://www.cppblog.com/Leaf/aggbug/98317.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Leaf/" target="_blank">大渊献</a> 2009-10-11 16:34 <a href="http://www.cppblog.com/Leaf/archive/2009/10/11/98317.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于图象的拉伸问题</title><link>http://www.cppblog.com/Leaf/archive/2009/10/10/98276.html</link><dc:creator>大渊献</dc:creator><author>大渊献</author><pubDate>Sat, 10 Oct 2009 13:25:00 GMT</pubDate><guid>http://www.cppblog.com/Leaf/archive/2009/10/10/98276.html</guid><wfw:comment>http://www.cppblog.com/Leaf/comments/98276.html</wfw:comment><comments>http://www.cppblog.com/Leaf/archive/2009/10/10/98276.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Leaf/comments/commentRss/98276.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Leaf/services/trackbacks/98276.html</trackback:ping><description><![CDATA[<br>&lt;如果窗口的视图区大小和SwapChain的大小不一，那么DirectX将通过Stretch Blit来自动处理图像的伸缩变化。尽管这可能并不令人期待，因为这在视图区变大的时候将导致图像的模糊。&gt; 你说的这个问题 要怎么才能解决？ <br><br><br>有位博友这样问过，因为最近少有上博客，于是没有回答及时，请见谅，我这里只能说说我们现在的解决办法。<br><br>当后台缓冲区的分辨率和视图区不统一的时候，会导致拉伸现象，使画面变得模糊。<br><br>首先说个题外话：<br>正因为变得模糊，因此有人故意将后台缓冲区做得比视区稍大一点，这样来抗锯齿，至于效果如何，没有真正见过，有兴趣的可以试试。<br><br>下面说说解决办法。<br>我们的解决办法也很简单，就分三步<br>1，窗口改变的时候，告诉设备窗口大小改变。<br>2，按改变后的窗口重建缓冲区。<br>3。强制设备丢失，并重新加载需要的资源。<br><br>由于在做这个之前，设备丢失已经做好了，于是就偷了个懒，窗口改变的时候就传入窗口大小，并reset<br>这样设备就强制处于丢失状态。<br><br><br>不知道有没有说清楚，反正主要的就是要重建缓冲区，并处理设备丢失问题。。。 
<img src ="http://www.cppblog.com/Leaf/aggbug/98276.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Leaf/" target="_blank">大渊献</a> 2009-10-10 21:25 <a href="http://www.cppblog.com/Leaf/archive/2009/10/10/98276.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>如何避免VS2005下的d3d9types.h(1385) warning C4819 </title><link>http://www.cppblog.com/Leaf/archive/2009/08/24/94310.html</link><dc:creator>大渊献</dc:creator><author>大渊献</author><pubDate>Mon, 24 Aug 2009 12:51:00 GMT</pubDate><guid>http://www.cppblog.com/Leaf/archive/2009/08/24/94310.html</guid><wfw:comment>http://www.cppblog.com/Leaf/comments/94310.html</wfw:comment><comments>http://www.cppblog.com/Leaf/archive/2009/08/24/94310.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/Leaf/comments/commentRss/94310.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Leaf/services/trackbacks/94310.html</trackback:ping><description><![CDATA[<p>用VS2005+DirectX9 SDK（手头测试过的是2004年10月的DirectX SDK和2006年4月的DirectX SDK）编译游戏会出现以下warning：</p>
<p>&nbsp;</p>
<p><br>--------------------------------------------------------------------------------</p>
<p><br>d:\microsoft directx 9.0 sdk (october 2004)\include\d3d9types.h(1385) : warning C4819: The file contains a character that cannot be represented in the current code page (936). Save the file in Unicode format to prevent data loss</p>
<p>&nbsp;</p>
<p>--------------------------------------------------------------------------------<br>要修正这个问题，不必要存为UTF8的文件，而是搜索_D3DDEVINFO_VCACHE，然后会看到：</p>
<p>typedef struct _D3DDEVINFO_VCACHE ...{<br>&nbsp;&nbsp;&nbsp; DWORD&nbsp;&nbsp; Pattern;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /**//* bit pattern, return value must be FOUR_CC(慍? 慉? 慍? 慔? */<br>&nbsp;&nbsp;&nbsp; DWORD&nbsp;&nbsp; OptMethod;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /**//* optimization method 0 means longest strips, 1 means vertex cache based */<br>&nbsp;&nbsp;&nbsp; DWORD&nbsp;&nbsp; CacheSize;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /**//* cache size to optimize for&nbsp; (only required if type is 1) */<br>&nbsp;&nbsp;&nbsp; DWORD&nbsp;&nbsp; MagicNumber;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /**//* used to determine when to restart strips (only required if type is 1)*/<br>} D3DDEVINFO_VCACHE, *LPD3DDEVINFO_VCACHE;<br>那四个乱码的去掉就可以了</p>
<p>参考了<a href="http://gamep.mmoh.jp/e39255.html">http://gamep.mmoh.jp/e39255.html</a></p>
<p><br>本文来自CSDN博客，转载请标明出处：<a href="http://blog.csdn.net/SONIC3D/archive/2007/11/01/1861794.aspx">http://blog.csdn.net/SONIC3D/archive/2007/11/01/1861794.aspx</a></p>
<img src ="http://www.cppblog.com/Leaf/aggbug/94310.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Leaf/" target="_blank">大渊献</a> 2009-08-24 20:51 <a href="http://www.cppblog.com/Leaf/archive/2009/08/24/94310.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转 深入理解D3D9</title><link>http://www.cppblog.com/Leaf/archive/2009/08/13/93109.html</link><dc:creator>大渊献</dc:creator><author>大渊献</author><pubDate>Wed, 12 Aug 2009 16:29:00 GMT</pubDate><guid>http://www.cppblog.com/Leaf/archive/2009/08/13/93109.html</guid><wfw:comment>http://www.cppblog.com/Leaf/comments/93109.html</wfw:comment><comments>http://www.cppblog.com/Leaf/archive/2009/08/13/93109.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Leaf/comments/commentRss/93109.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Leaf/services/trackbacks/93109.html</trackback:ping><description><![CDATA[<p>文章来源：<a href="http://www.cnblogs.com/effulgent/archive/2009/02/10/1387438.html">http://www.cnblogs.com/effulgent/archive/2009/02/10/1387438.html</a><br>深入理解D3D9对图形程序员来说意义重大，我把以前的一些学习笔记都汇总起来，希望对朋友们有些所帮助，因为是零散笔记，思路很杂，还请包涵。</p>
<p>其实只要你能完美理解D3DLOCK、D3DUSAGE、D3DPOOL、LOST DEVICE、QUERY、Present（）、BeginScene（）、EndScene（）等概念，就算是理解D3D9了， 不知道大家有没有同感。有如下几个问题，如果你能圆满回答就算过关：）。<br><span lang=EN-US>1、&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span lang=EN-US>D3DPOOL_DEFAULT</span>、<span lang=EN-US>D3DPOOL_MANAGED</span>、<span lang=EN-US>D3DPOOL_SYSTEMMEM</span>和<span lang=EN-US>D3DPOOL_SCRATCH</span><span>到底有何本质区别？<br><span lang=EN-US>2、&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span lang=EN-US>D3DUSAGE<span>的具体怎么使用？<br><span lang=EN-US>3、&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 什么是<span lang=EN-US>Adapter？什么是<span lang=EN-US>D3D Device</span>？<span lang=EN-US>HAL Device</span>和<span lang=EN-US>Ref Device</span>有何区别？<span lang=EN-US>Device</span>的类型又和<span lang=EN-US>Vertex Processing</span><span>类型有什么关系？<br><span lang=EN-US>4、&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span lang=EN-US>APP（<span lang=EN-US>CPU</span>）、<span lang=EN-US>RUNTIME</span>、<span lang=EN-US>DRIVER</span>、<span lang=EN-US>GPU</span>是如何协同工作的？<span lang=EN-US>D3D API</span><span>是同步函数还是异步函数？<br><span lang=EN-US>5、&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Lost Device到底发生了什么？为什么在设备丢失后<span lang=EN-US>D3DPOOL_DEFAULT类型资源需要重新创建？</span></span></span></span></span></span></span></span></span></span></span></span></span></p>
<p>在<span lang=EN-US>D3D</span>中有三大对象，他们是<span lang=EN-US>D3D OBJECT</span>、<span lang=EN-US>D3D ADAPTER</span>和<span lang=EN-US>D3D DEVICE</span>。<span lang=EN-US>D3D OBJECT</span>很简单，就是一个使用<span lang=EN-US>D3D</span>功能的<span lang=EN-US>COM</span>对象，其提供了创建<span lang=EN-US>DEVICE</span>和枚举<span lang=EN-US>ADAPTER</span>的功能。<span lang=EN-US>ADAPTER</span>是对计算机图形硬件和软件性能的一个抽象，其包含了<span lang=EN-US>DEVICE</span>。<span lang=EN-US>DEVICE</span>则是<span lang=EN-US>D3D</span>的核心，它包装了整个图形流水管线，包括变换、光照和光栅化（着色），根据<span lang=EN-US>D3D</span>版本不同，流水线也有区别，比如最新的<span lang=EN-US>D3D10</span>就包含了新的<span lang=EN-US>GS</span>几何处理。图形管线的所有功能由<span lang=EN-US>DRIVER</span>提供，而<span lang=EN-US>DIRVER</span>分两类，一种是<span lang=EN-US>GPU</span>硬件<span lang=EN-US>DRIVER</span>，另一种是软件<span lang=EN-US>DRIVER</span>，这就是为什么在<span lang=EN-US>D3D</span>中主要有两类<span lang=EN-US>DEVICE</span>， <span lang=EN-US>REF和<span lang=EN-US>HAL</span>，使用<span lang=EN-US>REF DEVICE</span>时，图形管线的光栅化功能由软件<span lang=EN-US>DRIVER</span>在<span lang=EN-US>CPU</span>上模拟的，<span lang=EN-US>REF DEVICE</span>从名字就可以看出这个给硬件厂商做功能参考用的，所以按常理它应该是全软件实现，具备全部<span lang=EN-US>DX</span>标准功能。而使用<span lang=EN-US>HAL DEVICE</span>时，<span lang=EN-US>RUNTIME</span>则将使用<span lang=EN-US>HAL</span>硬件层控制<span lang=EN-US>GPU</span>来完成变换、光照和光栅化，而且只有<span lang=EN-US>HAL DEVICE</span>中同时实现了硬件顶点处理和软件顶点处理（<span lang=EN-US>REF DEVICE</span>一般不能使用硬件顶点处理，除非自己在驱动上做手脚，比如<span lang=EN-US>PERFHUD</span>）。另外还有个一个不常用的<span lang=EN-US>SOFTWARE DEVICE</span>，用户可以使用<span lang=EN-US>DDI</span>编写自己的软件图形驱动，然后注册进系统，之后便可在程序中使用。</span></p>
<p>检查系统软件硬件性能。<br>在程序的开始我们就要判断目标机的性能，其主要流程是：<br>确定要用的缓冲格式<br>GetAdapterCount()<br>GetAdapterDisplayMode</p>
<p><span><span lang=EN-US>GetAdapterIdentifier //</span><span>得到适配器描述<br><span lang=EN-US>CheckDeviceType //<span>判断指定适配器上的设备是否支持硬件加速<br><span lang=EN-US>GetDeviceCaps //指定设备的性能，主要判断是否支持硬件顶点处理<span lang=EN-US>(T&amp;L)<br><span lang=EN-US>GetAdapterModeCount //</span><span>得到适配器上指定缓冲格式所有可用的显示模式<br><span lang=EN-US>EnumAdapterModes //<span>枚举所有显示模式<br><span>CheckDeviceFormat<br>CheckDeviceMultiSampleType<br>详细使用请参考DX文档。<span lang=EN-US><br></span></span></span></span></span></span></span></span></span></span></span></p>
<p><span lang=EN-US><span lang=EN-US>WINDOWS图形系统的主要分为四层：图形应用程序、<span lang=EN-US>D3D RUNTIME、<span lang=EN-US>SOFTWARE DRIVER和<span lang=EN-US>GPU。此四层是按功能来分的，实际上他们之间界限并不如此明确，比如<span lang=EN-US>RUNTIME中其实也包含有<span lang=EN-US>USER MODE的<span lang=EN-US>SOFTWARE DRIVER，详细结构这里不再多说。而在<span lang=EN-US>RUNTIME里有一个很重要的结构，叫做<span lang=EN-US>command buffer，当应用程序调用一个<span lang=EN-US>D3D API时，<span lang=EN-US>RUNTIME</span>将调用转换成设备无关的命令，然后将命令缓冲到这个<span lang=EN-US>COMMAND BUFFER</span>中，这个<span lang=EN-US>BUFFER</span>的大小是根据任务负载动态改变的，当这个<span lang=EN-US>BUFFER</span>满员之后，<span lang=EN-US>RUNTIME</span>会让所有命令<span lang=EN-US>FLUSH</span>到<span lang=EN-US>KERNEL</span>模式下的驱动中，而驱动中也是有一个<span lang=EN-US>BUFFER</span>的，用来存储已被转换成的硬件相关的命令，<span lang=EN-US>D3D</span>一般只允许其缓冲最多<span lang=EN-US>3</span>个帧的图形指令，而且<span lang=EN-US>RUNTIME</span>和<span lang=EN-US>DRIVER</span>都会被<span lang=EN-US>BUFFER</span>中的命令做适当优化，比如我们在程序中连续设置同一个<span lang=EN-US>RENDER STATE</span>，我们就会在调试信息中看到如下信息&#8220;<span lang=EN-US>Ignoring redundant SetRenderState - X</span>&#8221;，这便是<span lang=EN-US>RUNTIME</span>自动丢弃无用的状态设置命令。在<span lang=EN-US>D3D9</span>中可以使用<span lang=EN-US>QUERY</span>机制来与<span lang=EN-US>GPU</span>进行异步工作，所谓<span lang=EN-US>QUERY</span>就是查询命令，用来查询<span lang=EN-US>RUNTIME</span>、<span lang=EN-US>DRIVER</span>或者<span lang=EN-US>GPU</span>的状态，<span lang=EN-US>D3D9</span>中的<span lang=EN-US>QUERY</span>对象有三种状态，<span lang=EN-US>SIGNALED</span>、<span lang=EN-US>BUILDING</span>和<span lang=EN-US>ISSUED</span>，当他们处于空闲状态后会将查询状态置于<span lang=EN-US>SIGNALED STATE</span>，查询分开始和结束，查询开始表示对象开始记录应用程序所需数据，当应用程序指定查询结束后，如果被查询的对象处于空闲状态，则被查询对象会将查询对象置于<span lang=EN-US>SIGNALED</span>状态。<span lang=EN-US>GetData</span>则是用来取得查询结果，如果返回的是<span lang=EN-US>D3D_OK</span>则结果可用，如果使用<span lang=EN-US>D3DGETDATA_FLUSH</span>标志，表示将<span lang=EN-US>COMMAND BUFFER</span>中的所有命令都发送到<span lang=EN-US>DRIVER</span>。现在我们知道<span lang=EN-US>D3D API绝大部分都是同步函数，应用程序调用后，<span lang=EN-US>RUNTIME只是简单的将其加入到<span lang=EN-US>COMMAND BUFFER</span>，可能有人会疑惑我们如何测定帧率？又如何分析<span lang=EN-US>GPU</span>时间呢？对于第一个问题我们要看当一帧完毕，也就是<span lang=EN-US>PRESENT()</span>函数调用是否被阻塞，答案是可能被阻塞也可能不被阻塞，要看<span lang=EN-US>RUNTIME</span>允许缓冲中存在的指令数量，如果超过额度，则<span lang=EN-US>PRESENT</span>函数会被阻塞下来，如何<span lang=EN-US>PRESENT</span>完全不被阻塞，当<span lang=EN-US>GPU</span>执行繁重的绘制任务时，<span lang=EN-US>CPU</span>工作进度会大大超过<span lang=EN-US>GPU</span>，导致游戏逻辑快于图形显示，这显然是不行的。测定<span lang=EN-US>GPU</span>工作时间是件很麻烦的事，首先我们要解决同步问题，要测量<span lang=EN-US>GPU</span>时间，首先我们必须让<span lang=EN-US>CPU</span>与<span lang=EN-US>GPU</span>异步工作，在<span lang=EN-US>D3D9</span>中可以使用<span lang=EN-US>QUERY</span>机制做到这点，让我们看看<span lang=EN-US>Accurately Profiling Driect3D API Calls</span>中的例子:<br>IDirect3DQuery9* pQueryEvent;</span></span></span></span></span></span></span></span></span></span></span></span></span></p>
<p><span lang=EN-US><span lang=EN-US>//1.</span><span>创建事件类型的查询事件<br><span>m_pD3DDevice-&gt;CreateQuery( D3DQUERYTYPE_EVENT, &amp;pQueryEvent);<br><span lang=EN-US>//2.</span>在<span lang=EN-US>COMMAND BUFFER</span>中加入一个查询结束的标记，此查询默认开始于<span lang=EN-US>CreateDevice<br>pQueryEvent-&gt;Issue(D3DISSUE_END);<br><span lang=EN-US>//3.</span>将<span lang=EN-US>COMMAND BUFFER</span>中的所有命令清空到<span lang=EN-US>DRIVER</span>中去，并循环查询事件对象转换到<span lang=EN-US>SIGNALED</span>状态，当<span lang=EN-US>GPU</span>完成<span lang=EN-US>CB</span><span>中所有命令后会将查询事件状态进行转换。<br><span>while(S_FALSE == pQueryEvent-&gt;GetData( NULL, 0, D3DGETDATA_FLUSH) )<br>&nbsp;&nbsp; &nbsp;&nbsp;;<br>LARGE_INTEGER start, stop;<br>QueryPerformanceCounter(&amp;start);&nbsp;<br>SetTexture();<br>DrawPrimitive();&nbsp;<br>pQueryEvent-&gt;Issue(D3DISSUE_END);<br>while(S_FALSE == pQueryEvent-&gt;GetData( NULL, 0, D3DGETDATA_FLUSH) )<br>&nbsp;&nbsp; &nbsp; &nbsp;&nbsp;;<br>QueryPerformanceCounter(&amp;stop);&nbsp;</span></span></span></span></span></span></p>
<p><span lang=EN-US><span lang=EN-US>1.第一个GetData调用使用了D3DGETDATA_FLUSH标志，表示要将COMMAND BUFFER中的绘制命令都清空到DRIVER中去，当GPU处理完所有命令后会将这个查询对象状态置SIGNALED。<br>2.将设备无关的SETTEXTURE命令加入到RUNTIME的COMMAND BUFFER中。<br>3.将设备无关的DrawPrimitive命令加入到RUNTIME的COMMAND BUFFER中。<br>4.将设备无关的ISSUE命令加入到RUNTIME的COMMAND BUFFER中。<br>5.GetData会将BUFFER中的所有命令清空到DRIVER中去，注意这是GETDATA不会等待GPU完成所有命令的执行才返回。这里会有一个从用户模式到核心模式的切换。<br>6.等待DRIVER将所有命令都转换为硬件相关指令，并填充到DRIVER BUFFER中后，调用从核心模式返回到用户模式。<br>7.GetData循环查询 查询对象 状态。当GPU完成所有DRIVER BUFFER中的指令后会改变查询对象的状态。</span></span></p>
<p>如下情况可能清空RUNTIME COMMAND BUFFER，并引起一个模式切换：<br>1.Lock method（某些条件下和某些LOCK标志）</p>
<p>2.创建设备、顶点缓冲、索引缓冲和纹理<br>3.完全释放设备、顶点缓冲、索引缓冲和纹理资源<br>4.调用ValidateDevice<br>5.调用Present<br>6.COMMAND BUFFER已满<br>7.用D3DGETDATA_FLUSH调用GetData函数</p>
<p><span lang=EN-US><span><span lang=EN-US><span><span>对于<span lang=EN-US>D3DQUERYTYPE_EVENT的解释我不能完全理解（<span><span lang=EN-US>Query for any and all asynchronous events that have been issued from API calls</span>）明白的朋友一定告诉我，只知道当<span lang=EN-US>GPU</span>处理完<span lang=EN-US>D3DQUERYTYPE_EVENT</span>类型查询在<span lang=EN-US>CB</span>中加入的<span lang=EN-US>D3DISSUE_END</span>标记后，会将查询对象状态置<span lang=EN-US>SIGNALED</span>状态，所以<span lang=EN-US>CPU</span>等待查询一定是异步的。为了效率所以尽量少在<span lang=EN-US>PRESENT</span>之前使用<span lang=EN-US>BEGINSCENE ENDSCENE</span>对，为什么会影响效率？原因只能猜测，可能<span lang=EN-US>EndScene</span>会引发<span lang=EN-US>Command buffer flush</span>这样会有一个执行的模式切换，也可能会引发D3D RUNTIME对MANAGED资源的一些操作。而且<span lang=EN-US>ENDSCENE</span>不是一个同步方法，它不会等待<span lang=EN-US>DRIVER</span>把所有命令执行完才返回。&nbsp;</span></span></span></span></span></span></span></p>
<p><span lang=EN-US><span lang=EN-US><span lang=EN-US>D3D RUTIME的内存类型，分为<span lang=EN-US>3种，<span lang=EN-US>VIDEO MEMORY（<span lang=EN-US>VM）、<span lang=EN-US>AGP MEMORY</span>（<span lang=EN-US>AM</span>）和<span lang=EN-US>SYSTEM MEMORY</span>（<span lang=EN-US>SM</span>），所有<span lang=EN-US>D3D</span>资源都创建在这<span lang=EN-US>3</span>种内存之中，在创建资源时，我们可以指定如下存储标志，<span lang=EN-US>D3DPOOL_DEFAULT</span>、<span lang=EN-US>D3DPOOL_MANAGED</span>、<span lang=EN-US>D3DPOOL_SYSTEMMEM</span>和<span lang=EN-US>D3DPOOL_SCRATCH</span>。<span lang=EN-US>VM</span>就是位于显卡上的显存，<span lang=EN-US>CPU</span>只能通过<span lang=EN-US>AGP</span>或<span lang=EN-US>PCI-E</span>总线访问到，读写速度都是非常慢的，<span lang=EN-US>CPU</span>连续写<span lang=EN-US>VM</span>稍微快于读，因为<span lang=EN-US>CPU</span>写<span lang=EN-US>VM</span>时会在<span lang=EN-US>CACHE</span>中分配<span lang=EN-US>32或</span><span lang=EN-US>64</span>个字节（取决于<span lang=EN-US>CACHE LINE</span>长度）的写缓冲，当缓冲满后会一次性写入<span lang=EN-US>VM</span>；<span lang=EN-US>SM</span>就是系统内存，<span lang=EN-US>CPU</span>读写都非常快，因为<span lang=EN-US>SM</span>是被<span lang=EN-US>CACHE</span>到<span lang=EN-US>2</span>级缓冲的，但<span lang=EN-US>GPU</span>却不能直接访问到系统缓冲，所以创建在<span lang=EN-US>SM</span>中的资源，<span lang=EN-US>GPU</span>是不能直接使用的；<span lang=EN-US>AM</span>是最麻烦的一个类型，<span lang=EN-US>AM</span>实际也存在于系统内存中，但这部分<span lang=EN-US>MEM</span>不会被<span lang=EN-US>CPU CACHE</span>，意味着<span lang=EN-US>CPU</span>读写<span lang=EN-US>AM</span>都会写来个<span lang=EN-US>CACHE MISSING</span>然后才通过内存总线访问<span lang=EN-US>AM</span>，所以<span lang=EN-US>CPU</span>读写<span lang=EN-US>AM</span>相比<span lang=EN-US>SM</span>会比较慢，但连续的写会稍微快于读，原因就是<span lang=EN-US>CPU</span>写<span lang=EN-US>AM</span>使用了&#8220;<span lang=EN-US>write combining</span>&#8221;，而且<span lang=EN-US>GPU</span>可以直接通过<span lang=EN-US>AGP</span>或<span lang=EN-US>PCI-E</span>总线访问<span lang=EN-US>AM</span>。&nbsp;</span></span></span></span></span></span></p>
<p><span lang=EN-US><span lang=EN-US><span>如果我们使用<span lang=EN-US>D3DPOOL_DEFAULT来创建资源，则表示让<span lang=EN-US>D3D RUNTIME根据我们指定的资源使用方法来自动使用存储类型，一般是<span lang=EN-US>VM或<span lang=EN-US>AM，系统不会在其他地方进行额外备份，当设备丢失后，这些资源内容也会被丢失掉。<span>但系统并不会在创建的时候使用D3DPOOL_SYSTEMMEM或D3DPOOL_MANAGED来替换它，注意他们是完全不同的POOL类型，创建到D3DPOOL_DEFAULT中的纹理是不能被CPU LOCK的，除非是动态纹理。但创建在D3DPOOL_DEFAULT中的VB IB RENDERTARGET BACK BUFFERS可以被LOCK。当你用D3DPOOL_DEFAULT创建资源时，如果显存已经使用完毕，则托管资源会被换出显存来释放足够的空间。&nbsp;<span lang=EN-US>D3DPOOL_SYSTEMMEM和<span lang=EN-US>D3DPOOL_SCRATCH</span>都是位于<span lang=EN-US>SM</span>中的，其差别是使用<span lang=EN-US>D3DPOOL_SYSTEMMEM</span>时，资源格式受限于<span lang=EN-US>Device</span>性能，因为资源很可能会被更新到<span lang=EN-US>AM</span>或<span lang=EN-US>VM</span>中去供图形系统使用，但<span lang=EN-US>SCRATCH</span>只受<span lang=EN-US>RUNTIME</span><span>限制，所以这种资源无法被图形系统使用。<span>&nbsp;<span>D3DRUNTIME会优化D3DUSAGE_DYNAMIC 资源，一般将其放置于AM中，但不敢完全保证。另外为什么静态纹理不能被LOCK，动态纹理却可以，都关系到D3D RUNTIME的设计，在后面D3DLOCK说明中会叙述。</span></span></span></span></span></span></span></span></span></span></span></span></p>
<p><span lang=EN-US>D3DPOOL_MANAGED</span>表示让<span lang=EN-US>D3D RUNTIME</span>来管理资源，被创建的资源会有<span lang=EN-US>2</span>份拷贝，一份在<span lang=EN-US>SM</span>中，一份在<span lang=EN-US>VM/AM</span>中，创建的时候被放置<span lang=EN-US>L</span>在<span lang=EN-US>SM</span>，在<span lang=EN-US>GPU</span>需要使用资源时<span lang=EN-US>D3D RUNTIME</span>自动将数据拷贝到<span lang=EN-US>VM</span>中去，当资源被<span lang=EN-US>GPU</span>修改后，<span lang=EN-US>RUNTIME</span>在必要时自动将其更新到<span lang=EN-US>SM</span>中来，而在<span lang=EN-US>SM</span>中修改后也会被<span lang=EN-US>UPDATE</span>到<span lang=EN-US>VM</span>去中。所以被<span lang=EN-US>CPU</span>或者<span lang=EN-US>GPU</span>频发修改的数据，一定不要使用托管类型，这样会产生非常昂贵的同步负担。当<span lang=EN-US>LOST DEVICE</span>发生后，<span lang=EN-US>RESET</span>时<span lang=EN-US>RUNTIME</span>会自动利用<span lang=EN-US>SM</span>中的<span lang=EN-US>COPY</span>来恢复<span lang=EN-US>VM</span>中的数据，因为备份在<span lang=EN-US>SM</span>中的数据并不是全部都会提交到<span lang=EN-US>VM</span>中，所以实际备份数据可以远多于<span lang=EN-US>VM</span>容量，随着资源的不断增多，备份数据很可能被交换到硬盘上，这是<span lang=EN-US>RESET</span>的过程可能变得异常缓慢，<span lang=EN-US>RUNTIME</span>给每个<span lang=EN-US>MANAGED</span>资源都保留了一个时间戳，当<span lang=EN-US>RUNTIME</span>需要把备份数据拷贝到<span lang=EN-US>VM</span>中时，<span lang=EN-US>RUNTIME</span>会在<span lang=EN-US>VM</span>中分配显存空间，如果分配失败，表示<span lang=EN-US>VM</span>已经没有可用空间，这样<span lang=EN-US>RUNTIME</span>会使用<span lang=EN-US>LRU</span>算法根据时间戳释放相关资源，<span lang=EN-US>SetPriority</span>通过时间戳来设置资源的优先级，最近常用的资源将拥有高的优先级，这样<span lang=EN-US>RUNTIME</span>通过优先级就能合理的释放资源，发生释放后马上又要使用这种情况的几率会比较小，应用程序还可以调用<span lang=EN-US>EvictManagedResources</span>强制清空<span lang=EN-US>VM</span>中的所有<span lang=EN-US>MANAGED</span>资源，这样如果下一帧有用到<span lang=EN-US>MANAGED</span>资源，<span lang=EN-US>RUNTIME</span>需要重新载入，这样对性能有很大影响，平时一般不要使用，但在关卡转换的时候，这个函数是非常有用的，可以消除<span lang=EN-US>VM</span>中的内存碎片。<span lang=EN-US>LRU</span>算法在某些情况下有性能缺陷，比如绘制一帧所需资源量无法被<span lang=EN-US>VM</span>装下的时候（<span lang=EN-US>MANAGED</span>），使用<span lang=EN-US>LRU</span>算法会带来严重的性能波动，如下例子：</p>
<p><span lang=EN-US>BeginScene();<br>Draw(Box0);<br>Draw(Box1);<br>Draw(Box2);<br>Draw(Box3);<br>Draw(Circle0);<br>Draw(Circle1);<br>EndScene();<br>Present();</span></p>
<p>假设<span lang=EN-US>VM</span>只能装下其中5个几何体的数据，那么根据<span lang=EN-US>LRU</span>算法，在绘制<span lang=EN-US>Box3</span>之前必须清空部分数据，那清空的必然是<span lang=EN-US>Circle0</span>&#8230;&#8230;，很显然清空<span lang=EN-US>Box2</span>是最合理的，所以这是<span lang=EN-US>RUNTIME</span>使用<span lang=EN-US>MRU</span>算法处理后续<span lang=EN-US>Draw Call</span>能很好的解决性能波动问题，但资源是否被使用是按<span lang=EN-US>FRAME</span>为单位来检测的，并不是每个<span lang=EN-US>DRAW CALL</span>都被记录，每个<span lang=EN-US>FRAME</span>的标志就是<span lang=EN-US>BEGINSCENE/ENDSCENE</span>对，所以在这种情况下合理使用<span lang=EN-US>BEGINSCENE/ENDSCENE</span>对可以很好的提高<span lang=EN-US>VM</span>不够情况下的性能。根据<span lang=EN-US>DX</span>文档的提示我们还可以使用<span lang=EN-US>QUERY</span>机制来获得更多关于<span lang=EN-US>RUNTIME MANAGED RESOURCE</span>信息，但好像只在<span lang=EN-US>RUNTIME DEBUG</span>模式下有用，理解<span lang=EN-US>RUNTIME</span>如何<span lang=EN-US>MANAGE RESOURCE</span>很重要，但编写程序的时候不要将这些细节暴露出来，因为这些东西都是经常会变的。最后还要提醒的是，不光<span lang=EN-US>RUNTEIME</span>会<span lang=EN-US>MANAGE RESOURCE</span>，<span lang=EN-US>DRIVER</span>也很可能也实现了这些功能，我们可以通过<span lang=EN-US>D3DCAPS2_CANMANAGERESOURCE</span>标志取得<span lang=EN-US>DRIVER</span>是否实现资源管理功能的信息，而且也可以在<span lang=EN-US>CreateDevice</span>的时候指定<span lang=EN-US>D3DCREATE_DISABLE_DRIVER_MANAGEMENT</span>来关闭<span lang=EN-US>DRIVER</span>资源管理功能。&nbsp;&nbsp;</p>
<p><span lang=EN-US>D3DLOCK</span>探索<span lang=EN-US>D3D RUNTIME</span>工作</p>
<p>如果<span lang=EN-US>LOCK DEFAULT</span>资源会发生什么情况呢？<span lang=EN-US>DEFAULT</span>资源可能在<span lang=EN-US>VM</span>或<span lang=EN-US>AM</span>中，如果在<span lang=EN-US>VM</span>中，必须在系统内容中开辟一个临时缓冲返回给数据，当应用程序将数据填充到临时缓冲后，<span lang=EN-US>UNLOCK</span>的时候，<span lang=EN-US>RUNTIME</span>会将临时缓冲的数据传回到<span lang=EN-US>VM</span>中去，如果资源<span lang=EN-US>D3DUSAGE</span>属性不是<span lang=EN-US>WRITEONLY</span>的，则系统还需要先从<span lang=EN-US>VM</span>里拷贝一份原始数据到临时缓冲区，这就是为什么不指定<span lang=EN-US>WRITEONLY</span>会降低程序性能的原因。<span lang=EN-US>CPU</span>写<span lang=EN-US>AM</span>也有需要注意的地方，因为<span lang=EN-US>CPU</span>写<span lang=EN-US>AM</span>一般是<span lang=EN-US>WRITE COMBINING</span>，也就是说将写缓冲到一个<span lang=EN-US>CACHE LINE</span>上，当<span lang=EN-US>CACHE LINE</span>满了之后才<span lang=EN-US>FLUSH</span>到<span lang=EN-US>AM</span>中去，第一个要注意的就是写数据必须是<span lang=EN-US>WEAK ORDER</span>的（图形数据一般都满足这个要求），据说<span lang=EN-US>D3DRUNTIME</span>和<span lang=EN-US>NV DIRVER</span>有点小<span lang=EN-US>BUG</span>，就是在<span lang=EN-US>CPU</span>没有<span lang=EN-US>FLUSH</span>到<span lang=EN-US>AM</span>时，<span lang=EN-US>GPU</span>就开始绘制相关资源产生的错误，这时请使用<span lang=EN-US>SFENCE</span>等指令<span lang=EN-US>FLUSH CACHE LINE</span>。第二请尽量一次写满一个<span lang=EN-US>CACHE LINE</span>，否则会有额外延迟，因为<span lang=EN-US>CPU</span>每次必须<span lang=EN-US>FLUSH</span>整个<span lang=EN-US>CACHE LINE</span>到目标，但如果我们只写了<span lang=EN-US>LINE</span>中部分字节，<span lang=EN-US>CPU</span>必须先从<span lang=EN-US>AM</span>中读取整个<span lang=EN-US>LINE</span>长数据<span lang=EN-US>COMBINE</span>后重新<span lang=EN-US>FLUSH</span>。第三尽可能顺序写，随机写会让<span lang=EN-US>WRITE COMBINING</span>反而变成累赘，如果是随机写资源，不要使用<span lang=EN-US>D3DUSAGE_DYNAMIC</span>创建，请使用<span lang=EN-US>D3DPOOL_MANAGED</span>，这样写会完全在<span lang=EN-US>SM</span>中完成。</p>
<p>普通纹理（<span lang=EN-US>D3DPOOL_DEFAULT</span>）是不能被锁定的，因为其位于<span lang=EN-US>VM</span>中，只能通过<span lang=EN-US>UPDATESURFACE</span>和<span lang=EN-US>UPDATETEXTURE</span>来访问，为什么<span lang=EN-US>D3D</span>不让我们锁定静态纹理，却让我们锁定静态<span lang=EN-US>VB IB</span>呢？我猜测可能有<span lang=EN-US>2</span>个方面的原因，第一就是纹理矩阵一般十分庞大，且纹理在<span lang=EN-US>GPU</span>内部已二维方式存储；第二是纹理在<span lang=EN-US>GPU</span>内部是以<span lang=EN-US>NATIVE FORMAT</span>方式存储的，并不是明文<span lang=EN-US>RGBA</span>格式。动态纹理因为表明这个纹理需要经常修改，所以<span lang=EN-US>D3D</span>会特别存储对待，高频率修改的动态纹理不适合用动态属性创建，在此分两种情况说明，一种是<span lang=EN-US>GPU</span>写入的<span lang=EN-US>RENDERTARGET</span>，一种是<span lang=EN-US>CPU</span>写入的<span lang=EN-US>TEXTURE VIDEO</span>，我们知道动态资源一般是放置在<span lang=EN-US>AM</span>中的，<span lang=EN-US>GPU</span>访问<span lang=EN-US>AM</span>需要经过<span lang=EN-US>AGP/PCI-E</span>总线，速度较<span lang=EN-US>VM</span>慢许多，而<span lang=EN-US>CPU</span>访问<span lang=EN-US>AM</span>又较<span lang=EN-US>SM</span>慢很多，如果资源为动态属性，意味着<span lang=EN-US>GPU</span>和<span lang=EN-US>CPU</span>访问资源会持续的延迟，所以此类资源最好以<span lang=EN-US>D3DPOOL_DEFAULT</span>和<span lang=EN-US>D3DPOOL_SYSTEMMEM</span>各创建一份，自己手动进行双向更新更好。千万别&nbsp;<span lang=EN-US>RENDERTARGET</span>以<span lang=EN-US>D3DPOOL_MANAGED&nbsp;</span>属性创建，这样效率极低，原因自己分析。而对于改动不太频繁的资源则推荐使用<span lang=EN-US>DEFAULT</span>创建，自己手动更新，因为一次更新的效率损失远比<span lang=EN-US>GPU</span>持续访问<span lang=EN-US>AM</span>带来的损失要小。&nbsp;</p>
<p>不合理的<span lang=EN-US>LOCK</span>会严重影响程序性能，因为一般<span lang=EN-US>LOCK</span>需要等待<span lang=EN-US>COMMAND BUFFER</span>前面的绘制指令全部执行完毕才能返回，否则很可能修改正在使用的资源，从<span lang=EN-US>LOCK</span>返回到修改完毕<span lang=EN-US>UNLOCK</span>这段时间<span lang=EN-US>GPU</span>全部处于空闲状态，没有合理使用<span lang=EN-US>GPU</span>和<span lang=EN-US>CPU</span>的并行性，<span lang=EN-US>DX8.0</span>引进了一个新的<span lang=EN-US>LOCK</span>标志<span>D3DLOCK_DISCARD，表示不会读取资源，只会全写资源，这样驱动和RUNTIME配合来了个瞒天过海，立即返回给应用程序另外块VM地址指针，而原指针在本次UNLOCK之后被丢弃不再使用，这样CPU LOCK无需等待GPU使用资源完毕，能继续操作图形资源（顶点缓冲和索引缓冲），这技术叫VB IB换名（renaming）。</span>&nbsp;</p>
<p>很多困惑来源于底层资料的不足，相信要是MS开放D3D源码，开放驱动接口规范，NV / ATI显示开放驱动和硬件架构信息，这些东西就很容易弄明白了。</p>
<p>顺便做个书的广告 《人工智能：一种现代方法》中文版 卓越网已经有货，AI巨作，不过阅读需要相当的基础，对思维非常有启迪，想买的朋友不要错过。后面我会将学习重点从图形转到AI上来，对AI有兴趣的朋友一起交流。</p>
<img src ="http://www.cppblog.com/Leaf/aggbug/93109.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Leaf/" target="_blank">大渊献</a> 2009-08-13 00:29 <a href="http://www.cppblog.com/Leaf/archive/2009/08/13/93109.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Direct3D9 中的Gamma矫正</title><link>http://www.cppblog.com/Leaf/archive/2009/08/13/93107.html</link><dc:creator>大渊献</dc:creator><author>大渊献</author><pubDate>Wed, 12 Aug 2009 16:17:00 GMT</pubDate><guid>http://www.cppblog.com/Leaf/archive/2009/08/13/93107.html</guid><wfw:comment>http://www.cppblog.com/Leaf/comments/93107.html</wfw:comment><comments>http://www.cppblog.com/Leaf/archive/2009/08/13/93107.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Leaf/comments/commentRss/93107.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Leaf/services/trackbacks/93107.html</trackback:ping><description><![CDATA[纹理内容常常是存在sRGB格式中的。关于这个格式的细节是可以被找到的。通常，像素管线假定颜色是线性的以便融合（blending）操作可以在线性空间中进行。因为sRGB中的内容是Gamma较正，所以融合操作在线性空间中处理会导致错误的结果。显卡在读到有关sRGB内容的时候便会取消Gamma较正以避免错误的发生。然后当输出像素的时候再将像素信息写回sRGB格式中。在这种情况下，所有像素管线中的操作就可以都在心线性空间中进行。<br><br>Gamma校正<br>在D3D9中。<br>&nbsp;&nbsp;&nbsp;&nbsp; 可以指明一张纹理是不是Gamma 2.2（sRGB) 较正.驱动程序将会在SetTexture的时候将其转换到线性的Gamma以进行融合操作。或者采样器将会在查询的时候将其变为线性数据。<br>&nbsp;&nbsp;&nbsp;&nbsp; 可以指明像素管线在输出到渲染目标的时候是否将Gamma校正变换回sRGB空间。<br><br>所有其它颜色(clear color, material color, vertex color, etc）都被假定为线性空间中。应用程序可以用像素着色器指令对写入到帧缓存中的颜色进行Gamma校正。线性化操作只对RGB通道有效，忽略ALPHA通道。<br><br>不是所有的表面格式都可以线性化。只有通过 <a href="mk:@MSITStore:D:\Program%20Files\Microsoft%20DirectX%20SDK%20(October%202006)\Documentation\DirectX9\directx9_c.chm::/IDirect3D9__CheckDeviceFormat.htm">IDirect3D9::CheckDeviceFormat</a>检测（参数为D3DUSAGE_QUERY_SRGBREAD ）的格式才可以被线性化。除此之外，采样状态D3DSAMP_SRGBTEXTURE 也会被忽略。只有无符号纹理格式支持这种变换。无符号纹理格式是指仅包含有R G B 和 L成分的纹理格式。如果包含ALPHA通道，那它将被忽略。如果混合的格式支持sRGB线性化，那么只有无符号通道有效。理想情况是硬件在纹理过滤前实现线性化。但在D3D9中，硬件只有在纹理过滤后才允许线性化。<br><br>不是所有的表面都可以被写进sRGB空间，只有通过用D3DUSAGE_QUERY_SRGBWRITE进行<strong>IDirect3D9::CheckDeviceFormat </strong>测试的表面格式才能进行线性化。另外，渲染状态中的D3DRS_SRGBWRITEENABLE标志将会被忽略。每个通道8位的无符号RGB格式是比较适合的格式。<br>理想地，硬件将会在线性空间上进行帧缓存融合操作。但实际上硬件只能在像素管线处理后，帧缓存融合前进行。这意味着在sRGB中进行帧缓存融合操作会导致错误的结果。当清除渲染目标时。D3DRS_SRGBWRITEENABLE 标志 is Honored.对于硬件支持多渲染目标或多元素纹理的情况，只有第一个渲染目标和第一个元素会被写入缓存。<br><br>API变化<br><br>
<h3><a name=API_Changes></a>API Changes</h3>
<pre class=clsCode>// New sampler state (DWORD) 新的采样器状态
// If this is nonzero, the texture is linearized on lookup.</pre>
<pre class=clsCode>如果它非0，纹理在查询是线性化。
D3DSAMP_SRGBTEXTURE       // Default FALSE   默认为假
// New render state (DWORD)</pre>
<pre class=clsCode>新的渲染状态
D3DRS_SRGBWRITEENABLE     // Default FALSE 默认为假
// New usage flags</pre>
<pre class=clsCode>新的使用标志
D3DUSAGE_QUERY_SRGBWRITE
D3DUSAGE_QUERY_SRGBREAD</pre>
<pre class=clsCode>&nbsp;</pre>
<pre class=clsCode>窗口下的交换链</pre>
<pre class=clsCode>为了进行正确的融合操作，应用程序保存他们的交换链在线性空间中的后台缓冲区是非常必要的。因为桌面通常情况下是不在线性空间的。所以需要在后台缓冲区内容显示前进行Gamma校正。</pre>
<pre class=clsCode>应用程序可以通过新增额外的缓冲区来自我校正，并把他自已正确的结果从线性空间复制到后台缓冲区。当驱动将Gamma校正作为部分显示的时候，是可以避免使用额外的缓冲区的。
</pre>
<img src ="http://www.cppblog.com/Leaf/aggbug/93107.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Leaf/" target="_blank">大渊献</a> 2009-08-13 00:17 <a href="http://www.cppblog.com/Leaf/archive/2009/08/13/93107.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>IDirect3DDevice9::Reset 失败的原因</title><link>http://www.cppblog.com/Leaf/archive/2009/08/11/92897.html</link><dc:creator>大渊献</dc:creator><author>大渊献</author><pubDate>Tue, 11 Aug 2009 06:22:00 GMT</pubDate><guid>http://www.cppblog.com/Leaf/archive/2009/08/11/92897.html</guid><wfw:comment>http://www.cppblog.com/Leaf/comments/92897.html</wfw:comment><comments>http://www.cppblog.com/Leaf/archive/2009/08/11/92897.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Leaf/comments/commentRss/92897.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Leaf/services/trackbacks/92897.html</trackback:ping><description><![CDATA[许多时候，需要处理设备丢失问题，而通常情况下，RESET会因为一些小问题而导致失败，下面我就把gamedev上的一贴子翻译一下，只翻译中间那小段<br>摘自: <a href="http://www.gamedev.net/community/forums/topic.asp?topic_id=146731"><u><font color=#0000ff>http://www.gamedev.net/community/forums/topic.asp?topic_id=146731</font></u></a>
<div>1) One of the parameters you pass is probably not possible on the hardware, e.g. a depth buffer format which won't work with the back buffer format.<br><br>你传入的D3DPRESENT_PARAMETERS和你的硬件不符，可能是深度格式与你的后台缓冲格式不匹配。通常情况下我们是将先前的D3DPRESENT_PARAMETERS保存，RESET的时候传入，若是这种情况，则不必担心这个问题</div>
<div><br>2) The debug D3D runtime will tell you exactly "why":<br><br>把DirectX Control Pannel中的Direct3D开为调试模式，运行过后，编译器的信息提示框里会输出原因，多半是因为位于D3DPOOL_DEFAULT中的内容未释放完而导致的</div>
<div>a. When you install the DirectX SDK you get the option to install the debug or retail runtime, if you're developing software, always choose debug.<br><br>安装SDK的时候，你可以选则是调式还是运行模式，如果你是软件开发，通常选择为调式</div>
<div>b. Go to the control panel and open the DirectX applet.<br><br>到SDK中把DirectX Control Pannel小程序打开</div>
<div>c. Go to the Direct3D tab and put the "debug output level" slider to maximum.<br><br>把DirectX Control Pannel中的Direct3D开为调试模式</div>
<div>d. Run your application in the debugger (if using MSVC, press F5) and repeat whatever process causes it to fail.<br>在调试状态下运行你的程序，重复处理导致你出错的地方<br></div>
<div>e. Once it fails, close the app if necessary and return to MSVC, now look in the "output" pane (usually at the bottom). D3D will tell you about everything noteworthy, from information about its DLL being attached to your application, to warnings about things which may harm performance to the full reason why it gave an error.<br><br>如果发现失败了，就关掉调试，在输出信息面板中D3D将会告诉你是什么原因导致你失败的。</div>
<div>f. If your application creates its D3D device in PURE mode, creating it in non-PURE mode should enable more checking and reporting.<br><br>如果你的程序创建的时候的D3D设备是PURE模式，那在创建的时候改为非PURE模式，这样你在上面的控制面板中得到的信息会更多。</div>
<div><br>&nbsp;</div>
<img src ="http://www.cppblog.com/Leaf/aggbug/92897.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Leaf/" target="_blank">大渊献</a> 2009-08-11 14:22 <a href="http://www.cppblog.com/Leaf/archive/2009/08/11/92897.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>翻译 D3DPOOL</title><link>http://www.cppblog.com/Leaf/archive/2009/08/11/92850.html</link><dc:creator>大渊献</dc:creator><author>大渊献</author><pubDate>Mon, 10 Aug 2009 16:34:00 GMT</pubDate><guid>http://www.cppblog.com/Leaf/archive/2009/08/11/92850.html</guid><wfw:comment>http://www.cppblog.com/Leaf/comments/92850.html</wfw:comment><comments>http://www.cppblog.com/Leaf/archive/2009/08/11/92850.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.cppblog.com/Leaf/comments/commentRss/92850.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Leaf/services/trackbacks/92850.html</trackback:ping><description><![CDATA[<h1>边看边写下来的，肯定翻译得不好，有要看的将就一下</h1>
<h1>D3DPOOL</h1>
<p>Defines the memory class that holds the buffers for a resource.<br>这句不用翻译<br></p>
<pre class=syntax>typedef enum D3DPOOL
{
<font style="BACKGROUND-COLOR: #0a246a" color=#ffffff>D3DPOOL_DEFAULT</font> = 0,
D3DPOOL_MANAGED = 1,
D3DPOOL_SYSTEMMEM = 2,
D3DPOOL_SCRATCH = 3,
D3DPOOL_FORCE_DWORD = 0x7fffffff,
} D3DPOOL, *LPD3DPOOL;</pre>
<h4><a name=constants></a>Constants 常量</h4>
<dl>
<dt><a name=D3DPOOL_DEFAULT></a><font style="BACKGROUND-COLOR: #0a246a" color=#ffffff>D3DPOOL_DEFAULT</font>
<dd>Resources are placed in the memory pool most appropriate for the set of usages requested for the given resource. This is usually video memory, including both local video memory and AGP memory. The <font style="BACKGROUND-COLOR: #0a246a" color=#ffffff>D3DPOOL_DEFAULT</font> pool is separate from D3DPOOL_MANAGED and D3DPOOL_SYTEMMEM, and it specifies that the resource is placed in the preferred memory for device access. Note that <font style="BACKGROUND-COLOR: #0a246a" color=#ffffff>D3DPOOL_DEFAULT</font> never indicates that either D3DPOOL_MANAGED or D3DPOOL_SYSTEMMEM should be chosen as the memory pool type for this resource. Textures placed in the <font style="BACKGROUND-COLOR: #0a246a" color=#ffffff>D3DPOOL_DEFAULT</font> pool cannot be locked unless they are dynamic textures or they are private, FOURCC, driver formats. To access unlockable textures, you must use functions such as <a href="mk:@MSITStore:D:\Program%20Files\Microsoft%20DirectX%20SDK%20(October%202006)\Documentation\DirectX9\directx9_c.chm::/IDirect3DDevice9__UpdateSurface.htm"><u><font color=#0000ff>IDirect3DDevice9::UpdateSurface</font></u></a>, <a href="mk:@MSITStore:D:\Program%20Files\Microsoft%20DirectX%20SDK%20(October%202006)\Documentation\DirectX9\directx9_c.chm::/IDirect3DDevice9__UpdateTexture.htm"><u><font color=#0000ff>IDirect3DDevice9::UpdateTexture</font></u></a>, <a href="mk:@MSITStore:D:\Program%20Files\Microsoft%20DirectX%20SDK%20(October%202006)\Documentation\DirectX9\directx9_c.chm::/IDirect3DDevice9__GetFrontBufferData.htm"><u><font color=#0000ff>IDirect3DDevice9::GetFrontBufferData</font></u></a>, and <a href="mk:@MSITStore:D:\Program%20Files\Microsoft%20DirectX%20SDK%20(October%202006)\Documentation\DirectX9\directx9_c.chm::/IDirect3DDevice9__GetRenderTarGetData.htm"><u><font color=#0000ff>IDirect3DDevice9::GetRenderTargetData</font></u></a>. D3DPOOL_MANAGED is probably a better choice than <font style="BACKGROUND-COLOR: #0a246a" color=#ffffff>D3DPOOL_DEFAULT</font> for most applications. Note that some textures created in driver-proprietary pixel formats, unknown to the Direct3D runtime, can be locked. Also note that - unlike textures - swap chain back buffers, render targets, vertex buffers, and index buffers can be locked. When a device is lost, resources created using <font style="BACKGROUND-COLOR: #0a246a" color=#ffffff>D3DPOOL_DEFAULT</font> must be released before calling <a href="mk:@MSITStore:D:\Program%20Files\Microsoft%20DirectX%20SDK%20(October%202006)\Documentation\DirectX9\directx9_c.chm::/IDirect3DDevice9__Reset.htm"><u><font color=#800080>IDirect3DDevice9::Reset</font></u></a>. For more information, see <a href="mk:@MSITStore:D:\Program%20Files\Microsoft%20DirectX%20SDK%20(October%202006)\Documentation\DirectX9\directx9_c.chm::/lost_devices.htm"><u><font color=#0000ff>Lost Devices</font></u></a>.
<p>When creating resources with <font style="BACKGROUND-COLOR: #0a246a" color=#ffffff>D3DPOOL_DEFAULT</font>, if video card memory is already committed, managed resources will be evicted to free enough memory to satisfy the request.<br><br><br>资源被放入内存池中多半是为了给被请求的资源腾出使用的空间。 通常是显存，包括显卡道内存中的AGP部分。<font style="BACKGROUND-COLOR: #0a246a" color=#ffffff>D3DPOOL_DEFAULT</font> 是从MANGED和SYSTEMMEM中分离出来的。它指明了被放入此中的资源是用来被设备访问。注意，<font style="BACKGROUND-COLOR: #0a246a" color=#ffffff>D3DPOOL_DEFAULT</font> 没有意味着MANAGED或SYSTEMMEM将会被选择用来存储资源，这是一个独立的部分。放入<font style="BACKGROUND-COLOR: #0a246a" color=#ffffff>D3DPOOL_DEFAULT</font> 中的纹理不可以被LOCK除非是动态纹理或是私有纹理。FOURCC，驱动格式，为了访问未锁定的纹理，我们必须用<a href="mk:@MSITStore:D:\Program%20Files\Microsoft%20DirectX%20SDK%20(October%202006)\Documentation\DirectX9\directx9_c.chm::/IDirect3DDevice9__UpdateSurface.htm"><u><font color=#0000ff>IDirect3DDevice9::UpdateSurface</font></u></a>, <a href="mk:@MSITStore:D:\Program%20Files\Microsoft%20DirectX%20SDK%20(October%202006)\Documentation\DirectX9\directx9_c.chm::/IDirect3DDevice9__UpdateTexture.htm"><u><font color=#0000ff>IDirect3DDevice9::UpdateTexture</font></u></a>, <a href="mk:@MSITStore:D:\Program%20Files\Microsoft%20DirectX%20SDK%20(October%202006)\Documentation\DirectX9\directx9_c.chm::/IDirect3DDevice9__GetFrontBufferData.htm"><u><font color=#0000ff>IDirect3DDevice9::GetFrontBufferData</font></u></a>, and <a href="mk:@MSITStore:D:\Program%20Files\Microsoft%20DirectX%20SDK%20(October%202006)\Documentation\DirectX9\directx9_c.chm::/IDirect3DDevice9__GetRenderTarGetData.htm"><u><font color=#0000ff>IDirect3DDevice9::GetRenderTargetData</font></u></a>.函数。显然，对于许多应用程序来说，MANAGED是更好的选择。<br>注意，许多纹理是以私有的格式创建的。运行时D3D并不知道。是可以被加锁的。同时应该注意，不像纹理，交换链后台缓冲区，渲染目标，顶点缓冲，索引缓冲是可以被加锁的。当设备丢失的时候，<font style="BACKGROUND-COLOR: #0a246a" color=#ffffff>使用D3DPOOL_DEFAULT</font> 创建的资源必须要在调用Reset之前释放。可以参看D3D的LostDevice.<br>当使用<font style="BACKGROUND-COLOR: #0a246a" color=#ffffff>D3DPOOL_DEFAULT</font> 创建资源，并且显卡的内存有限的时候，MANAGED中的资源将会被清除以释放足够的内存来满足需求。<br></p>
<dt><a name=D3DPOOL_MANAGED></a>D3DPOOL_MANAGED
<dd>Resources are copied automatically to device-accessible memory as needed. Managed resources are backed by system memory and do not need to be recreated when a device is lost. See <a href="mk:@MSITStore:D:\Program%20Files\Microsoft%20DirectX%20SDK%20(October%202006)\Documentation\DirectX9\directx9_c.chm::/managing_resources.htm"><u><font color=#0000ff>Managing Resources</font></u></a> for more information. Managed resources can be locked. Only the system-memory copy is directly modified. Direct3D copies your changes to driver-accessible memory as needed.
<dd>在MANAGED中的当有需要的时候，会被自动复制到设备的可访问内存。MANAGED资源在系统内存中是有备份的，于是当设备丢失的时候，不需要重新创建。参见<a href="mk:@MSITStore:D:\Program%20Files\Microsoft%20DirectX%20SDK%20(October%202006)\Documentation\DirectX9\directx9_c.chm::/managing_resources.htm"><u><font color=#0000ff>Managing Resources</font></u></a> 。MANAGED资源可以被加锁，只有系统内存中的备份是直接被修改的。当有必要的时候，D3D复制你所修改的内容到系统可访问区
<dt><a name=D3DPOOL_SYSTEMMEM></a>D3DPOOL_SYSTEMMEM
<dd>Resources are placed in memory that is not typically accessible by the Direct3D device. This memory allocation consumes system RAM but does not reduce pageable RAM. These resources do not need to be recreated when a device is lost. Resources in this pool can be locked and can be used as the source for a <strong>IDirect3DDevice9::UpdateSurface</strong> or <strong>IDirect3DDevice9::UpdateTexture</strong> operation to a memory resource created with <font style="BACKGROUND-COLOR: #0a246a" color=#ffffff>D3DPOOL_DEFAULT</font>.
<dd>被放置在SYSTMMEM中的资源是典型地不可被3D直接访问的。这种内存分配消耗系统的RAM但是不减少RAM的可用页。在设备丢失的时候，这些资源不需要再次创建。在这个内存池中的资源可以被加锁，可以被用来让<strong>IDirect3DDevice9::UpdateSurface</strong> or <strong>IDirect3DDevice9::UpdateTexture</strong> 去操作以<font style="BACKGROUND-COLOR: #0a246a" color=#ffffff>D3DPOOL_DEFAULT</font>. 方式创建的资源
<dt><a name=D3DPOOL_SCRATCH></a>D3DPOOL_SCRATCH
<dd>Resources are placed in system RAM and do not need to be recreated when a device is lost. These resources are not bound by device size or format restrictions. Because of this, these resources cannot be accessed by the Direct3D device nor set as textures or render targets. However, these resources can always be created, locked, and copied.
<dd>这种资源被放在系统的RAM中，设备丢失时候不用重新创建。这种资源不受设备大小和格式的限制。为此，这种资源不能被D3D访问，也不能设置为纹理或渲染目标。但是，这种资源总是可以被创建，加锁和复制。
<dt><a name=D3DPOOL_FORCE_DWORD></a>D3DPOOL_FORCE_DWORD
<dd>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.
<dd>强制编译器使用32位来编译这个枚举量。若没有这个值，一些编译器将允许这个枚举以非32位的方式编译。这个值未被使用。</dd></dl>
<h4><a name=remarks></a>Remarks 评论</h4>
<p>All pool types are valid with all resources. This includes: vertex buffers, index buffers, textures, and surfaces.<br>所有的内存池类型对所有的资源有效。包括：顶点缓冲，索引缓冲，纹理，表面。<br><br><br>剩下的一些内容就是各种类型的资源对各种渲染目标的效性。参见SDK<br><br><br><span style="FONT-SIZE: 18pt; FONT-FAMILY: 黑体">以下为FancyBit对D3DUSAGE的翻译，由于和D3DPOOL关联较强，故贴在此里，十分感谢FancyBit</span><br>他的主页是：<a href="http://hi.baidu.com/148332727">http://hi.baidu.com/148332727</a>&nbsp;希望大家多多交流，共同进步<br><br></p>
<div class=comment_content>资源的使用的方式 <br><br>D3DUSAGE_RENDERTARGET 此纹理或表面作为一个渲染目标被创建，只能分配在D3DPOOL_DEFAULT的显卡内存中 <br><br>D3DUSAGE_AUTOGENMIPMAP 资源会自动生成多精度（多层次细节）纹理(mipmap)(Direct3D 9). 不支持volume textures 和深度表面/深度纹理（凹凸贴图）. 这个usage 不支持系统内存中的资源(用D3DPOOL_SYSTEMMEM参数创建的资源). <br><br>D3DUSAGE_DEPTHSTENCIL 此资源将会是一个深度缓冲，只能用D3DPOOL_DEFAULT分配. <br><br>D3DUSAGE_DMAP The resource will be a displacement map. ？？？ <br><br>D3DUSAGE_DONOTCLIP 顶点缓冲区内容不需要裁减. 当被渲染的缓冲区设置此位时,D3DRS_CLIPPING 渲染器状态必须设置为false. <br><br>D3DUSAGE_DYNAMIC 设置此位表示顶点缓冲需要动态的内存使用。这对驱动程序很有用因为它使得驱动程序可以决定把缓冲区放在哪里。一般的，静态顶端缓冲放置在显存儿动态缓冲防止在AGP内存中。注意如果你没有指定该标志位，那么顶点缓冲默认就是静态的。 <br><br>D3DUSAGE_DYNAMIC会被强制设置，当D3DLOCK_DISCARD 和 D3DLOCK_NOOVERWRITE 锁标志一起使用. 因此, D3DLOCK_DISCARD and D3DLOCK_NOOVERWRITE 只对使用 D3DUSAGE_DYNAMIC创建的顶点缓冲有用.更多参见Managing Resources (Direct3D 9). <br><br>更多关于动态顶点缓冲的信息，参见Performance Optimizations (Direct3D 9). <br><br>D3DUSAGE_DYNAMIC 和 D3DPOOL_MANAGED不兼容. See D3DPOOL. <br><br>纹理也可以使用D3DUSAGE_DYNAMIC.当然, 托管的纹理不能使用 D3DUSAGE_DYNAMIC. 关于动态纹理的信息，参见 Using Dynamic Textures. <br><br>D3DUSAGE_WRITEONLY <br>用于顶点缓冲和索引缓冲 <br>通知系统程序只想顶点和索引缓冲中写入数据。使用这个标记可以让驱动程序选择让写入和渲染操作效率最佳的内存分配方式。启用此特性后尝试从内存缓冲中读取数据的操作会失败。只对使用D3DPOOL_DEFAULT分配在显存中的数据有效。 <br><br>D3DUSAGE_RTPATCHES Set to indicate that the vertex buffer is to be used for drawing high-order primitives. ？？？ <br><br>D3DUSAGE_NONSECURE 允许创建的表面被另一个程序用一个无安全性的共享句柄打开，只在D3D9EX使用 <br><br>D3DUSAGE_TEXTAPI D3D9EX专用 略 <br><br>用于： <br>IDirect3DDevice9::CreateCubeTexture <br>IDirect3DDevice9::CreateDepthStencilSurface <br>IDirect3DDevice9::CreateIndexBuffer <br>IDirect3DDevice9::CreateOffscreenPlainSurface <br>IDirect3DDevice9::CreateRenderTarget <br>IDirect3DDevice9::CreateTexture <br>IDirect3DDevice9::CreateVertexBuffer <br>IDirect3DDevice9::CreateVolumeTexture <br>D3DXCreatexxx texturing functions</div>
<img src ="http://www.cppblog.com/Leaf/aggbug/92850.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Leaf/" target="_blank">大渊献</a> 2009-08-11 00:34 <a href="http://www.cppblog.com/Leaf/archive/2009/08/11/92850.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]关于Direct3D多窗口编程的一篇翻译</title><link>http://www.cppblog.com/Leaf/archive/2009/08/10/92824.html</link><dc:creator>大渊献</dc:creator><author>大渊献</author><pubDate>Mon, 10 Aug 2009 09:32:00 GMT</pubDate><guid>http://www.cppblog.com/Leaf/archive/2009/08/10/92824.html</guid><wfw:comment>http://www.cppblog.com/Leaf/comments/92824.html</wfw:comment><comments>http://www.cppblog.com/Leaf/archive/2009/08/10/92824.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.cppblog.com/Leaf/comments/commentRss/92824.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Leaf/services/trackbacks/92824.html</trackback:ping><description><![CDATA[<p><br>In DirectX 8, support for rendering to multiple windows is provided through the creation of additional swap chains. However, there are currently no examples of this in the SDK, and the documentation is a bit vague. This article is provided to fill the gaps, and will explain the steps you need to take to write an application that will render multiple views in separate windows.</p>
<p><br>在DX8中，对多窗口的支持是通过创建更多的Swap Chains来提供的。SDK中没有相关的例子而且文档也只是泛泛而谈。这篇文章就是为了解决这个问题，它将向您展示应当如何一步步地实现在多个分离窗口中渲染多个视图。</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>Step 1 - Setting Up The Parent Frame</p>
<p><br>第一步：设置父框架窗口</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>In an application with multiple views, we start with a top level frame that will contain child windows in its client area to display various views. Once the parent frame parent frame has been created, we create our Direct3D device interface, specifying windowed mode and setting the top level window handle as the focus window:</p>
<p><br>在多视图的应用程序中，我们需要从最高层次的框架——这个框架将包含所有在他用户区之内的子视图窗口——开始我们的旅程。当父框架创建的时候，我们需要创建Direct3D Device接口，为其指定使用窗口模式，而且设置这最高层次的窗口句柄作为&#8220;焦点窗口&#8221;的句柄：</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>g_pD3D=Direct3DCreate8(D3D_SDK_VERSION);</p>
<p><br>if (!g_pD3D) return -1;</p>
<p><br>D3DPRESENT_PARAMETERS d3dpp;</p>
<p><br>ZeroMemory( &amp;d3dpp, sizeof(d3dpp) );</p>
<p><br>d3dpp.Windowed = TRUE;</p>
<p><br>d3dpp.SwapEffect = D3DSWAPEFFECT_COPY;</p>
<p><br>// Use the current display mode. 使用当前的显示模式</p>
<p><br>D3DDISPLAYMODE mode;</p>
<p>if(FAILED(g_pD3D-&gt;GetAdapterDisplayMode(D3DADAPTER_DEFAULT , &amp;mode))) {</p>
<p><br>&nbsp;&nbsp;&nbsp; SAFE_RELEASE(g_pD3D);</p>
<p><br>&nbsp;&nbsp;&nbsp; return -1;</p>
<p><br>}</p>
<p><br>d3dpp.BackBufferFormat = mode.Format;</p>
<p><br>d3dpp.BackBufferWidth = mode.Width;</p>
<p><br>d3dpp.BackBufferHeight = mode.Height;</p>
<p><br>d3dpp.EnableAutoDepthStencil=TRUE;</p>
<p><br>d3dpp.AutoDepthStencilFormat = D3DFMT_D16;</p>
<p><br>// m_hWnd is handle to top level window&nbsp;&nbsp;&nbsp; m_hWnd是最高层窗口的句柄</p>
<p><br>if( FAILED( g_pD3D-&gt;CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, m_hWnd,</p>
<p><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; D3DCREATE_SOFTWARE_VERTEXPROCESSING,</p>
<p><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;d3dpp, &amp;g_pd3dDevice) ) ) {</p>
<p><br>&nbsp;&nbsp;&nbsp; SAFE_RELEASE(g_pD3D);</p>
<p><br>&nbsp;&nbsp;&nbsp; return -1;</p>
<p><br>}</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><br>Note that for simplicity the above code does not test depth format, instead choosing a fixed format. Your application should determine a compatible depth format for the format of the rendering target.</p>
<p><br>注意上面代码处于简单考虑并没有去测试深度缓存的格式（？depth format），而只是选择了一个确定的格式（D3DFMT_D16）。您的程序应该为需要渲染的Render Target选择一个可接受的深度缓存格式。</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>The device has a frame buffer, which the child views will be rendered into, as well as a depth buffer which will be shared among the views. The frame buffer and depth buffer are sized to the full screen resolution, to allow for the fact that the window may later be resized. Otherwise, window size changes would require resetting the device and re-creating the swap chains.</p>
<p><br>Device都需要有帧缓存，这样子视图才能进行渲染，同时，深度缓冲也应当被不同的视图进行共享。帧缓存和深度缓存都被设置为全屏幕大小，以考虑到可能窗口会被改变大小的情况。如果不的话，窗口改变大小的时候，就需要Reset Device和重新创建Swap Chain。</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>Step 2 - Setting Up View Windows</p>
<p><br>第二步：设置子视图窗口</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>Now we are ready to create our view windows, and associate them with swap chains that can be rendered to the device. Once the windows have been created, the following code generates a swap chain for the child window:</p>
<p><br>现在我们可以准备创建我们的子窗口也就是视图窗口，并把它们与交换链关联以使得他们可以被渲染到Device上。当窗口创建后，下面的代码将为子窗口创建一个交换链：</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>D3DPRESENT_PARAMETERS d3dpp;</p>
<p><br>ZeroMemory( &amp;d3dpp, sizeof(d3dpp) );</p>
<p><br>d3dpp.Windowed = TRUE;</p>
<p><br>d3dpp.SwapEffect = D3DSWAPEFFECT_COPY;</p>
<p><br>// Use the current display mode. 使用当前的显示模式</p>
<p><br>D3DDISPLAYMODE mode;</p>
<p><br>g_pD3D-&gt;GetAdapterDisplayMode(D3DADAPTER_DEFAULT , &amp;mode);</p>
<p><br>d3dpp.BackBufferFormat = mode.Format;</p>
<p><br>// m_hWnd contains child window handle m_hWnd储存子窗口的句柄</p>
<p><br>d3dpp.hDeviceWindow=m_hWnd;</p>
<p><br>// m_pSwapChain is IDirect3DSwapChain *&nbsp;&nbsp; m_pSwapChain是一个IDirect3DSwapChain*对象</p>
<p><br>g_pd3dDevice-&gt;CreateAdditionalSwapChain(&amp;d3dpp, &amp;m_pSwapChain);</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><br>After executing this code, the m_pSwapChain variable will contain a pointer to an IDirect3DSwapChain interface, which contains a frame buffer corresponding to the client area of the child window. This process is performed for each view window, so that that there is a swap chain for each view window.</p>
<p><br>经过这些代码之后，m_pSwapChain变量就储存了IDirect3DSwapChain接口的指针，这个接口将储存子窗口视图区所对应的帧缓冲。</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>Step 3 - Rendering a View</p>
<p>第三步：渲染视图</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>Prior to rendering each view, we must direct the device to render to the appropriate frame buffer, using the SetRenderTarget() method. We pass the back buffer from the window's swap chain, while using the depth buffer that was originally created with the device:</p>
<p><br>在渲染每个视图窗口之前，我们必须使得Device来渲染对应的帧缓冲，这我们就需要用到SetRenderTarget方法。我们向其中传入子窗口SwapChain交换链的后备缓冲BackBuffer，以及使用最开始跟着Device一起创建的深度缓冲。</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>LPDIRECT3DSURFACE8 pBack=NULL,pStencil=NULL;</p>
<p><br>m_pSwapChain-&gt;GetBackBuffer(0,D3DBACKBUFFER_TYPE_MONO,&amp;pBack);</p>
<p><br>g_pd3dDevice-&gt;GetDepthStencilSurface(&amp;pStencil);</p>
<p><br>g_pd3dDevice-&gt;SetRenderTarget(pBack,pStencil);</p>
<p><br>pBack-&gt;Release();</p>
<p><br>pStencil-&gt;Release();</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><br>Note that we release the stencil and backbuffer pointers after we use them, because the GetBackBuffer() and GetDepthStencilSurface() functions call AddRef() on these interfaces to increment their reference counters. Failing to release them would lead to a memory leak.</p>
<p><br>注意我们必须Release掉Stencil和BackBuffer的指针，因为GetBackBuffer和GetDepthStencilSurface这两个函数都会调用COM的AddRef方法，来增加相应COM接口的引用计数，因此如果不删除它们，将会导致内存泄露。</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>We are now ready to render the view. Rendering is performed within a scene in the normal manner, except that we call Present() on the swap chain interface rather than the device interface:</p>
<p><br>我们现在已经做好准备渲染视图窗口了。渲染的方法看起来和我们平常用的方法差不多，只是有一点：我们现在需要调用Swap Chain的接口，而不是Device的接口。</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>g_pd3dDevice-&gt;Clear(0,NULL,D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER,0x00000000,1.0,0);</p>
<p><br>if (SUCCEEDED(g_pd3dDevice-&gt;BeginScene())) {</p>
<p><br>&nbsp;&nbsp; </p>
<p><br>&nbsp;&nbsp;&nbsp; // rendering code goes here 渲染代码写在这里</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><br>&nbsp;&nbsp;&nbsp; g_pd3dDevice-&gt;EndScene();</p>
<p><br>}</p>
<p><br>m_pSwapChain-&gt;Present(NULL,NULL,NULL,NULL);</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><br>Step 4 - Handling Resize of Child Views</p>
<p><br>第四步，子窗口的Resize问题</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>DirectX will automatically deal with changes in the child view by using a stretch blit to present the swap chain if the dimensions have client area is not the same size as the swap chain's frame buffer. However, this may not be desirable, as it will cause aliasing if the client area is increased in size.</p>
<p><br>如果窗口的视图区大小和SwapChain的大小不一，那么DirectX将通过Stretch Blit来自动处理图像的伸缩变化。尽管这可能并不令人期待，因为这在视图区变大的时候将导致图像的模糊。</p>
<p>&nbsp;<br></p>
<img src ="http://www.cppblog.com/Leaf/aggbug/92824.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Leaf/" target="_blank">大渊献</a> 2009-08-10 17:32 <a href="http://www.cppblog.com/Leaf/archive/2009/08/10/92824.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转：纹理矩阵</title><link>http://www.cppblog.com/Leaf/archive/2009/08/07/92552.html</link><dc:creator>大渊献</dc:creator><author>大渊献</author><pubDate>Fri, 07 Aug 2009 09:05:00 GMT</pubDate><guid>http://www.cppblog.com/Leaf/archive/2009/08/07/92552.html</guid><wfw:comment>http://www.cppblog.com/Leaf/comments/92552.html</wfw:comment><comments>http://www.cppblog.com/Leaf/archive/2009/08/07/92552.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Leaf/comments/commentRss/92552.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Leaf/services/trackbacks/92552.html</trackback:ping><description><![CDATA[<h1 class=block_title><a id=ctl03_TitleUrl href="http://www.cnblogs.com/billwillman/articles/1232081.html"><font color=#800080><u>[转]纹理矩阵</u></font></a></h1>
<div class=post>
<div class=postcontent>
<div class=postTitle><a class=postTitle2 id=viewpost1_TitleUrl href="http://www.cppblog.com/lovedday/archive/2008/05/20/50567.html"><u><font color=#0000ff>高级纹理映射技术（7）</font></u></a> </div>
<p><strong>纹理坐标变换</strong></p>
<p>Direct3D提供了对生成的纹理坐标进行坐标变换的功能，与顶点坐标变换相类似，可以指定一个4x4的纹理坐标变换矩阵，把它与生成的纹理坐标相乘，然后将变换之后的纹理坐标输出至Direct3D渲染流水线。使用纹理坐标变换可以对纹理坐标进行诸如平移、旋转和缩放等三维变换。纹理坐标变换对于生成一些特殊效果是非常有用的，它不用直接修改顶点的纹理坐标。例如可以通过一个简单的平移矩阵对纹理坐标进行变换，从而使物体表面上的纹理不断变换位置，产生动画效果。纹理坐标自动生成在三维图形程序中最广泛的应用是环境映射。</p>
<p>可通过函数IDirect3DDevice9::SetTransform()来设置4x4的纹理坐标变换矩阵，它以D3DTS_TEXTURE0~ D3DTS_TEXTURE7作为第一个参数，表示设置纹理层0~7的纹理矩阵。下列代码对纹理层0设置了一个将纹理坐标u、v缩小到原来一半的纹理矩阵：</p>
<p><font color=#004080>D3DXMATRIX mat;<br>D3DXMatrixIdentity(&amp;mat);<br>mat._11 = 0.5f;<br>mat._22 = 0.5f;<br>pd3dDevice-&gt;SetTransform(D3DTS_TEXTURE0, &amp;mat);</font></p>
<p>下面的代码将原来的纹理坐标平移（1.0, 1.0, 0）个单位。</p>
<p><font color=#004080>D3DXMATRIX mat;<br>D3DXMatrixIdentity(&amp;mat);<br>mat._41 = 1.0f;<br>mat._42 = 1.0f;<br>mat._43 = 0.0f;<br>pd3dDevice-&gt;SetTransform(D3DTS_TEXTURE0, &amp;mat);</font></p>
<p>示例程序通过下列代码对自动生成的纹理坐标进行变换：</p>
<p><font color=#004080>// texture coordinate transform<br><br>D3DXMATRIX mat_texture, mat_scale, mat_trans;<br><br>D3DXMatrixIdentity(&amp;mat_texture);<br>D3DXMatrixScaling(&amp;mat_scale, 0.5f, -0.5f, 1.0f);<br>D3DXMatrixTranslation(&amp;mat_trans, 0.5f, 0.5f, 1.0f);<br><br>mat_texture = mat_texture * mat_scale * mat_trans;<br>pd3dDevice-&gt;SetTransform(D3DTS_TEXTURE0, &amp;mat_texture);</font></p>
<br>要看效果和完整代码<br>去下面这个主页<br><br><a href="http://www.cnblogs.com/billwillman/articles/1232081.html">http://www.cnblogs.com/billwillman/articles/1232081.html</a><br><br><br>注：纹理矩阵很好很强大。可以做简单的水，天空盒背景等效果。<br>更为严重的是，还能做出流光效果，就像《蜀门》上的衣服效果。。<br></div>
</div>
<img src ="http://www.cppblog.com/Leaf/aggbug/92552.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Leaf/" target="_blank">大渊献</a> 2009-08-07 17:05 <a href="http://www.cppblog.com/Leaf/archive/2009/08/07/92552.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]D3D中的渲染到纹理</title><link>http://www.cppblog.com/Leaf/archive/2009/07/24/91094.html</link><dc:creator>大渊献</dc:creator><author>大渊献</author><pubDate>Fri, 24 Jul 2009 15:38:00 GMT</pubDate><guid>http://www.cppblog.com/Leaf/archive/2009/07/24/91094.html</guid><wfw:comment>http://www.cppblog.com/Leaf/comments/91094.html</wfw:comment><comments>http://www.cppblog.com/Leaf/archive/2009/07/24/91094.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Leaf/comments/commentRss/91094.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Leaf/services/trackbacks/91094.html</trackback:ping><description><![CDATA[<h2><a id=ctl10_TitleUrl href="http://www.cnblogs.com/flying_bat/archive/2008/08/15/1268370.html"><font color=#009933>D3D中的渲染到纹理</font></a> </h2>
<p><a href="http://www.cnblogs.com/flying_bat/">http://www.cnblogs.com/flying_bat/</a></p>
<p>渲染到纹理是D3D中的一项高级技术。一方面，它很简单，另一方面它很强大并能产生很多特殊效果。 比如说发光效果，环境映射，阴影映射，都可以通过它来实现。渲染到纹理只是渲染到表面的一个延伸。我们只需再加些东西就可以了。首先，我们要创造一个纹理，并且做好一些防范措施。第二步我们就可以把适当的场景渲染到我们创建的纹理上了。然后，我们把这个纹理用在最后的渲染上。<br>　　?main.cpp<br>　　首先我们得声明所需要的对象。当然我们需要一张用来渲染的纹理。此外，我们还需要两个Surface对象。一个是用来存储后台缓冲区，一个用来当纹理的渲染对象。后面我再详细介绍它们。另外我们还需要两个矩阵，一个是用来当纹理的投影矩阵，另一个是存储原来的矩阵。<br>　　LPDIRECT3DTEXTURE9 pRenderTexture = NULL;<br>　　LPDIRECT3DSURFACE9 pRenderSurface = NULL,pBackBuffer = NULL;<br>　　D3DXMATRIX matProjection,matOldProjection;<br>　　现在我们来创建纹理。前两个参数是纹理的宽度和高度，第三个参数是纹理的多级渐进纹理序列参数，在这里是设为1，第四个参数非常重要而且必须设为D3DUSAGE_RENDERTARGET，表明我们所创建的纹理是用来渲染的。剩下的参数就是指纹理格式，顶点缓冲区的内存位置，和一个指向纹理的指针。当纹理是用来当渲染对象时，顶点缓冲区的内存位置必须设为D3D_DEFAILT。<br>　　g_App.GetDevice()-&gt;CreateTexture(256,256,1,D3DUSAGE_RENDERTARGET,D3DFMT_R5G6B5,D3DPOOL_DEFAULT,&amp;pRenderTexture,NULL);<br>　　为了访问纹理内存对象，我们需要一个Surface对象，因为D3D中的纹理是用这样的一个Surface来存储纹理数据的。为了得到纹理表面的Surface,我们需要调用方法GetSurfaceLevel() 。第一个参数我们设为0，第二个参数为一个指向surface对象的指针。<br>　　pRenderTexture-&gt;GetSurfaceLevel(0,&amp;pRenderSurface);<br>　　下一步就是创建一个适合纹理维数的投影矩阵，因为纹理的横纵比和后台缓冲区的不一样。<br>　　D3DXMatrixPerspectiveFovLH(&amp;matProjection,D3DX_PI / 4.0f,1,1,100);<br>　　在我们的循环渲染之前，我们必须保存后台缓冲区和它的投影矩阵。<br>　　g_App.GetDevice()-&gt;GetTransform(D3DTS_PROJECTION,&amp;matOldProjection);<br>　　g_App.GetDevice()-&gt;GetRenderTarget(0,&amp;pBackBuffer);<br>　　渲染循环函数可以分为两个部分。第一部分是渲染到纹理的过程。因此，渲染对象必须设为纹理表面。然后我们就可以把东西渲染到这个对象上了。渲染到另一个表面上和正常地渲染到后台缓冲区差不多。只有一点不同，那就是先不调用Prensent（）函数，因为纹理上的内容并不需要显示在屏幕上。象平时一样，我们先要重置表面颜色缓冲区，并且调用BeginSence()和EndSence()方法。为了能够适当的渲染，我们必须设置和纹理表面相符的投影矩阵。否则最后的图象可能被扭曲<br>　　//render-to-texture<br>　　g_App.GetDevice()-&gt;SetRenderTarget(0,pRenderSurface); //set new render target<br>　　g_App.GetDevice()-&gt;Clear(0,NULL,D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,D3DCOLOR_XRGB(100,100,100),1.0f,0); //clear texture<br>　　g_App.GetDevice()-&gt;BeginScene();<br>　　g_App.GetDevice()-&gt;SetTexture(0,pPyramideTexture);<br>　　D3DXMatrixRotationY(&amp;matRotationY,fRotation);<br>　　D3DXMatrixTranslation(&amp;matTranslation,0.0f,0.0f,5.0f);<br>　　g_App.GetDevice()-&gt;SetTransform(D3DTS_WORLD,&amp;(matRotationY * matTranslation));<br>　　g_App.GetDevice()-&gt;SetTransform(D3DTS_PROJECTION,&amp;matProjection); //set projection matrix<br>　　g_App.GetDevice()-&gt;SetStreamSource(0,pTriangleVB,0,sizeof(D3DVERTEX));<br>　　g_App.GetDevice()-&gt;DrawPrimitive(D3DPT_TRIANGLELIST,0,4);<br>　　g_App.GetDevice()-&gt;EndScene();<br>　　渲染循环的第二部分就是渲染最后场景的过程（也就是显示到屏幕上的过程）。渲染对象重新设为后台缓冲区，投影矩阵重新设为原来的投影矩阵。由于纹理已经准备好了，所以它和纹理层0相关联。<br>　　//render scene with texture<br>　　g_App.GetDevice()-&gt;SetRenderTarget(0,pBackBuffer); //set back buffer<br>　　g_App.GetDevice()-&gt;Clear(0,NULL,D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,D3DCOLOR_XRGB(0,0,0),1.0f,0);<br>　　g_App.GetDevice()-&gt;BeginScene();<br>　　g_App.GetDevice()-&gt;SetTexture(0,pRenderTexture); //set rendered texture<br>　　g_App.GetDevice()-&gt;SetTransform(D3DTS_WORLD,&amp;matTranslation);<br>　　g_App.GetDevice()-&gt;SetTransform(D3DTS_PROJECTION,&amp;matOldProjection); //restore projection matrix<br>　　g_App.GetDevice()-&gt;SetStreamSource(0,pQuadVB,0,sizeof(D3DVERTEX));<br>　　g_App.GetDevice()-&gt;DrawPrimitive(D3DPT_TRIANGLESTRIP,0,2);<br>　　g_App.GetDevice()-&gt;EndScene();<br>　　g_App.GetDevice()-&gt;Present(NULL,NULL,NULL,NULL);<br>　　最后我们通过调用Release()方法释放Surface对象。<br>　　pRenderSurface-&gt;Release();<br>　　pRenderSurface = NULL;<br>　　pBackBuffer-&gt;Release();<br>　　pBackBuffer = NULL;<br>　　渲染到纹理能让你做很多事情，但是你必须注意一些限制。首先深度缓冲区必须总是大于或等于渲染对象的大小。此外，渲染对象和深度缓冲区的格式必须一致。</p>
<img src ="http://www.cppblog.com/Leaf/aggbug/91094.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Leaf/" target="_blank">大渊献</a> 2009-07-24 23:38 <a href="http://www.cppblog.com/Leaf/archive/2009/07/24/91094.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于材质编辑器的结构</title><link>http://www.cppblog.com/Leaf/archive/2009/06/05/86846.html</link><dc:creator>大渊献</dc:creator><author>大渊献</author><pubDate>Fri, 05 Jun 2009 05:32:00 GMT</pubDate><guid>http://www.cppblog.com/Leaf/archive/2009/06/05/86846.html</guid><wfw:comment>http://www.cppblog.com/Leaf/comments/86846.html</wfw:comment><comments>http://www.cppblog.com/Leaf/archive/2009/06/05/86846.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/Leaf/comments/commentRss/86846.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Leaf/services/trackbacks/86846.html</trackback:ping><description><![CDATA[<p>一直不知道编辑器的结构是如何的。有没有哪位大大耐心讲解一下。或者给一个结构图也行<br>谢谢啦！<br><br>试着用DXUT写了一下，初步决定以下功能，由于不熟悉DXUT，捣鼓了半天才弄出来<br><br>1、读取一个模型文件<br>2、分析模型文件，取得其subset个数，取得每个subset的材质<br>3、根据选中的subset调整其材质<br>4、保存编辑好的材质到二进制文件中，后缀名暂定为 *.mtrl<br><br>效果图如下<br>至于调节材质的那里，还有一种方案是调用shoosecolor面板，那样比较直观，但操作复杂。不知道是slider好还是面板好<br>暂时用slider<br><img height=510 alt="" src="http://www.cppblog.com/images/cppblog_com/leaf/materialeditor.jpg" width=650 border=0><br><br>有谁能告诉我，下拉列表那里，怎么能让他向上跑吗？ 向下跑会遮住一些，不得不调整大小。</p>
<img src ="http://www.cppblog.com/Leaf/aggbug/86846.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Leaf/" target="_blank">大渊献</a> 2009-06-05 13:32 <a href="http://www.cppblog.com/Leaf/archive/2009/06/05/86846.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>终于使现了基于GPU计算的粒子效果 </title><link>http://www.cppblog.com/Leaf/archive/2009/05/20/83430.html</link><dc:creator>大渊献</dc:creator><author>大渊献</author><pubDate>Tue, 19 May 2009 16:46:00 GMT</pubDate><guid>http://www.cppblog.com/Leaf/archive/2009/05/20/83430.html</guid><wfw:comment>http://www.cppblog.com/Leaf/comments/83430.html</wfw:comment><comments>http://www.cppblog.com/Leaf/archive/2009/05/20/83430.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Leaf/comments/commentRss/83430.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Leaf/services/trackbacks/83430.html</trackback:ping><description><![CDATA[样子还不咋样。但还是实现了。。轨迹公式是小事了。呵呵。很高兴，一天时间没白忙<br><img height=309 alt="" src="http://www.cppblog.com/images/cppblog_com/leaf/gpuparticle.jpg" width=546 border=0><br><br>还是看看C++吧，为了可持续发展~~~ 
<img src ="http://www.cppblog.com/Leaf/aggbug/83430.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Leaf/" target="_blank">大渊献</a> 2009-05-20 00:46 <a href="http://www.cppblog.com/Leaf/archive/2009/05/20/83430.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>XFile的骨骼动画</title><link>http://www.cppblog.com/Leaf/archive/2009/05/17/83232.html</link><dc:creator>大渊献</dc:creator><author>大渊献</author><pubDate>Sun, 17 May 2009 15:52:00 GMT</pubDate><guid>http://www.cppblog.com/Leaf/archive/2009/05/17/83232.html</guid><wfw:comment>http://www.cppblog.com/Leaf/comments/83232.html</wfw:comment><comments>http://www.cppblog.com/Leaf/archive/2009/05/17/83232.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Leaf/comments/commentRss/83232.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Leaf/services/trackbacks/83232.html</trackback:ping><description><![CDATA[<p>读SDK中那SkinMesh的源代码真费尽，到目前还对一些细节不明了，但是总算是会简单地应用那个CSinMesh类了，并生让Tiny动了起来<br><br>Tiny很出名哦，是DX SDK的代言人。哈哈，看看他的照片吧`~<br><img border=0 alt="" src="http://www.cppblog.com/images/cppblog_com/leaf/ANIM3.jpg" width=528 height=444><br><img border=0 alt="" src="http://www.cppblog.com/images/cppblog_com/leaf/ANIM2.jpg" width=461 height=446><br><img border=0 alt="" src="http://www.cppblog.com/images/cppblog_com/leaf/ANIM1.jpg" width=561 height=442><br><br>哈哈，感觉还不错，增加了人物控制后，更安逸~~<br><br></p>
<img src ="http://www.cppblog.com/Leaf/aggbug/83232.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Leaf/" target="_blank">大渊献</a> 2009-05-17 23:52 <a href="http://www.cppblog.com/Leaf/archive/2009/05/17/83232.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ZBuffer与WBuffer</title><link>http://www.cppblog.com/Leaf/archive/2009/04/24/80969.html</link><dc:creator>大渊献</dc:creator><author>大渊献</author><pubDate>Fri, 24 Apr 2009 09:49:00 GMT</pubDate><guid>http://www.cppblog.com/Leaf/archive/2009/04/24/80969.html</guid><wfw:comment>http://www.cppblog.com/Leaf/comments/80969.html</wfw:comment><comments>http://www.cppblog.com/Leaf/archive/2009/04/24/80969.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/Leaf/comments/commentRss/80969.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Leaf/services/trackbacks/80969.html</trackback:ping><description><![CDATA[<p>Depth-Buffer（深度缓存）有两种：Z-Buffer 和 W-Buffer，这里讨论这两种深度缓存的区别，以及如何在两者之间转换。<br>　　w 的含义<br>　　3D空间点的坐标是（x，y，z），为了使矩阵乘法具有平移变换的功效，我们用4D空间中的点（x，y，z，w）来表示3D空间中的点（x'，y'，z'），这两个不同空间点之间的关系是：<br>　　<br>　　x' = x / w<br>　　y' = y / w<br>　　z' = z / w<br>　　<br>　　像这样用四维空间点表示三维空间点，或者说用 n + 1 维空间点表示 n 维空间点的方法叫做 &#8220;齐次坐标表示法&#8221;。<br>　　<br>　　实际使用中，在模型-&gt;世界转换、世界-&gt;视图转换过程中，w 通常保持不变，总是等于一，这样，齐次坐标的前三个分量就是对应3D空间点的三个坐标分量。但是，经过投影变换后，w 将得到一个比例值，比如，一般的透视投影变换矩阵是：<br>　　<br>　　| W　 0　 0　 0 |<br>　　| 0　 H　 0　 0 |<br>　　| 0　 0　 Q　 1 |<br>　　| 0　 0　-QZn 0 |<br>　　<br>　　其中　 Zn　=　近裁剪面 z 坐标<br>　　Zf　=　远裁剪面 z 坐标<br>　　W　=　2 * Zn / 视口宽度<br>　　H　=　2 * Zn / 视口高度<br>　　Q　=　Zf / (Zf - Zn)<br>　　<br>　　将点（x，y，z，1）乘以此矩阵，w 便不再是一，而对应的3D空间点坐标（x / w，y / w，z / w）将出现一个缩放效果。同时，因为 w 的值通常与 z 坐标成正比（比如经过上面这个矩阵的变换，w 的值其实就是 z 坐标的值），所以经过投影变换，物体会产生近大远小的效果。<br>　　<br>　　Z-Buffer 与 W-Buffer 的区别<br>　　简单的说，z-buffer 与 w-buffer 的区别就是前者保存的是点的 z 坐标，而后者保存的是点的 w 坐标。<br>　　<br>　　具体的说，两者因为保存的值有不同的含义，所以表现出来的实际效果也会有差别。<br>　　<br>　　z-buffer 保存的是经过投影变换后的 z 坐标，前面说过，投影后物体会产生近大远小的效果，所以距离眼睛比较近的地方，z 坐标的分辨率比较大，而远处的分辨率则比较小，换句话说，投影后的 z 坐标在其值域上，对于离开眼睛的物理距离变化来说，不是线性变化的（即非均匀分布），这样的一个好处是近处的物体得到了较高的深度分辨率，但是远处物体的深度判断可能会出错。<br>　　<br>　　w-buffer 保存的是经过投影变换后的 w 坐标，而 w 坐标通常跟世界坐标系中的 z 坐标成正比，所以变换到投影空间中之后，其值依然是线性分布的，这样无论远处还是近处的物体，都有相同的深度分辨率，这是它的优点，当然，缺点就是不能用较高的深度分辨率来表现近处的物体。<br>　　<br>　　从硬件实现角度来说，几乎所有的硬件3D加速卡都支持 z-buffer，而 w-buffer 的支持没有 z-buffer 那么广泛。另外，早期的 Direct3D 版本看起来也不支持 w-buffer。<br>　　<br>　　Z-Buffer 与 W-Buffer 之间的转换<br>　　根据上面的矩阵变换，可以很容易的导出将 w-buffer 转换成 z-buffer 的公式：<br>　　<br>　　zDepth = Q * ( wDepth - Zn ) / wDepth<br>　　= Zf / ( Zf - Zn ) * ( wDepth - Zn ) / wDepth<br>　　<br>　　这个转换公式有什么用处？举个例子：3DS MAX 使用的是 w-buffer，如果从 3DS MAX 中导出深度信息到 Direct3D 中，作为预渲染的背景使用，就有可能用到上面这个转换。当然，如果在 D3D 中使用 w-buffer，问题就不大了，但是如果使用 z-buffer，不经过这样的转换，渲染结果就会出错。</p>
<img src ="http://www.cppblog.com/Leaf/aggbug/80969.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Leaf/" target="_blank">大渊献</a> 2009-04-24 17:49 <a href="http://www.cppblog.com/Leaf/archive/2009/04/24/80969.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>通过高度图生成地形时候出现的失误画面</title><link>http://www.cppblog.com/Leaf/archive/2009/04/24/80968.html</link><dc:creator>大渊献</dc:creator><author>大渊献</author><pubDate>Fri, 24 Apr 2009 09:43:00 GMT</pubDate><guid>http://www.cppblog.com/Leaf/archive/2009/04/24/80968.html</guid><wfw:comment>http://www.cppblog.com/Leaf/comments/80968.html</wfw:comment><comments>http://www.cppblog.com/Leaf/archive/2009/04/24/80968.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Leaf/comments/commentRss/80968.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Leaf/services/trackbacks/80968.html</trackback:ping><description><![CDATA[很神奇呀，参数没设置对，于是出现了下面的&#8220;火星&#8221;地形。<br><img height=732 alt="" src="http://www.cppblog.com/images/cppblog_com/leaf/firestar.jpg" width=1015 border=0><br><br>还有一个带有金属感的&#8220;艺术品&#8221;<br><br><img height=727 alt="" src="http://www.cppblog.com/images/cppblog_com/leaf/especet.jpg" width=1018 border=0><br>真的好神奇~~<br><br>不过呢，我那地形生成还是没实现，很囧~~
<img src ="http://www.cppblog.com/Leaf/aggbug/80968.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Leaf/" target="_blank">大渊献</a> 2009-04-24 17:43 <a href="http://www.cppblog.com/Leaf/archive/2009/04/24/80968.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>第一次实现水面渲染（纹理动画方法）</title><link>http://www.cppblog.com/Leaf/archive/2009/04/22/80705.html</link><dc:creator>大渊献</dc:creator><author>大渊献</author><pubDate>Wed, 22 Apr 2009 04:26:00 GMT</pubDate><guid>http://www.cppblog.com/Leaf/archive/2009/04/22/80705.html</guid><wfw:comment>http://www.cppblog.com/Leaf/comments/80705.html</wfw:comment><comments>http://www.cppblog.com/Leaf/archive/2009/04/22/80705.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/Leaf/comments/commentRss/80705.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Leaf/services/trackbacks/80705.html</trackback:ping><description><![CDATA[找了很久的相关资料，关于水面渲染的还真不好找呢，突然发现了一个地方有介绍纹理动画，于是就试着做了做，效果还真是不错呢。<br><img style="WIDTH: 443px; HEIGHT: 377px" height=377 alt="" src="http://www.cppblog.com/images/cppblog_com/leaf/waterEffect.jpg" width=443 border=0><br>只是还没有实现倒影，下次再做。<br><br>虽然大虾很多，但像我这样自己捣鼓的也同样存在，那我就说说怎么实现的吧，也顺便理清自己的思路<br>
<div>我说说我的实现方法吧</div>
<div><br>std::vector&lt;IDirect3DTexture9 *&gt; vecTexture(0); //这个就是用来存储我们的几十张纹理。</div>
<div>先把他们全部加载进去。</div>
<div>然后我们先画一个矩形（略）</div>
<div>&nbsp;</div>
<div>然后我们根据下面这个来动态切换纹理</div>
<div>Direct3DTexture9* Texture = NULL;</div>
<div>float timeElapsed = 0;</div>
<div>DWORD dwFrameSpeed = 0;</div>
<div>timeElapsed += timeDelta*FrameSpeed;//timeDelta是两次渲染的间隔时间</div>
<div>&nbsp;&nbsp; if(timeElapsed&gt;vecTexture.size()) timeElapsed = 0;<br>&nbsp;&nbsp; Texture = vecTexture[(int)timeElapsed];</div>
<div>&nbsp;</div>
<div>接下来我们就可以设置纹理，然后渲染那个矩形就可以了。</div>
<div>如果要使水呈透明效果，只要和地形进行ALPHA混合就行了，混合参数自己多调两下。</div>
<br>
<img src ="http://www.cppblog.com/Leaf/aggbug/80705.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Leaf/" target="_blank">大渊献</a> 2009-04-22 12:26 <a href="http://www.cppblog.com/Leaf/archive/2009/04/22/80705.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>为DEMO加入雾化效果</title><link>http://www.cppblog.com/Leaf/archive/2009/04/20/80522.html</link><dc:creator>大渊献</dc:creator><author>大渊献</author><pubDate>Mon, 20 Apr 2009 03:58:00 GMT</pubDate><guid>http://www.cppblog.com/Leaf/archive/2009/04/20/80522.html</guid><wfw:comment>http://www.cppblog.com/Leaf/comments/80522.html</wfw:comment><comments>http://www.cppblog.com/Leaf/archive/2009/04/20/80522.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Leaf/comments/commentRss/80522.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Leaf/services/trackbacks/80522.html</trackback:ping><description><![CDATA[<p>雾化效果<br><img height=768 alt="" src="http://www.cppblog.com/images/cppblog_com/leaf/FogEffect.JPG" width=1024 border=0><br>首先来看看我们可以设置哪些参数<br>1、D3DFOGSTART、D3DFOGEND表示雾的起始和结束距离<br>2、D3DFOGDENSITY 雾的浓度<br>3、D3DFOGCOLOR 雾的颜色<br>4、D3DFOGTABLEMODE、D3DFOGVERTEXMODE 雾的模式，第一个为像素雾化，第二个为顶点雾化</p>
<p><br>首先看看雾化的方程</p>
<p>Color = f * Color(scene) + (1-f) * color(fog)</p>
<p>Color(scene):背景色<br>Color(fog): 雾色<br>f:雾化参数，随观察点的距离的增大而减小，从而可知最后得到的颜色，当观察点越远时，雾色占的比例越大。</p>
<p>雾有四种方式<br>D3DFOG_NONE 禁用雾化效果<br>D3DFOG_EXP&nbsp; 雾化效果随指数增加 f = 1/(e^density)<br>D3DFOG_EXP2 同上，不过公式变为 f = 1/(e^(density^2))<br>D3DFOG_LINEAR 线性雾 f = (end-d)/(end-start) d 为当前计算点与观察点距离</p>
<p>现在我们来看如何实现雾化<br>首先开启雾化效果<br>Device-&gt;SetRenderState(D3DRS_FOGENABLE,true);<br>然后我们要设置雾化的模式和公式<br>Device-&gt;SetRenderState(DEDRS_FOGTABLEMODE,D3DFOG_LINEAR);<br>这里我将其设置成了像素雾和线性，同样可以将其换成上面介绍的其它模式和公式<br>接下来我们就要设置雾化参数了<br>Device-&gt;SetRenderState(D3DRS_FOGCOLOR,oxffffffff);//设置成白色</p>
<p>设置start end<br>float start= 50,end = 400;<br>Device-&gt;SetRenderState(D3DRS_FOGSTART,*(DWORD*)&amp;start);<br>Device-&gt;SetRenderState(D3DRS_FOGEND,*(DWORD*)&amp;end);<br>设置浓度<br>float density = 0.001f;//0.0 -- 1.0<br>Device-&gt;SetRenderState(D3DRS_FOGDENSITY,*(DWORD*)&amp;density);</p>
<p>这样,我们的设置就完了,只要将雾化设置放入我们渲染场景中,就可以看到雾化效果了.<br>但应该注意以下几点<br>-----------------------------------------line<br>&nbsp;&nbsp; A&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; C<br>&nbsp;&nbsp;&nbsp; \&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; /<br>&nbsp;&nbsp;&nbsp;&nbsp; \&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; /<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; /<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; /<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \&nbsp;&nbsp;&nbsp; d&nbsp;&nbsp; &nbsp; /<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;/<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\ &nbsp; |&nbsp; /<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \&nbsp;&nbsp; /<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; P<br>------------------------------------------<br>雾化效果是以刚刚上面讲的 d 作为计算标准,所以,我们从P点看到A ,B ,C三点的雾化效果是一样的,而按常理,A ,C的雾应该更浓才对.<br>很显然,这让我们想到,D3D会有对应的处理办法.<br>D3D提供了基于发散的雾化效果,雾化随观察点的距离增大而增大,就像点光源,不过,这要求我们的硬件支持,所以在设置前应该检查<br>D3DCAPS9 caps;<br>Device-&gt;GetDeviceCaps(&amp;caps);</p>
<p>if(caps.RasterCaps&amp;D3DPRASTERCAPS_FOGRANGE)<br>&nbsp;Device-&gt;SetRenderState(D3DRS_RANGEFOGENABLE,true);</p>
<p>把这个增加到雾化设置中即可~~~</p>
<p>已经12点了,估计人有点晕,也讲不明白些什么东西了,到此为止</p>
<p>&nbsp;</p>
<p><br>&nbsp;</p>
<img src ="http://www.cppblog.com/Leaf/aggbug/80522.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Leaf/" target="_blank">大渊献</a> 2009-04-20 11:58 <a href="http://www.cppblog.com/Leaf/archive/2009/04/20/80522.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>透明纹理和平面阴隐DEMO</title><link>http://www.cppblog.com/Leaf/archive/2009/04/19/80444.html</link><dc:creator>大渊献</dc:creator><author>大渊献</author><pubDate>Sun, 19 Apr 2009 06:40:00 GMT</pubDate><guid>http://www.cppblog.com/Leaf/archive/2009/04/19/80444.html</guid><wfw:comment>http://www.cppblog.com/Leaf/comments/80444.html</wfw:comment><comments>http://www.cppblog.com/Leaf/archive/2009/04/19/80444.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Leaf/comments/commentRss/80444.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Leaf/services/trackbacks/80444.html</trackback:ping><description><![CDATA[<img height=566 alt="" src="http://www.cppblog.com/images/cppblog_com/leaf/shadow.jpg.jpg" width=749 border=0><br>加入了影子后的效果<br><br>渲染树目纹理之前，进行如下设置<br>Device-&gt;SetRenderState(D3DRS_ALPHABLENDENABLE,true);<br>Device-&gt;SetRenderState(D3DRS_SCRBLEND,D3DBLEND_SCRALPHA);<br>Device-&gt;SetRenderState(D3DRS_DESTBLEND,D3DBLEND_INVSCRALPHA);<br>Device-&gt;SetRenderState(D3DRS_ALPHATESTENABLE,true);<br>Device-&gt;SetRenderState(D3DRS_ALPHAREF,0x0);<br>Device-&gt;SetRenderState(D3DRS_ALPHAFUNC,D3DCMP_GREATEREQUAL);<br><br><br>渲染阴影的时候，使用的是同一纹理，但需进行如下设置<br>Device-&gt;SetRenderState(D3DRS_SCRBLEND,D3DBLEND_INVSCRALPHA);<br><br><br>调了一下午，才调出这阴影，难得呀~~`<br>
<img src ="http://www.cppblog.com/Leaf/aggbug/80444.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Leaf/" target="_blank">大渊献</a> 2009-04-19 14:40 <a href="http://www.cppblog.com/Leaf/archive/2009/04/19/80444.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>3D DEMO的初步实现</title><link>http://www.cppblog.com/Leaf/archive/2009/04/18/80354.html</link><dc:creator>大渊献</dc:creator><author>大渊献</author><pubDate>Sat, 18 Apr 2009 07:42:00 GMT</pubDate><guid>http://www.cppblog.com/Leaf/archive/2009/04/18/80354.html</guid><wfw:comment>http://www.cppblog.com/Leaf/comments/80354.html</wfw:comment><comments>http://www.cppblog.com/Leaf/archive/2009/04/18/80354.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Leaf/comments/commentRss/80354.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Leaf/services/trackbacks/80354.html</trackback:ping><description><![CDATA[<p><img height=462 alt="" src="http://www.cppblog.com/images/cppblog_com/leaf/FirstScreen.jpg" width=643 border=0><br>D3DMULTISAMPLE_0_SAMPLES</p>
<p>&nbsp;</p>
<p><img height=411 alt="" src="http://www.cppblog.com/images/cppblog_com/leaf/test2.jpg" width=563 border=0><br>D3DMULTISAMPLE_4_SAMPLES<br><br>终于成功地把天空盒，公告板，MESH，纹理等一起用在了一个工程中，哈哈，虽然比较白啦，但还是很有成就感。 </p>
<img src ="http://www.cppblog.com/Leaf/aggbug/80354.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Leaf/" target="_blank">大渊献</a> 2009-04-18 15:42 <a href="http://www.cppblog.com/Leaf/archive/2009/04/18/80354.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>平面阴隐（用模版缓存防止二次融合）</title><link>http://www.cppblog.com/Leaf/archive/2009/04/18/80325.html</link><dc:creator>大渊献</dc:creator><author>大渊献</author><pubDate>Sat, 18 Apr 2009 02:02:00 GMT</pubDate><guid>http://www.cppblog.com/Leaf/archive/2009/04/18/80325.html</guid><wfw:comment>http://www.cppblog.com/Leaf/comments/80325.html</wfw:comment><comments>http://www.cppblog.com/Leaf/archive/2009/04/18/80325.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Leaf/comments/commentRss/80325.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Leaf/services/trackbacks/80325.html</trackback:ping><description><![CDATA[平面阴隐其实就是将物体&#8220;压扁&#8221;到某一平面进行绘制。原理类似于投影<br>而对于一个三维物体来说，当多个部分投影到平面上时，会产生叠加效果，导致某一部位颜色较深，而此时我们可以通过模版缓存来防止二次融合，从而避免这类现象的产生。<br>Device-&gt;Clear(0,0,D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER|D3DCLEAR_STENCIL,0xfff0000,1.0,0);<br>Device-&gt;SetRenderState(D3DRS_STENCILENABLE,true);<br>Device-&gt;SetRenderState(D3DRS_STENCILFUNC,D3DCMP_EQUAL);<br>Device-&gt;SetRenderState(D3DRS_STENCILREF,0x0);<br>Device-&gt;SetRenderState(D3DRS_STENCILMASK,0xffffffff);<br>Device-&gt;SetRenderState(D3DRS_STENCILWRITEMASK,0xffffffff);<br>Device-&gt;SetRenderState(D3DRS_STENCILZFAIL,D3DSTENCILOP_KEEP);<br>Device-&gt;SetRenderState(D3DRS_STENCILFAIL,D3DSTENCILOP_KEEP);<br>Device-&gt;SetRenderState(D3DRS_STENCILPASS,D3DSTENCILOP_INCR);<br><br>首先开启模版,将测试规则设置为"相等",模版参考设置为0,失败时候不更改,成功的时候增加1.<br><br>由于我们已经将模版缓存清0 ,于是,当第一次写入模版的时候,测试总是成功的,模版值加1.而当第二次写入的时候,模版值与模版参考值不相等,测试便会失败,从而阻止了再次写入缓存.<br><br>Device-&gt;SetRenderState(D3DRS_ALPHABLENDENABLE,true);<br>Device-&gt;SetRenderState(D3DRS_SCRBLEND,D3DBLEND_SCRALPHA);<br>Device-&gt;SetRenderState(D3DRS_DESTBLEND,D3DBLEND_INVSRCAPHA);<br>Device-&gt;SetRenderState(D3DRS_ZENABLE,false);<br><br><br>//使用D3DXMatrixShadow(&amp;S,&amp;linghtDirection,&amp;groundplane);<br>//S为最后输出的矩阵,然后是光线方向,然后是要绘制阴影的平面<br><br>//绘制<br><br>//最后做收尾工作<br>Device-&gt;SetRenderState(D3DRS_ZENABLE,true);<br>Device-&gt;SetRenderState(D3DRS_ALPHABLENDENABLE,false);<br>Device-&gt;SetRenderState(D3DRS_STENCILENABLE,false);<br><br>&nbsp; 
<img src ="http://www.cppblog.com/Leaf/aggbug/80325.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Leaf/" target="_blank">大渊献</a> 2009-04-18 10:02 <a href="http://www.cppblog.com/Leaf/archive/2009/04/18/80325.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>从Xfile加载MESH模型</title><link>http://www.cppblog.com/Leaf/archive/2009/04/18/80326.html</link><dc:creator>大渊献</dc:creator><author>大渊献</author><pubDate>Sat, 18 Apr 2009 02:02:00 GMT</pubDate><guid>http://www.cppblog.com/Leaf/archive/2009/04/18/80326.html</guid><wfw:comment>http://www.cppblog.com/Leaf/comments/80326.html</wfw:comment><comments>http://www.cppblog.com/Leaf/archive/2009/04/18/80326.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Leaf/comments/commentRss/80326.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Leaf/services/trackbacks/80326.html</trackback:ping><description><![CDATA[<p>首先介绍ID3DXBuffer 接口<br>此类型有两个方法<br>LPVOID&nbsp;ID3DXBuffer::GetBufferPointer();//返回指向缓存中数据起始位置的指针<br>DWORD ID3DXBuffer::GetBufferSize()//返回缓存的大小,单位为字节<br><br>下面函数用于创建一个空的ID3DXBuffer对象<br><br>HRESULT D3DXCreateBuffer(DWORD NumBytes, LPD3DBUFFER *ppBuffer);</p>
<br>再来介绍一个D3DXMATRIAL结构<br>typedef struct D3DXMATERIAL<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;D3DMATERIAL9 Mat3D; //存储材质<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LPSTR pTextureFilename;//存储纹理路径名<br>}D3DXMATERIAL;<br><br>再来看看一个重要的函数<br>HRESULT D3DXLoadMeshFromX(<br>LPCSTR pFilename,//文件名<br>DWORD Options,//创建网格时所使用的标记<br>LPDIRECT3DDEVICE9 *pDevice,<br>LPD3DXBUFFER *ppAdjacency,//邻接表信息<br>LPD3DXBUFFER *ppMaterials,//材质和纹理信息. D3DXMATRIAL结构<br>LPD3DXBUFFER *ppEffectInstances,//<br>PDWORD pNumMaterials,//材质数目<br>LPD3DXMESH *ppMesh//返回填充好的Mesh对象<br>};<br><br>下面是一个实用的例子<br><br>class MyMesh<br>{<br>...........<br><br>private:<br>ID3DXMesh* Mesh = 0;<br>std::vector&lt;D3DMATERIAL9&gt; Mtrls(0);<br>std::vector&lt;IDirect3DTexture9*&gt; Textures(0);<br>......<br>};<br><br>bool MyMesh::LoadMyMesh( LPCSTR pName,IDirect3DDevice9* Device)<br>{<br>&nbsp;&nbsp; ID3DXBuffer* adjBuffer = 0;<br>&nbsp;&nbsp; ID3DXBuffer* mtrlBuffer = 0;<br>&nbsp;&nbsp; DWORD numMtrls = 0;<br>&nbsp;&nbsp; HRESULT hr&nbsp;= D3DXLoadMeshFromX(pName,D3DXMESH_MANAGED,Device,&amp;adjBuffer,&amp;mtrlBuffer,0,&amp;numMtrls,&amp;Mesh);<br>&nbsp;&nbsp; if(FAILED(hr))<br>&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp; ::MessageBox(NULL,"D3DXLoadFromX() - FAILED",0,0);<br>&nbsp;&nbsp;&nbsp; return false;<br>&nbsp;&nbsp; }<br>&nbsp;&nbsp; if(mtrlBuffer!=0&amp;&amp;numMtrls!=0) //如果有材质<br>&nbsp;&nbsp;&nbsp; {&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; D3DXMATERIAL* mtrls&nbsp; =<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (D3DXMATERIAL*)mtrlBuffer-&gt;GetBufferPointer();//GetBufferPointer() 为了适合各种类型,因为返回VOID*类型 需要强制转换<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for(int i = 0;i&lt;numMtrls;i++)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //得到的材质没有环境光,我们得自己加上,让它等于漫射光<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mtrls[i].MatD3D.Ambient = mtrls[i].MatD3D.Diffuse;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Mtrls.push_back(mtrls[i].MatD3D);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(mtrls[i].pTextureFilename!=0)//纹理不为空<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IDirect3DTexture9* tex = 0;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; D3DXCreateTextureFromFile(Device,mtrls.pTextureFilename,&amp;tex);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Textures.push_back(tex);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Textures.push_back(0);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;adjBuffer-&gt;Release();<br>&nbsp;mtrlBuffer-&gt;Release();<br>&nbsp;return true;<br>}<br><br>//加载好后,设置好矩阵,就可以进行绘制了.由于Mesh是分为许多子集的,所以要一个一个渲染<br>void MyMesh::DrawMyMesh(IDreict3DDevice9* Deivice)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp; for(int i =0;i&lt;Mtrls.size();i++)<br>&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Device-&gt;SetMaterial(&amp;Mtrls[i]);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Device-&gt;SetTexture(0,Textures[i]);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Mesh-&gt;DrawSubset(i);<br>&nbsp;&nbsp;&nbsp;&nbsp; }<br>}<br><br>
<img src ="http://www.cppblog.com/Leaf/aggbug/80326.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Leaf/" target="_blank">大渊献</a> 2009-04-18 10:02 <a href="http://www.cppblog.com/Leaf/archive/2009/04/18/80326.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用模版缓存实现镜面效果</title><link>http://www.cppblog.com/Leaf/archive/2009/04/18/80324.html</link><dc:creator>大渊献</dc:creator><author>大渊献</author><pubDate>Sat, 18 Apr 2009 02:01:00 GMT</pubDate><guid>http://www.cppblog.com/Leaf/archive/2009/04/18/80324.html</guid><wfw:comment>http://www.cppblog.com/Leaf/comments/80324.html</wfw:comment><comments>http://www.cppblog.com/Leaf/archive/2009/04/18/80324.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Leaf/comments/commentRss/80324.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Leaf/services/trackbacks/80324.html</trackback:ping><description><![CDATA[一直不知道那一堆长长的代码是什么意思，今天上课无聊的时候就在那里想，一不留神就想通了，真是谢天谢地！<br><br>首先将模版缓存清空<br>Device-&gt;Clear(&nbsp;&nbsp;&nbsp; 0,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER|D3DCLEAR_STENCIL, //清空模版缓存，深度缓存<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0ff000000,//颜色<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1.0f,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0)//清空后的模版缓存值<br><br>//接下来就是模版缓存进行设置<br>Device-&gt;SetRenderState(D3DRS_STENCILENABLE,true) //开启模版缓存<br>Device-&gt;SetRenderState(D3DRS_STENCILFUNC,D3DCMP_ALWAYS);//将模版测试设置为总是成功，因为我们是在画镜面，不管镜面如何，都要画上去<br>Device-&gt;SetRenderState(D3DRS_STENCILREF,0x1);//设置模版参考值为1，这样将会用0x1来标记镜面区域<br>Device-&gt;SetRenderState(D3DRS_STENCILMASK,0xffffffff);//设置模版掩码，0xffffffff表示不屏蔽任何位<br>Device-&gt;SetRenderState(D3DRS_STENCILWRITEMASK,0xffffffff)//模版写掩码<br>Device-&gt;SetRenderState(D3DRS_STENCILPASS,D3DSTENCILOP_REPLACE);//当模版测试成功时，便用模版参考值（0x1）去替换缓存中的值<br>Device-&gt;SetRenderState(D3DRS_ZWRITEENALBE,false);//关闭深处缓存的写功能，以便阻止对深缓存的更改<br><br>Device-&gt;SetRenderState(D3DRS_ALPHABLENDENABLE,true);//开启ALPHA混合功能<br>Device-&gt;SetRenderState(D3DRS_SRCBLEND,D3DBLEND_ZERO);//将源融合因子设置为（0，0，0，0）；<br>Device-&gt;SetRenderState(D3DRS_DESTBLEND,D3DBLEND_ONE);//将目标融合因子设置为(1,1,1,1);<br><br>//在这里画镜面,此时的镜面会通过模版缓存进行绘制,并且模版缓存中的代表镜面的部分被标记为0x1,而其它区域为0;<br><br>//接下来就要绘制我们的物体了<br>Device-&gt;SetRenderState(D3DRS_ZWRITEEABLE,true);//重新开启ZWRITE<br><br>Device-&gt;SetRenderState(D3DRS_STENCILFUNC,D3DCMP_EQUAL);//将模版测试规则设置为相等<br>Device-&gt;SetRenderState(D3DRS_STENCILZFAIL,D3DSTENCILOP_KEEP);<br>Device-&gt;SetRenderState(D3DRS_STENCILFAIL,D3DSTENCILOP_KEEP);//这两排表示如果深度和模版测试失败,则不对模版中的内容作更改<br>Device-&gt;SetRenderState(D3DRS_STENCILPASS,D3DSTENCILOP_KEEP);//若测试成功也不对其作更改<br><br><br>//使用D3DXMatrixReflect(&amp;R,&amp;plane);求出物体的镜像,其中plane为镜面平面;<br><br>//若此时绘画我们会看不到物体,因为物体的深度大于镜面的深度,于是我们要清空深度缓存<br><br>Device-&gt;Clear(0,0,D3DCLEAR_ZBUFFER,0,1.0f,0);<br><br>//为了能达到物体在镜子中的效果,我们依然要用到ALPHA混合<br><br>Device-&gt;SetRenderState(D3DRS_SRCBLEND,D3DBLEND_DESTCOLOR);//(Rd,Gd,Bd,Ad)<br>Device-&gt;SetRenderState(D3DRS_DESTBLEND,D3DBLEND_ZERO);//(0,0,0,0);<br>//由于物体在镜面中的显示为物体的像,于是我们要变改镜像绘制时的背面消隐模式<br>Device-&gt;SetRenderState(D3DRS_CULLMODE,D3DCULL_CW);//顺时针<br><br><br>最后的工作就是绘制出你的物体,然后关闭开启的功能,并恢复消隐模式<br>Device-&gt;SetRenderState(D3DRS_ALPHABLEND,false);<br>Device-&gt;SetRenderState(D3DRS_STENCILENABLE,false);<br>Device-&gt;SetRenderState(D3DRS_CULLMODE,CCW);//恢复默认(逆时针)<br>&nbsp;&nbsp; 
<img src ="http://www.cppblog.com/Leaf/aggbug/80324.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Leaf/" target="_blank">大渊献</a> 2009-04-18 10:01 <a href="http://www.cppblog.com/Leaf/archive/2009/04/18/80324.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>透明纹理</title><link>http://www.cppblog.com/Leaf/archive/2009/04/18/80322.html</link><dc:creator>大渊献</dc:creator><author>大渊献</author><pubDate>Sat, 18 Apr 2009 02:00:00 GMT</pubDate><guid>http://www.cppblog.com/Leaf/archive/2009/04/18/80322.html</guid><wfw:comment>http://www.cppblog.com/Leaf/comments/80322.html</wfw:comment><comments>http://www.cppblog.com/Leaf/archive/2009/04/18/80322.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Leaf/comments/commentRss/80322.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Leaf/services/trackbacks/80322.html</trackback:ping><description><![CDATA[<p>对于一些纹理，我们不要求全部显示出来，如用公告板显示的一棵树的纹理，此时就要求让背景不显示出来，只显示树的部分。<br>//首先图片的背景要处理成透明，显然我们看到是透明的了，但是对于计算机来说，同样会将透明背景后的物品遮住。<br>//此时就要在渲染纹理前对其进行ALPHA混合<br><br>/*</p>
<p><font color=#800000>alpha测试根据当前像素是否满足alpha测试条件（即是否达到一定的透明度）来控制是否绘制该像素，图形程序应用alpha测试可以有效地屏蔽某些像素颜色。与alpha混合相比，alpha测试不将当前像素的颜色与颜色缓冲区中像素的颜色混合，像素要么完全不透明，要么完全透明。由于无需进行颜色缓冲区的读操作和颜色混合，因此alpha测试在速度上要优于alpha混合。<br></font></p>
<p>比如一棵树，我们将它的背景ALPHA值设置为小于1。0，那么，我们可以将ALPHAREF 设置为1。0 即0x000000ff 然后ALPHAFUNC 设置为GREATEREQUAL （&gt;=） 所以，只有当ALPHA值大于等于1的部份被渲染，这样树的背景就不用画了！<br>*/<br><br>&nbsp;g_pMyd3dDevice-&gt;SetRenderState( D3DRS_ALPHABLENDENABLE,&nbsp;&nbsp; TRUE );//开启ALPHA混合功能<br>&nbsp;g_pMyd3dDevice-&gt;SetRenderState( D3DRS_SRCBLEND,&nbsp; D3DBLEND_SRCALPHA);//设置源混合因子为（As,As,As,As)<br>&nbsp;g_pMyd3dDevice-&gt;SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);//设置目标混合因子为（1-As,1-As,1-As,1-As);</p>
<p>&nbsp;g_pMyd3dDevice-&gt;SetRenderState( D3DRS_ALPHATESTENABLE, TRUE );//开启ALPHA测试功能<br>&nbsp;g_pMyd3dDevice-&gt;SetRenderState( D3DRS_ALPHAREF,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x0f );//设置ALPHA测试参考值<br>&nbsp;g_pMyd3dDevice-&gt;SetRenderState( D3DRS_ALPHAFUNC,D3DCMP_GREATEREQUAL );//设置APLHA测试比较规则<br><br><br>//在此处加载纹理和渲染<br><br><br>在渲染完毕后，不要忘了关闭开启的功能<br><br>&nbsp;g_pMyd3dDevice-&gt;SetRenderState( D3DRS_ALPHATESTENABLE, FALSE );<br>&nbsp;g_pMyd3dDevice-&gt;SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE );</p>
&nbsp;&nbsp; 
<img src ="http://www.cppblog.com/Leaf/aggbug/80322.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Leaf/" target="_blank">大渊献</a> 2009-04-18 10:00 <a href="http://www.cppblog.com/Leaf/archive/2009/04/18/80322.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>公告板</title><link>http://www.cppblog.com/Leaf/archive/2009/04/18/80321.html</link><dc:creator>大渊献</dc:creator><author>大渊献</author><pubDate>Sat, 18 Apr 2009 01:59:00 GMT</pubDate><guid>http://www.cppblog.com/Leaf/archive/2009/04/18/80321.html</guid><wfw:comment>http://www.cppblog.com/Leaf/comments/80321.html</wfw:comment><comments>http://www.cppblog.com/Leaf/archive/2009/04/18/80321.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/Leaf/comments/commentRss/80321.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Leaf/services/trackbacks/80321.html</trackback:ping><description><![CDATA[<p>开始只是知道公告板就是不管摄相机怎么转，对象总是在摄相机前面。这是公告板中的一种，也是最常见的一种<br>如游戏中人物、NPC的名字等，就是用贴图技术，然后再用公告板，这样不管玩家怎么转动视角，总是能看见名字正对着自己，<br>终于自己实现了一回公告板函数<br><br>void Billboard(IDirect3DDevice9* Device,D3DXMATRIX &amp;matInput,D3DXMATRIX &amp;matOutput)<br>{<br>&nbsp;&nbsp;&nbsp;//=========================<br>&nbsp;&nbsp;&nbsp;//公告板技术<br>&nbsp;&nbsp;&nbsp;//==========================<br>&nbsp;&nbsp;&nbsp;D3DXMATRIX matBillboard,matView;<br>&nbsp;&nbsp;&nbsp;D3DXMatrixIdentity(&amp;matBillboard);//初始化为单位矩阵<br>&nbsp;&nbsp;&nbsp;Device-&gt;GetTransform(D3DTS_VIEW,&amp;matView);//取得观察矩阵<br>&nbsp;&nbsp;&nbsp;matBillboard._11 = matView._11;//赋值<br>&nbsp;&nbsp;&nbsp;matBillboard._13 = matView._13;<br>&nbsp;&nbsp;&nbsp;matBillboard._31 = matView._31;<br>&nbsp;&nbsp;&nbsp;matBillboard._33 = matView._33;</p>
<p>&nbsp;&nbsp;&nbsp;D3DXMatrixInverse(&amp;matBillboard,NULL,&amp;matBillboard);//求其逆矩阵<br>&nbsp;&nbsp;&nbsp;matOutput = matBillboard * matInput;<br>&nbsp;&nbsp;&nbsp;//公告板结束<br>}<br><br>函数说明：<br>返回值：void<br>Device 是一个IDirect3DDevice9* 类型的参数<br>&amp;matInput 是一个D3DXMATRIX 类型的参数 <br>&amp;matOutput 是一个D3DXMATRIX 类型的参数<br><br>功能，将传入的matInput 矩阵，与摄相机矩阵的Look方向矩阵相乘，得到matOutput<br><br>用法<br>D3DXMATRIX matWorld;<br>D3DXMatrixIdentity(&amp;matWorld);<br><br>D3DXMatrixTranslation(&amp;matWorld,1,1,1); //对matWorld 进行必要的变换 如translation ，rotation 之类的。<br><br>Billboard(g_pDevice,matWorld,matWorld);<br><br>g_pDevice-&gt;SetTransform(D3DTS_WORLD,&amp;matWorld);<br><br>接下来就可以进行材料，纹理设置、绘制等工作了！&nbsp;&nbsp;&nbsp; <br>虽然是很简单的技术，但却很实用。</p>
&nbsp;&nbsp;&nbsp;&nbsp; 
<img src ="http://www.cppblog.com/Leaf/aggbug/80321.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Leaf/" target="_blank">大渊献</a> 2009-04-18 09:59 <a href="http://www.cppblog.com/Leaf/archive/2009/04/18/80321.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>D3D API调用消耗表</title><link>http://www.cppblog.com/Leaf/archive/2009/04/17/80229.html</link><dc:creator>大渊献</dc:creator><author>大渊献</author><pubDate>Fri, 17 Apr 2009 05:21:00 GMT</pubDate><guid>http://www.cppblog.com/Leaf/archive/2009/04/17/80229.html</guid><wfw:comment>http://www.cppblog.com/Leaf/comments/80229.html</wfw:comment><comments>http://www.cppblog.com/Leaf/archive/2009/04/17/80229.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.cppblog.com/Leaf/comments/commentRss/80229.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Leaf/services/trackbacks/80229.html</trackback:ping><description><![CDATA[<table>
    <tbody>
        <tr>
            <th>API Call</th>
            <th>Average number of Cycles</th>
        </tr>
        <tr>
            <td>SetVertexDeclaration</td>
            <td>6500 - 11250</td>
        </tr>
        <tr>
            <td>SetFVF</td>
            <td>6400 - 11200</td>
        </tr>
        <tr>
            <td>SetVertexShader</td>
            <td>3000 - 12100</td>
        </tr>
        <tr>
            <td>SetPixelShader</td>
            <td>6300 - 7000</td>
        </tr>
        <tr>
            <td>SPECULARENABLE</td>
            <td>1900 - 11200</td>
        </tr>
        <tr>
            <td>SetRenderTarget</td>
            <td>6000 - 6250</td>
        </tr>
        <tr>
            <td>SetPixelShaderConstant (1 Constant)</td>
            <td>1500 - 9000</td>
        </tr>
        <tr>
            <td>NORMALIZENORMALS</td>
            <td>2200 - 8100</td>
        </tr>
        <tr>
            <td>LightEnable</td>
            <td>1300 - 9000</td>
        </tr>
        <tr>
            <td>SetStreamSource</td>
            <td>3700 - 5800</td>
        </tr>
        <tr>
            <td>LIGHTING</td>
            <td>1700 - 7500</td>
        </tr>
        <tr>
            <td>DIFFUSEMATERIALSOURCE</td>
            <td>900 - 8300</td>
        </tr>
        <tr>
            <td>AMBIENTMATERIALSOURCE</td>
            <td>900 - 8200</td>
        </tr>
        <tr>
            <td>COLORVERTEX</td>
            <td>800 - 7800</td>
        </tr>
        <tr>
            <td>SetLight</td>
            <td>2200 - 5100</td>
        </tr>
        <tr>
            <td>SetTransform</td>
            <td>3200 - 3750</td>
        </tr>
        <tr>
            <td>SetIndices</td>
            <td>900 - 5600</td>
        </tr>
        <tr>
            <td>AMBIENT</td>
            <td>1150 - 4800</td>
        </tr>
        <tr>
            <td><font style="BACKGROUND-COLOR: #316ac5" color=#ffffff>&nbsp;SetTexture</font></td>
            <td>2500 - 3100</td>
        </tr>
        <tr>
            <td>SPECULARMATERIALSOURCE</td>
            <td>900 - 4600</td>
        </tr>
        <tr>
            <td>EMISSIVEMATERIALSOURCE</td>
            <td>900 - 4500</td>
        </tr>
        <tr>
            <td>SetMaterial</td>
            <td>1000 - 3700</td>
        </tr>
        <tr>
            <td>ZENABLE</td>
            <td>700 - 3900</td>
        </tr>
        <tr>
            <td>WRAP0</td>
            <td>1600 - 2700</td>
        </tr>
        <tr>
            <td>MINFILTER</td>
            <td>1700 - 2500</td>
        </tr>
        <tr>
            <td>MAGFILTER</td>
            <td>1700 - 2400</td>
        </tr>
        <tr>
            <td>SetVertexShaderConstant (1 Constant)</td>
            <td>1000 - 2700</td>
        </tr>
        <tr>
            <td>COLOROP</td>
            <td>1500 - 2100</td>
        </tr>
        <tr>
            <td>COLORARG2</td>
            <td>1300 - 2000</td>
        </tr>
        <tr>
            <td>COLORARG1</td>
            <td>1300 - 1980</td>
        </tr>
        <tr>
            <td>CULLMODE</td>
            <td>500 - 2570</td>
        </tr>
        <tr>
            <td>CLIPPING</td>
            <td>500 - 2550</td>
        </tr>
        <tr>
            <td>DrawIndexedPrimitive</td>
            <td>1200 - 1400</td>
        </tr>
        <tr>
            <td>ADDRESSV</td>
            <td>1090 - 1500</td>
        </tr>
        <tr>
            <td>ADDRESSU</td>
            <td>1070 - 1500</td>
        </tr>
        <tr>
            <td>DrawPrimitive</td>
            <td>1050 - 1150</td>
        </tr>
        <tr>
            <td>SRGBTEXTURE</td>
            <td>150 - 1500</td>
        </tr>
        <tr>
            <td>STENCILMASK</td>
            <td>570 - 700</td>
        </tr>
        <tr>
            <td>STENCILZFAIL</td>
            <td>500 - 800</td>
        </tr>
        <tr>
            <td>STENCILREF</td>
            <td>550 - 700</td>
        </tr>
        <tr>
            <td>ALPHABLENDENABLE</td>
            <td>550 - 700</td>
        </tr>
        <tr>
            <td>STENCILFUNC</td>
            <td>560 - 680</td>
        </tr>
        <tr>
            <td>STENCILWRITEMASK</td>
            <td>520 - 700</td>
        </tr>
        <tr>
            <td>STENCILFAIL</td>
            <td>500 - 750</td>
        </tr>
        <tr>
            <td>ZFUNC</td>
            <td>510 - 700</td>
        </tr>
        <tr>
            <td>ZWRITEENABLE</td>
            <td>520 - 680</td>
        </tr>
        <tr>
            <td>STENCILENABLE</td>
            <td>540 - 650</td>
        </tr>
        <tr>
            <td>STENCILPASS</td>
            <td>560 - 630</td>
        </tr>
        <tr>
            <td>SRCBLEND</td>
            <td>500 - 685</td>
        </tr>
        <tr>
            <td>Two_Sided_StencilMODE</td>
            <td>450 - 590</td>
        </tr>
        <tr>
            <td>ALPHATESTENABLE</td>
            <td>470 - 525</td>
        </tr>
        <tr>
            <td>ALPHAREF</td>
            <td>460 - 530</td>
        </tr>
        <tr>
            <td>ALPHAFUNC</td>
            <td>450 - 540</td>
        </tr>
        <tr>
            <td>DESTBLEND</td>
            <td>475 - 510</td>
        </tr>
        <tr>
            <td>COLORWRITEENABLE</td>
            <td>465 - 515</td>
        </tr>
        <tr>
            <td>CCW_STENCILFAIL</td>
            <td>340 - 560</td>
        </tr>
        <tr>
            <td>CCW_STENCILPASS</td>
            <td>340 - 545</td>
        </tr>
        <tr>
            <td>CCW_STENCILZFAIL</td>
            <td>330 - 495</td>
        </tr>
        <tr>
            <td>SCISSORTESTENABLE</td>
            <td>375 - 440</td>
        </tr>
        <tr>
            <td>CCW_STENCILFUNC</td>
            <td>250 - 480</td>
        </tr>
        <tr>
            <td>SetScissorRect</td>
            <td>150 - 340</td>
        </tr>
    </tbody>
</table>
使用D3D，我们就得知道常用的API的消耗，才能够方便我们优化自己的渲染器。这里给出了常用API的消耗表，可以有一个直观的比较。<br>这个表也可以在D3D SDK文档的 Accurately Profiling Direct3D API Calls (Direct3D 9) 一文中找到
<p>&nbsp;</p>
<img src ="http://www.cppblog.com/Leaf/aggbug/80229.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Leaf/" target="_blank">大渊献</a> 2009-04-17 13:21 <a href="http://www.cppblog.com/Leaf/archive/2009/04/17/80229.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>