﻿<?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++博客-组件工厂-随笔分类-3D渲染技术</title><link>http://www.cppblog.com/flashboy/category/6415.html</link><description>------3D游戏研发</description><language>zh-cn</language><lastBuildDate>Thu, 15 Jan 2015 05:58:20 GMT</lastBuildDate><pubDate>Thu, 15 Jan 2015 05:58:20 GMT</pubDate><ttl>60</ttl><item><title>RedLight引擎原型初成</title><link>http://www.cppblog.com/flashboy/archive/2009/12/05/102609.html</link><dc:creator>RedLight</dc:creator><author>RedLight</author><pubDate>Sat, 05 Dec 2009 09:36:00 GMT</pubDate><guid>http://www.cppblog.com/flashboy/archive/2009/12/05/102609.html</guid><wfw:comment>http://www.cppblog.com/flashboy/comments/102609.html</wfw:comment><comments>http://www.cppblog.com/flashboy/archive/2009/12/05/102609.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flashboy/comments/commentRss/102609.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flashboy/services/trackbacks/102609.html</trackback:ping><description><![CDATA[一直都没勇气去写一个游戏引擎，也许是太懒了，也许是太大了。虽然写了些零零碎碎的Test Case! 但你知道的，这始终不成气候！<br>最近我被安排到一个小组中，做的项目是一个体感游戏，之前考虑过用ogre开源引擎来做，可惜我对这东西又不熟的，听人家说还要用第三方类库CEGUI, 还要配置一些东西，一听头都大了，光学习理想都用半个月了，我们的工程只给两个月的时间，唉呀！算了吧，自己不是还有些乱七八糟的代码可用吗？狠下决心，决定干它一把！哪怕辛苦一点，做下来一个游戏引擎以后就容易干事了。<br>经过和另外一个同事两个月的时间奋斗，一路中虽说遇到些困难，也常加班的，项目终于也做完了。<br><img src="http://www.cppblog.com/images/cppblog_com/flashboy/6771/he3.JPG" border=0><br><br><img src="http://www.cppblog.com/images/cppblog_com/flashboy/6771/he0.JPG" border=0><br><br><img src="http://www.cppblog.com/images/cppblog_com/flashboy/6771/he1.JPG" border=0><br>一个游戏引擎原型基本蛋生了，我们命名它为 RedLight，它基本实现了<br><br>(0) Win32程序渲染框架<br>(1) UI的基本消息交互流程，XML窗体配置管理，UI皮肤配置管理，基本的UI控件库，多分辩率无缝UI拼图<br>(2) 室外场景管理<br>(3) 摄相机路径摄像<br>(4) 3D Max8模型及骨骼动画导出插件<br>(5) 模型渲染, 关键帧动画及骨骼动画控制<br>(6) 基本的水面反射效果<br>(7) 简单的面粒子系统<br>(8) 声音控制接口<br><br>这是一个单机游戏引擎的原型，功能有限，但它总算不辱使命完成了一个项目了，以后再扩展使它日益强大吧！
<img src ="http://www.cppblog.com/flashboy/aggbug/102609.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flashboy/" target="_blank">RedLight</a> 2009-12-05 17:36 <a href="http://www.cppblog.com/flashboy/archive/2009/12/05/102609.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>BigWorld引擎初识大观</title><link>http://www.cppblog.com/flashboy/archive/2009/11/14/100914.html</link><dc:creator>RedLight</dc:creator><author>RedLight</author><pubDate>Sat, 14 Nov 2009 05:16:00 GMT</pubDate><guid>http://www.cppblog.com/flashboy/archive/2009/11/14/100914.html</guid><wfw:comment>http://www.cppblog.com/flashboy/comments/100914.html</wfw:comment><comments>http://www.cppblog.com/flashboy/archive/2009/11/14/100914.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/flashboy/comments/commentRss/100914.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flashboy/services/trackbacks/100914.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;&nbsp;<a href='http://www.cppblog.com/flashboy/archive/2009/11/14/100914.html'>阅读全文</a><img src ="http://www.cppblog.com/flashboy/aggbug/100914.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flashboy/" target="_blank">RedLight</a> 2009-11-14 13:16 <a href="http://www.cppblog.com/flashboy/archive/2009/11/14/100914.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>D3D与OpenGL常用API对译</title><link>http://www.cppblog.com/flashboy/archive/2009/11/14/100912.html</link><dc:creator>RedLight</dc:creator><author>RedLight</author><pubDate>Sat, 14 Nov 2009 04:49:00 GMT</pubDate><guid>http://www.cppblog.com/flashboy/archive/2009/11/14/100912.html</guid><wfw:comment>http://www.cppblog.com/flashboy/comments/100912.html</wfw:comment><comments>http://www.cppblog.com/flashboy/archive/2009/11/14/100912.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flashboy/comments/commentRss/100912.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flashboy/services/trackbacks/100912.html</trackback:ping><description><![CDATA[<p>作为一个3D程序员, 我用了OpenGL两年多, 最近在搞一个项目, 从OpenGL转到D3D, 虽然工程外在的框架都封装得不错, 但想完全地从OpenGL转换到D3D, 看起来还是有难度的, 花了我两个星期的时间, 我终于转换过来了。<br>D3D与OpenGL的几点比较明显不同的地方:<br>(一)、正交投影时：OpenGL以屏幕左上角为(0,0), 而D3D却以屏幕中心为(0,0)<br>(二)、OpenGL使用右手坐标系， 而D3D使用左手坐标系<br>(三)、OpenGL使用旋转操作等转入的角度参数是 角度, 而D3D是 弧度，所以注意要PI * Angle / 180<br><br>下面我把具体地API对照关系列出来(不是很全，以后添加中.......)<br><br><br>1. 坐标变换<br>&nbsp;pos = D3DXVECTOR3(0,2,-1.5);<br>&nbsp;at&nbsp; = D3DXVECTOR3(0,0,0);<br>&nbsp;up&nbsp; = D3DXVECTOR3(0,1,0);<br>&nbsp;D3DXMatrixLookAtLH(&amp;view,&amp;pos,&amp;at,&amp;up);<br>&nbsp;pd3dDevice-&gt;SetTransform(D3DTS_VIEW,&amp;view);</p>
<p>2. 绘制<br>&nbsp;pd3dDevice-&gt;SetRenderState(D3DRS_FILLMODE,D3DFILL_WIREFRAME);</p>
<p>&nbsp;&nbsp;&nbsp; DrawPrimitive()<br>&nbsp;DrawIndexedPrimitive()<br>&nbsp;<br>&nbsp;DrawPrimitiveUP()<br>&nbsp;DrawIndexedPrimitiveUP()<br>3. 颜色</p>
<p><br>4. 片段测试</p>
<p>&nbsp;(1) 深度测试<br>&nbsp;g_pDevice-&gt;SetRenderState(D3DRS_ZENABLE, TRUE);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//glEnable(GL_DEPTH_TEST);&nbsp;&nbsp; <br>&nbsp;g_pDevice-&gt;SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL);&nbsp;&nbsp;&nbsp;&nbsp;//glDepthFunc(GL_LEQUAL);<br>&nbsp;//--------------------------------------------------------------------------------------------------------<br>&nbsp;g_pDevice-&gt;SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//glEnable(GL_CULL_FACE);</p>
<p>&nbsp;<br>&nbsp;(2) Alpha测试<br>&nbsp;//--------------------------------------------------------------------------------------------------------&nbsp;<br>&nbsp;g_pDevice-&gt;SetRenderState(D3DRS_ALPHATESTENABLE, TRUE);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //glEnable(GL_ALPHA_TEST);<br>&nbsp;g_pDevice-&gt;SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATER);&nbsp;&nbsp;&nbsp;&nbsp;//glAlphaFunc(GL_GREATER, 0.1f);<br>&nbsp;g_pDevice-&gt;SetRenderState(D3DRS_ALPHAREF, 0.1 * 255); //取值范围 0 ~ 255<br>&nbsp;<br>&nbsp;(3) 剪裁测试 (平面剪切)<br>&nbsp;//--------------------------------------------------------------------------------------------------------<br>&nbsp;// Enable clip plane for reflection map<br>&nbsp;CMatrix44f pWorldViewProjIT=m_pWorldViewProj; <br>&nbsp;//pWorldViewProjIT.Transpose();<br>&nbsp;pWorldViewProjIT.Invert();&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;// Transform plane to clip-space<br>&nbsp;float pClipSpacePlane[4];<br>&nbsp;float pClipPlane[]= { 0, 0, 1, 0};&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;// Check if camera is below water surface, if so invert clip plane<br>&nbsp;CVector3f pEye=(CVector3f)m_pCamera.GetPosition();<br>&nbsp;if(-pEye.m_fZ&lt;0.0)<br>&nbsp;{<br>&nbsp;&nbsp;pClipPlane[2]=-pClipPlane[2];<br>&nbsp;}</p>
<p>&nbsp;MatrixTransformPlane(pClipSpacePlane, pClipPlane, pWorldViewProjIT);</p>
<p>&nbsp;// enable clip plane now<br>&nbsp;g_pDevice-&gt;SetClipPlane(0, pClipSpacePlane);&nbsp;&nbsp; <br>&nbsp;g_pDevice-&gt;SetRenderState(D3DRS_CLIPPLANEENABLE, 1);<br>&nbsp;<br>&nbsp; <br>&nbsp;<br>&nbsp;(4) 模板测试<br>&nbsp;//--------------------------------------------------------------------------------------------------------<br>&nbsp;g_pDevice-&gt;SetRenderState(D3DRS_STENCILENABLE, TRUE);<br>&nbsp;&nbsp;&nbsp; g_pDevice-&gt;SetRenderState(D3DRS_STENCILFUNC, 3DCMP_ALWAYS);<br>&nbsp;&nbsp;&nbsp; g_pDevice-&gt;SetRenderState(D3DRS_STENCILREF, 0x1); //取值范围 0 ~ 255<br>&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; Device-&gt;SetRenderState(D3DRS_STENCILPASS,&nbsp; D3DSTENCILOP_KEEP);<br>&nbsp;<br>5. 纹理操作<br>&nbsp;<br>&nbsp;g_pDevice-&gt;SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);<br>&nbsp; &nbsp;g_pDevice-&gt;SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);<br>&nbsp; &nbsp;g_pDevice-&gt;SetSamplerState( 0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);<br>&nbsp; &nbsp;<br>&nbsp;g_pDevice-&gt;SetSamplerState( 0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);<br>&nbsp;&nbsp;g_pDevice-&gt;SetSamplerState( 0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);</p>
<p>6. 缓冲区操作<br>&nbsp;&nbsp;<br>&nbsp;(1) 颜色缓冲<br>&nbsp;//--------------------------------------------------------------------------------------------------------<br>&nbsp;g_pDevice-&gt;SetRenderState(D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_ALPHA);<br>&nbsp;g_pDevice-&gt;SetRenderState(D3DRS_COLORWRITEENABLE, 0x000000F);<br>&nbsp;<br>&nbsp;(2) 深度缓冲<br>&nbsp;//--------------------------------------------------------------------------------------------------------<br>&nbsp;g_pDevice-&gt;SetRenderState(D3DRS_ZENABLE, TRUE);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//glEnable(GL_DEPTH_TEST);<br>&nbsp;g_pDevice-&gt;SetRenderState(D3DRS_ZWRITEENABLE, TRUE);&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//glDepthMask(GL_TRUE);</p>
<p>&nbsp;(3) 模板缓冲<br>&nbsp;//--------------------------------------------------------------------------------------------------------<br>&nbsp;<br>&nbsp;(4) 渲染到纹理<br>&nbsp;//--------------------------------------------------------------------------------------------------------<br>&nbsp;// Render targets<br>&nbsp;IDirect3DSurface9 *m_plD3DBackbufferSurf,<br>&nbsp;&nbsp;*m_plD3DDepthStencilSurfAA, <br>&nbsp;&nbsp;*m_plD3DDepthStencilSurf;</p>
<p>&nbsp;CRenderTarget *m_pRTRefraction, *m_pRTReflection; //(自定义纹理类)</p>
<p>&nbsp;//-----------------------------------------------------------------------------------</p>
<p>&nbsp;// Get backbuffer<br>&nbsp;g_pDevice-&gt;GetRenderTarget(0, &amp;m_plD3DBackbufferSurf);<br>&nbsp;<br>&nbsp;// Get depthstencil <br>&nbsp;g_pDevice-&gt;GetDepthStencilSurface(&amp;m_plD3DDepthStencilSurfAA);<br>&nbsp;</p>
<p>&nbsp;// Restore previous states<br>&nbsp;g_pDevice-&gt;SetRenderTarget(0, m_plD3DBackbufferSurf);<br>&nbsp;g_pDevice-&gt;SetDepthStencilSurface(m_plD3DDepthStencilSurfAA);<br>&nbsp;<br>&nbsp;// (1)折射图--------------------------------------------------------------------------<br>&nbsp;<br>&nbsp;//下面的语句调用了 g_pDevice-&gt;CreateRenderTarget(iWidth, iHeight, (D3DFORMAT) iFormat, (D3DMULTISAMPLE_TYPE)iAASamples, 0, 0, &amp;m_plD3Surf, 0));<br>&nbsp;if(FAILED(m_pRTRefraction-&gt;Create(m_fWidth&gt;&gt;1, m_fHeight&gt;&gt;1, D3DFMT_A8R8G8B8)))<br>&nbsp;{<br>&nbsp;&nbsp;return APP_ERR_INITFAIL;<br>&nbsp;}<br>&nbsp;<br>&nbsp;// Create depthstencil withouth multisampling<br>&nbsp;g_pDevice-&gt;CreateDepthStencilSurface(m_fWidth, m_fHeight, D3DFMT_D24X8, (D3DMULTISAMPLE_TYPE)0, 0, 0, &amp;m_plD3DDepthStencilSurf, 0);<br>&nbsp;<br>&nbsp;<br>&nbsp;g_pDevice-&gt;SetRenderTarget(0, m_pRTReflection-&gt;GetSurface());<br>&nbsp;<br>&nbsp;g_pDevice-&gt;StretchRect(m_plD3DBackbufferSurf, 0, m_pRTRefraction-&gt;GetSurface(), 0, D3DTEXF_NONE); <br>&nbsp;<br>&nbsp;// (2)反射图-----------------------------------------------------------------------------------<br>&nbsp;m_pRTReflection=new CRenderTarget;<br>&nbsp;if(FAILED(m_pRTReflection-&gt;Create(m_fWidth&gt;&gt;2, m_fHeight&gt;&gt;2, D3DFMT_A8R8G8B8))) <br>&nbsp;{<br>&nbsp;&nbsp;return APP_ERR_INITFAIL;<br>&nbsp;}<br>&nbsp;<br>&nbsp;g_pDevice-&gt;SetRenderTarget(0, m_pRTReflection-&gt;GetSurface());<br>&nbsp; <br>&nbsp;<br>&nbsp;//-----------------------------------------------------------------------------------<br>&nbsp;g_pDevice-&gt;SetRenderTarget(0, m_pRTReflection-&gt;GetSurface());<br>&nbsp;g_pDevice-&gt;SetDepthStencilSurface(m_plD3DDepthStencilSurf);<br>&nbsp;g_pDevice-&gt;Clear(0, 0, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_ARGB(255, 0, 0, 128), 1.0f, 0);&nbsp;&nbsp; <br>&nbsp;SetViewport(m_pRTReflection-&gt;GetWidth(), m_pRTReflection-&gt;GetHeight());<br>&nbsp;//-----------------------------------------------------------------------------------<br>&nbsp;<br>&nbsp;D3DXSaveTextureToFile("imageTex.jpg",D3DXIFF_JPG,(IDirect3DTexture9*)m_pWavesBump-&gt;GetTexture(),NULL); <br>&nbsp;<br>7. 混合操作<br>&nbsp;g_pDevice-&gt;SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); &nbsp;&nbsp;&nbsp;//glDisable(GL_BLEND);<br>&nbsp;g_pDevice-&gt;SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);&nbsp;&nbsp;//glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);<br>&nbsp;g_pDevice-&gt;SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);<br>&nbsp;</p>
<p>8. 灯光与材质<br>&nbsp;g_pDevice-&gt;SetRenderState(D3DRS_LIGHTING, FALSE);&nbsp;//glDisable(GL_LIGHTING);<br>&nbsp;<br>&nbsp;D3DMATERIAL9 mtrl;<br>&nbsp;mtrl.Ambient&nbsp; = a;<br>&nbsp;mtrl.Diffuse&nbsp; = d;<br>&nbsp;mtrl.Specular = s;<br>&nbsp;mtrl.Emissive = e;<br>&nbsp;mtrl.Power&nbsp;&nbsp;&nbsp; = p;<br>&nbsp;Device-&gt;SetMaterial(&amp;mtrl); &nbsp;//在设置纹理前设定<br>&nbsp;//设置当前使用的纹理<br>&nbsp;</p>
<img src ="http://www.cppblog.com/flashboy/aggbug/100912.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flashboy/" target="_blank">RedLight</a> 2009-11-14 12:49 <a href="http://www.cppblog.com/flashboy/archive/2009/11/14/100912.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>D3D常用API</title><link>http://www.cppblog.com/flashboy/archive/2009/09/17/96536.html</link><dc:creator>RedLight</dc:creator><author>RedLight</author><pubDate>Thu, 17 Sep 2009 07:55:00 GMT</pubDate><guid>http://www.cppblog.com/flashboy/archive/2009/09/17/96536.html</guid><wfw:comment>http://www.cppblog.com/flashboy/comments/96536.html</wfw:comment><comments>http://www.cppblog.com/flashboy/archive/2009/09/17/96536.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flashboy/comments/commentRss/96536.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flashboy/services/trackbacks/96536.html</trackback:ping><description><![CDATA[<h4 class=beTitle id=subjcns!F06D960017988A09!180>D3DAPI大全，全部函数</h4>
<div class=bvMsg id=msgcns!F06D960017988A09!180>
<p>//Direct3D 9.0 SDK 开发参考Direct3D 9.0 SDK 文档 (中文版)
<p>词汇表
<p>DirectX 8 教程
<p>你也可以把 COM 对象就想象成一套为某个主题而设计的一整套库函数。DX 就提供了一套完整的设计3D游戏的库。
<p><a href="http://baike.baidu.com/view/1169027.htm"><u><font color=#0000ff>http://baike.baidu.com/view/1169027.htm</font></u></a>
<p>使用DirectX的不同组件，你需要链接不同的静态库。例如你要使用DirectDraw组件，你就需要ddraw.lib。
<p>对于DirectDraw，这个头文件是ddraw.h。//com编程
<p>D3D.H
<p><a href="http://wowe1314.blog.163.com/blog/static/2358876200751191213583/"><u><font color=#0000ff>http://wowe1314.blog.163.com/blog/static/2358876200751191213583/</font></u></a>
<p>在Direct3D编程中，我们要做的工作基本上可以归纳为：
<p>调用适当的函数获取接口指针；
<p>调用接口的方法（成员函数）来完成所需功能；
<p>用完接口后，调用Release方法进行&#8220;释放&#8221;，注意释放顺序应该和获取它们的顺序相反。
<p><a href="http://www.lihuasoft.net/article/show.php?id=2928"><u><font color=#0000ff>http://www.lihuasoft.net/article/show.php?id=2928</font></u></a>
<p>Microsoft_DirectX_9.0c里的 9个DirectX的DLL
<p>DX9和DX10在渲染流水线上都是有天壤之别的，好在DX高版本开发包运行库中包含了对低版本开发包运行库的实现，所以用DX8开发的程序，DX9运行库也能够很好的支持，在安装有D9运行库的系统上跑DX8开发的程序不需要再安装DX8运行库，但是这个兼容性支持在最近被微软逐渐放弃，有时候DX9的不同更新版本做的程序也不能向下兼容，比如DX9FEB2007SDK,同DX9AUG2006SDK在shader编译规则上也是不同的，2007放弃了VS2.0和PS2.0以下版本shader的支持，同时对于HLSL中#include相对路径引用的默认根目录也是有区别的.openGL的shader扩展不同的厂商有不同的扩展开发包，但是这种情况随着GLSL和openGL2.0的出现有所改观.同时OpenGL是跨平台的而DX不是，这意味着用OpenGL和GNU&nbsp;&nbsp; C++规则开发的程序可以同时在Linux,unix和安装有GNU环境的Windows上同时运行。从效率上来看，DX由于数据时批量写入显存的，同OpenGL的单条函数写入来讲DX效率上要高一些，不过近来OpenGL也支持了批写入，只是支持批写入的OpenGL放弃了openGL一惯的优势也就是语言架构上的简洁使得函数的数目变得很冗杂。在效果上看DX9同支持GLSL或CG扩展的openGL可以实现相同的显示效果。但是有一点不同是DXUT和D3DX在一些基础绘制上比glu和openGL&nbsp;&nbsp; ARB&nbsp;&nbsp; Extend要差一点，比如绘制虚线，DX没有好的函数可以是实现这一功能。但是DX的扩展工具比openGL扩展工具又有多余的优势比如向量计算,GUI控件,mesh优化和曲面展开，PRT预计算等等和性能测试等等上又要强一点。DX10同OpenGL比较就感觉openGL不是同一个数量级上的产品，DX10在渲染流水线和架构上和能够实现的效果上要比DX9和openGL进步的多。要做面向未来的游戏产品尽量还是用DX10吧。
<p>&shy;
<p>LPDIRECT3D9 D3D主接口
<p>LPDIRECT3DDEVICE9 D3D硬件主接口
<p>LPDZRECT3DVERTXBUFFER9 顶点缓冲区接口
<p>LPD3DVIEWPORT9&nbsp; 视口接口
<p>LPD3DDISPLAYMODE D3D设备显示模式接口
<p>LPD3DPRESENT_PARAMETERS 创建结构用来保存设备的显示模式接口
<p>LPD3DXVECTOR3&nbsp; 3D向量接口
<p>LPDIRECT3DTEXTURE9 纹理接口
<p>ID3DXSprite&nbsp; 精灵接口
<p>g.pvb&nbsp; 成员函数
<p>g_pD3D&nbsp; 成员函数
<p>g_pD3D-&gt;GetAdapterDisplayMode(D3DADAPTER_DEFAULT,&amp;d3ddm) 获取显示模式
<p>g_pd3dDevice 成员函数
<p>g_pd3dDevice-&gt;SetRenderState（，BOOL） 是否开启灯光
<p>g_pd3dDevice-&gt;SetTransform( D3DTS_WORLD, &amp;(matWorld * matWorld_x));//将上面计算出来的旋转矩阵，设置为世界变换矩阵
<p>g_pd3dDevice-&gt;SetStreamSource( 0, g_pVB, 0, sizeof(CUSTOMVERTEX) );写入流
<p>g_pd3dDevice-&gt;SetFVF() 设置FVF
<p>g_pd3dDevice-&gt;DrawIndexedPrimitive( 画形状, 0, 0, 点个数, 0, 三角形个数 ); 画
<p>timeGetTime 当前系统的时间
<p>DIRECT3DTXTURE 文理接口
<p>BITMAPPEILEHEADER 文件头
<p>BITMAPINFOHEADER 信息头
<p>fread 读一个数据块
<p>biBitcout 每个象素占几个字节
<p>bicompression 是否被压缩
<p>fseek 跳，偏移指针
<p>greatetxture 创建一个空文理
<p>D3Dcaked_RECT 锁定结构体
<p>setTexturestagestata 设置文理操作
<p>CONSTD3DMATRIX*&nbsp; 进行变换的变换矩阵
<p>结构体
<p>D3DPRESENT_PARAMETERS 存储D3D设备信息
<p>D3DXMATRIX&nbsp; 修改矩阵
<p>数组
<p>CUSTOMVERTEX 设置顶点位置颜色信息
<p>&shy;
<p>矩阵函数
<p>D3DXMATRIX * D3DXMatrixIdentity（POut,pM） 单位矩阵
<p>D3DXMATRIX * D3DXMatrixTranspose（上）&nbsp; 矩阵转置
<p>D3DXMATRIX * D3DXMatrixInverse（上中间加个FLOAT） 逆矩阵
<p>D3DXMATRIX * D3DXMatrixTransformation（）
<p>D3DXMATRIX* D3DXMatrixTranslation（输出矩阵，X，Y，Z） 平移变换
<p>D3DXMATRIX * D3DXMatrixScaling（上） 缩放变换
<p>FLOAT D3DXPlaneDotCoord（pp,pv） 点和平面之见的关系
<p>D3DXPLANE * D3DXPlaneFromPointNormal（POUT，PPOINT，PNORMAL） 构造子
<p>D3DXPLANE * D3DXPlaneFromPoints(Pout,pv1,pv2,pv3) 通过点来描述平面
<p>D3DXPLANE * D3DPlaneNormalize(POUT,PP) 标准化一个平面
<p>D3DXPLANE * D3DXPlaneTransform(POUT,PP,PM) 平移平面
<p>D3DXM
<p>转换函数
<p>D3DXMATRIX* D3DXMatrixLookAtLH(输出用于视图转换的矩阵，摄象机的位置，摄象机面向的位置，摄象机的正方向)&nbsp; 视图转换的矩阵
<p>D3DXMATRIX* D3DXMatrixOrthoLH(输出用于正交投影的交换矩阵，取景宽，取景高，取景离摄象机的最近距离，取景离摄象机的最远距离) 正交投影变换矩阵
<p>D3DXMATRIX* D3DXMatrixPerspectiveFovLH(输出用于透视投影的交换矩阵，摄象机镜头的夹角Y，平截台体的纵横比，近平截面的距离，远平截面的距离) 透视投影的矩阵
<p>Direct3DCreate9(D3D版本) 创建D3D对象
<p>设备函数
<p>SetTransform(变换的类型，变换的变换矩阵) 设置左手或右手坐标
<p>SetViewport(视口指针) 设置远近距离
<p>GetClientRect(hWnd,*RECT) 获取窗口绘图区域
<p>memcpy（指针，数组，长度） 拷贝
<p>SetStreamSource(0,G.pvb接口指针，0,长度) 数据流
<p>GetAdapterDisplayMode(指定显示卡序列号，存储显示模式的指针) 获取显卡的模式
<p>HRESULT CreateDevice(显卡序列号，D3D设备类型，所属窗口句柄，D3D进行3D运算，存储D3D设备相关信息指针，返回D3D设备借口指针的地址) 创建设备借口
<p>HRESULT CreateVertexBuffer(顶点缓冲区大小（字节），顶点缓冲区属性，灵活顶点格式，顶点缓冲区内存位置，顶点缓冲区指针地址，保留参数通常为0) 创建顶点缓冲
<p>HRESULT CreateIndexBuffer(索引缓冲区大小（字节）,顶点缓冲区属性,FMT颜色,顶点缓冲区内存位置,索引缓冲区指针地址，保留参数通常为0)&nbsp;&nbsp; 创建索引缓冲
<p>HRESULT Lock(加锁内存起始地址，加锁内存大小，返回内存指针地址，加锁属性) 加缩内存
<p>HRESULT UnLock() 解锁
<p>HRESULT SetStreamSource(渲染数据流序列号，进行绑定连接的顶点缓冲区指针，进行绑定连接渲染数据流的起始位置，渲染数据流中一个顶点所占的内存大小) 顶点缓冲区和渲染数据流连接
<p>HRESULT SetFVF(灵活顶点格式) 设置顶点格式
<p>HRESULT DrawPrimitive(绘制的图元类型，绘制的开始顶点的索引值，绘制的图元数量)&nbsp; 画到后向缓冲区
<p>HRESULT DrawPrimitiveup() 可以直接画
<p>HRESULT Preesent(复制源的矩形区域指针，复制目的地的矩形区域指针，D3D设备窗口句柄，最小更新区域指针) 屏幕翻转
<p>HRESULT SetIndices(使用的索引缓冲区指针) 设置当前绘制的索引数组
<p>DrawIndexedPrimitive(图元类型，绘制到的索引缓冲区的开始地址，最小的索引数组元素的值，顶点的数目，开始的索引数组元素的值，绘制的数量) 同DrawPrimitive()
<p>&shy;
<p>绘制函数
<p>HRESULT DrawPrimitive(基本图元类型，起始顶点，绘制的图元的数量)&nbsp; 图元绘制
<p>HRESULT Clear(清楚的矩形区域数量，清除的举行区域数组指针，清楚哪个缓冲区，清除后重置的颜色，清除后重置的深度，0-1.0，重置的摸版值) 清空图形绘制区
<p>HRESULT BeginScene() 开始绘制
<p>HRESULT EndScene() 结束绘制
<p>&shy;
<p>纹理函数
<p>CreateTexture()&nbsp; 创建D3D纹理对象
<p>LoadBmpTeture（） 装载文理函数
<p>LoadBmpTexture24Bit (LPDIRECT3DDEVICE9 pDevice,LPCSTR&nbsp; pSrcFile,LPDIRECT3DTEXTURE9* ppTexture) 24位纹理
<p>D3DXCreateTextureFromFile(D3D设备指针，纹理图形文件，存储D3D文理的指针地址) 直接从磁盘获取纹理
<p>D3DXCreateTextureFromFileEx(D3D设备指针，纹理图形文件，指定纹理宽，高，指定渐进纹理序列级数，纹理使用方式一般为0，指定纹理图形格式，纹理存放的内存类型一般位为0，纹理过滤方式，自动生成的纹理序列过滤方式，设置透明色，图形文件信息存放地址可设置0，调色板存储地址，创建的D3D文理的指针地址) 高级获取纹理
<p>HRESULT SetTexture(多级纹理的索引0-7，D3D的纹理接口指针) 设置当前要渲染的纹理
<p>HRESULT SetTextureStageState(多级纹理的索引，纹理渲染状态的类型，纹理渲染状态的值，与类型相对应) 设置纹理的渲染状态
<p>HRESULT SetSamplerState(指定纹理属性0-7，纹理采样属性类型，设置纹理采样属性) 纹理采样
<p>HRESULT CheckDeviceFormat(指定显卡序列号,D3D设备类型,指定显示模式格式，缓冲区属性，需要使用查询的格式的设备类型,需要查询的显示格式) 纹理压缩
<p>HRESULT LockRect(指定加锁的纹理级别，指向D3DLOCKED_RECT结构，要加锁的RECT区域-0代表整个区域，加锁类型-取0或下表的值) 锁定纹理
<p>HRESULT UnlockRect(解锁的纹理级别) 解锁纹理
<p>向量函数
<p>D3DXVECTOR3 * D3DXVer3Length(V) 向量模的计算
<p>D3DXVECTOR3 * D3DXVec3Normalize（返回指针，V） 单位化
<p>D3DXVECTOR3 * D3DXVec3Add(返回的指针，u,v) 向量加法
<p>D3DXVECTOR3 * D3DXVec3Subtract（同上） 减法
<p>D3DXVECTOR3 * D3DXVec3Cross(同上) 向量X乘
<p>D3DXVECTOR3 * D3DXVec3Lerp(同上) 数乘
<p>D3DXVECTOR3 * D3DXVec3Maximize（同上） 取最大值
<p>D3DXVECTOR3 * D3DXVec3Minimize（同上） 取最小值
<p>D3DXVECTOR3 * D3DXVec3Scale（返回指针，PV，FLOAT） 比例
<p>FLOAT D3DXVec3Dot(pv1,pv2) 点乘
<p>参见编程精粹.chm中的COM中模块的导出函数
<p>Private Type D3DVECTOR
<p>&nbsp;&nbsp;&nbsp; x As Single
<p>&nbsp;&nbsp;&nbsp; y As Single
<p>&nbsp;&nbsp;&nbsp; z As Single
<p>End Type
<p>'返回3D向量的规格化向量
<p>Private Declare Function D3DXVec3Normalize Lib "DX8VB.DLL" Alias "VB_D3DXVec3Normalize" (VOut As D3DVECTOR, v As D3DVECTOR) As Long
<p>Private Declare Function D3DXVec3Add Lib "DX8VB.DLL" Alias "VB_D3DXVec3Add" (VOut As D3DVECTOR, v1 As D3DVECTOR, V2 As D3DVECTOR) As Long
<p>Private Declare Function D3DXVec3Subtract Lib "DX8VB.DLL" Alias "VB_D3DXVec3Subtract" (VOut As D3DVECTOR, v1 As D3DVECTOR, V2 As D3DVECTOR) As Long
<p>Private Declare Function D3DXVec3Length Lib "DX8VB.DLL" Alias "VB_D3DXVec3Length" (v As D3DVECTOR) As Single
<p>D3DFVF 自由顶点的格式
<p>D3DFVF_DIFFUSE 包含谩反射的信息
<p>D3DFVF_NORMAL 包含法线信息
<p>D3DFVF_PSIZE 顶点信息指明绘制点的大小
<p>D3DFVF_SPECULAR 包含镜面反射的信息
<p>D3DFVF_XYZ 包含未经转换的顶点坐标
<p>D3DFVF_XYZRHW 包含经过转换的顶点坐标
<p>D3DFVF_XYZB1 through D3DFVF_XYZB5 包含用于骨骼动化的顶点和顶点对骨骼的权重信息
<p>D3DFVF_XYZW 包含经过转换和裁剪的顶点坐标
<p>D3DTRANSFORMSTATETYPE 变换的类型
<p>&shy;
<p>D3DPRIMITIVETYPE 定义基本图元
<p>D3DPT_POINTLIST 一组点的集合
<p>D3DPT_LINELIST 一组线的集合
<p>D3DPT_LINESTRIP 首尾相连的线段的集合
<p>D3DPT_TRIANGLELIST 一组三角形的集合
<p>D3DPT_TRIANGLESTRIP 首尾相连的三角形，有两个顶点集合
<p>D3DPT_TRIANGLEFAN&nbsp;&nbsp; 组成扇形的一组三角形集合
<p>D3DPT_FORCE_DWORD 未定义的
<p>D3DDISPLAYMODE 屏幕显示模式
<p>D3DFMT_UNKNOWN&nbsp; 未知的象素格式
<p>D3DFMT_R8G8B8&nbsp; 24位色，RGB各占8位
<p>D3DFMT_A8R8G8B8&nbsp; 32位色，@RGB各占8位
<p>D3DFMT_X8R8G8B8&nbsp; 32位色，X为保留8位 RGB各占8位
<p>D3DFMT_R5G6B5&nbsp; 16位色，R占5，G占6，B占5位
<p>D3DFMT_X1R5G5B5&nbsp; 16位色，保留1位，RGB各占5位
<p>D3DFMT_A1R5G5B5&nbsp; 16位色，@占1位，RG5各占5位
<p>D3DFMT_A4R4G4B4&nbsp; 16位色，@RGB各占4位
<p>D3DFMT_R3G3B2&nbsp; 8位色，R3,G3,B2位
<p>D3DFMT_A8&nbsp; 只有8位@
<p>D3DFMT_A8R3G3B2&nbsp; 16位色，@8，R3，G3，B2位
<p>D3DFMT_X4R4G4B4&nbsp; 16位色
<p>D3DFMT_A2B10G10R10 32位色，@占2位，RGB各10位
<p>D3DFMT_A8B8G8R8&nbsp; 32位色
<p>D3DFMT_X8B8G8R8&nbsp; 32位色
<p>D3DFMT_G16R16&nbsp; 32位色，只有红和绿
<p>D3DFMT_A2R10G10B10 32位色
<p>D3DFMT_A16B16G16R16 64位色
<p>D3DFMT_A8P8&nbsp; 8位色，8位表示半透明，8位表示颜色
<p>D3DFMT_P8&nbsp; 8位色，用牙色索引值表示
<p>D3DFMT_L8&nbsp; 8位色，只表示亮度
<p>D3DFMT_L16&nbsp; 16位色，只表示亮度
<p>D3DFMT_A8L8&nbsp; 16位色，8位表示半透明，8位表示亮度
<p>D3DFMT_A4L4&nbsp; 8位色，4位表示半透明，4位表示亮度
<p>D3DDEVTYPE_HAL&nbsp;&nbsp; 硬件抽象层，通过显示硬件来完成图形渲染工作
<p>D3DDEVTYPE_NULLREF&nbsp;&nbsp;
<p>D3DDEVTYPE_REF&nbsp;&nbsp; 参考光栅器，一般用语测试显示卡不支持的D3D功能
<p>D3DDEVTYPE_SW&nbsp;&nbsp; 用语支持第三方的软件
<p>D3DDEVTYPE_FORCE_DWORD&nbsp; 扩展的
<p>D3DCREATE 3D运算的方式
<p>D3DCREATE_ADAPTERGROUP_DEVICE
<p>D3DCREATE_DISABLE_DRIVER_MANAGEMENT
<p>D3DCREATE_DISABLE_DRIVER_MANAGEMENT_EX
<p>D3DCREATE_FPU_PRESERVE&nbsp;&nbsp; 激活双精度浮点运算或浮点运算异常检测，设置该项会降低系统性能
<p>D3DCREATE_HARDWARE_VERTEXPROCESSING 由D3D硬件进行顶点预算
<p>D3DCREATE_MIXED_VERTEXPROCESSING 由混合方式进行顶点运算
<p>D3DCREATE_MULTITHREADED&nbsp;&nbsp; 支持多线程绘制，设置该项会降低系统性能
<p>D3DCREATE_NOWINDOWCHANGES&nbsp;&nbsp;
<p>D3DCREATE_PUREDEVICE&nbsp;&nbsp; 禁用D3D的GET*（）函数，禁止D3D使用虚拟设备模拟顶点运算
<p>D3DCREATE_SCREENSAVER&nbsp;&nbsp;
<p>D3DCREATE_SOFTWARE_VERTEXPROCESSING 由D3D软件进行顶点运算
<p>D3DSWAPEFFECT 取值列表
<p>D3DSWAPEFFECT_DISCARD&nbsp;&nbsp;&nbsp; 后台缓冲区复制到前台时，清除后台缓冲区内容
<p>D3DSWAPEFFECT_FLIP&nbsp;&nbsp;&nbsp; 后台缓冲区内容复制后，保持不变，有多个后台缓冲区时使用
<p>D3DSWAPEFFECT_COPY&nbsp;&nbsp;&nbsp; 后台缓冲区内容复制后，保持不变，只有1个后台缓冲区时使用
<p>D3DSWAPEFFECT_FORCE_DWORD&nbsp;&nbsp; 强迫该直作为32位存储，通常不用
<p>D3DPRESENT 屏幕反转模式列表
<p>D3DPRESENT_DONOTWAIT&nbsp;&nbsp;
<p>D3DPRESENT_INTERVAL_DEFAULT 默认的同ONE
<p>D3DPRESENT_INTERVAL_ONE&nbsp; 当屏幕刷新一次时前台后台进行交换
<p>D3DPRESENT_INTERVAL_TWO&nbsp; 当屏幕刷新二次时前台后台进行交换
<p>D3DPRESENT_INTERVAL_THREE 当屏幕刷新三次时前台后台进行交换
<p>D3DPRESENT_INTERVAL_FOUR 当屏幕刷新四次时前台后台进行交换
<p>D3DPRESENT_INTERVAL_IMMEDIATE 图形绘制完成时立即进行交换
<p>D3DPRESENT_LINEAR_CONTENT
<p>D3DUSAGE 缓冲区属性值列表
<p>D3DUSAGE_AUTOGENMIPMAP&nbsp;&nbsp;
<p>D3DUSAGE_DEPTHSTENCIL
<p>D3DUSAGE_DMAP&nbsp;&nbsp;
<p>D3DUSAGE_DONOTCLIP 禁用裁剪，表示顶点缓冲区中的顶点不进行裁剪，当设置该属性时，渲染状态D3DRS_CLIPPING必须设为FALSE
<p>D3DUSAGE_DYNAMIC 使用动态内存分配
<p>D3DUSAGE_NPATCHES 使用顶点缓冲区绘制N-patches曲线
<p>D3DUSAGE_POINTS&nbsp; 使用顶点缓冲区绘制点
<p>D3DUSAGE_RENDERTARGET
<p>D3DUSAGE_RTPATCHES 使用顶点缓冲区绘制曲线
<p>D3DUSAGE_SOFTWAREPROCESSING 使用软件进行顶点运算，否则使用硬件计算
<p>D3DUSAGE_WRITEONLY 只写属性，不能进行读操作，设置该属性可以提高系统性能
<p>D3DPOOL&nbsp; 缓冲区资源内存位置列表
<p>D3DPOOL_DEFAULT&nbsp; 默认的，顶点缓冲区尽可能存在与显存中
<p>D3DPOOL_MANAGED&nbsp; 由D3D自动调度顶点缓冲区内存位置（显存和内存）
<p>D3DPOOL_SCRATCH&nbsp; 顶点缓冲区位于计算机的临时内存中，这种类型的顶点缓冲区不能直接进行渲染，只能进行内存枷锁，拷贝等操作
<p>D3DPOOL_SYSTEMMEM 顶点缓冲区位于内存中
<p>D3DLOCK&nbsp; 缓冲区加锁
<p>D3DLOCK_DISCARD&nbsp; 更新整个缓冲区
<p>D3DLOCK_DONOTWAIT
<p>D3DLOCK_NO_DIRTY_UPDATE 在加锁的过程中系统进行其他操作（默认有Dirty标记)
<p>D3DLOCK_NOOVERWRITE 保证不腹稿缓冲区数据，设置该属性可以立即返回内存指针，提高系统性能
<p>D3DLOCK_NOSYSLOCK 在加锁的过程中系统可能执行其他操作
<p>D3DLOCK_READONLY 设置缓冲区位只读属性
<p>D3DXVECTOR3 向量算法
<p>D3DXVECTOR3u(x,y,z);
<p>D3DXVECTOR3v(x,y,z);
<p>float 变量=D3DXVec3Dot(u指针,v指针) 点乘
<p>D3DXMATRIX 矩阵
<p>D3DXMatrixIdentity 单位矩阵
<p>D3DXMatrixInverse 逆矩阵
<p>D3D实现图形变换
<p>D3DXMatrixTranslation 平移矩阵
<p>D3DXMatrixLockAtLH 观察矩阵
<p>D3DXMatrixIdentity&nbsp; 将一个矩阵单位化
<p>D3DXMatrixRotationY 绕Y轴转
<p>D3DXMatrixRotationX 绕X轴转
<p>D3DXMatrixRotationZ 绕Z轴转
<p>D3DXMatrixScaling 缩放变换
<p>D3DXMatrixMuLationAxis 围绕任意一个轴旋转
<p>D3DXMatrixMultiply 组合变换
<p>D3DUSAGE 纹理使用
<p>D3DUSAGE_AUTOGENMIPMAP 自动生成多级渐进纹理序列，该方式在资源处于D3DPOOL_SYSTEMMEM时无效
<p>D3DUSAGE_DEPTHSTENCIL 深度模版缓冲区，只在资源处于D3DPOOL_default时有效
<p>D3DUSAGE_DMAP&nbsp; 该纹理是一个置换纹理
<p>D3DUSAGE_DONOTCLIP
<p>D3DUSAGE_DYNAMIC
<p>D3DUSAGE_NPATCHES
<p>D3DUSAGE_POINTS
<p>D3DUSAGE_RENDERTARGET 该文理是一个渲染目标缓冲区
<p>D3DUSAGE_RTPATCHES
<p>D3DUSAGE_SOFTWAREPROCESSING 应用坐标变换
<p>D3DUSAGE_WRITEONLY
<p>D3DTEXTURESTAGESTATETYPE 渲染状态类型
<p>D3DTSS_COLOROP&nbsp;&nbsp; 1 文理层的颜色混合方式
<p>D3DTSS_COLORARG1&nbsp; 2 颜色混合的第一个参数
<p>D3DTSS_COLORARG2&nbsp; 3 颜色混合的第二个参数
<p>D3DTSS_ALPHAOP&nbsp;&nbsp; 4 指定纹理层的Alpha透明
<p>D3DTSS_ALPHAARG1&nbsp; 5 Alpha混合的第一个参数
<p>D3DTSS_ALPHAARG2&nbsp; 6 Alpha混合的第二个参数
<p>D3DTSS_BUMPENVMAT00&nbsp; 7 绘制凹凸纹理时
<p>D3DTSS_BUMPENVMAT01&nbsp; 8 绘制凹凸纹理时
<p>D3DTSS_BUMPENVMAT10&nbsp; 9 绘制凹凸纹理时
<p>D3DTSS_BUMPENVMAT11&nbsp; 10 绘制凹凸纹理时
<p>D3DTSS_TEXCOORDINDEX&nbsp; 11 该纹理层使用的纹理坐标的索引
<p>D3DTSS_BUMPENVLSCALE&nbsp; 22 绘制凹凸纹理的缩放参数
<p>D3DTSS_BUMPENVLOFFSET&nbsp;&nbsp; 23 绘制凹凸纹理的平移参数
<p>D3DTSS_TEXTURETRANSFORMFLAGS&nbsp; 24 控制纹理坐标的转换标志
<p>D3DTSS_COLORARG0&nbsp; 26 指定混合过程的第三个颜色
<p>D3DTSS_ALPHAARG0&nbsp; 27 Alpha混合的第三个参数
<p>D3DTSS_RESULTARG&nbsp; 28 颜色混合的结果输出寄存器
<p>D3DTSS_CONSTANT&nbsp; 32 颜色混合的常量寄存器
<p>D3DTSS_FORCE_DWORD&nbsp; 0x7fffffff 强制转换为32位，通常不用
<p>D3DSAMPLERSTATETYPE 纹理采样属性
<p>D3DSAMP_ADDRESSU&nbsp; 1 包装纹理
<p>D3DSAMP_ADDRESSV&nbsp; 2 包装纹理
<p>D3DSAMP_ADDRESSW&nbsp; 3 包装纹理
<p>D3DSAMP_BORDERCOLOR&nbsp; 4
<p>D3DSAMP_MAGFILTER&nbsp; 5 处理放大过滤
<p>D3DSAMP_MINFILTER&nbsp; 6 处理缩小过滤
<p>D3DSAMP_MIPFILTER&nbsp; 7 多纹理过滤
<p>D3DSAMP_MIPMAPLODBIAS&nbsp; 8 多级文理级数偏移值，初试直为0
<p>D3DSAMP_MAXMIPLEVEL&nbsp; 9 最大多纹理级别，初试值为0
<p>D3DSAMP_MAXANISOTROPY&nbsp; 10 各向异性，初试值为1
<p>D3DSAMP_SRGBTEXTURE&nbsp; 11
<p>D3DSAMP_ELEMENTINDEX&nbsp;&nbsp; 12
<p>D3DSAMP_DMAPOFFSET&nbsp; 13
<p>D3DSAMP_FORCE_DWORD&nbsp; 0x7fffffff 强制转换32位，通常不用
<p>纹理寻址
<p>D3DTADDRESS_WRAP&nbsp; 1 包装纹理寻址
<p>D3DTADDRESS_MIRROR&nbsp; 2 镜像纹理寻址
<p>D3DTADDRESS_CLAMP&nbsp; 3 夹取纹理寻址
<p>D3DTADDRESS_BORDER&nbsp; 4 边框颜色纹理寻址
<p>D3DTADDRESS_MIRRORONCE&nbsp; 5 一次镜像纹理寻址
<p>D3DTADDRESS_FORCE_DWORD 0x7fffffff强制转换32位，通常不用
<p>世界变换
<p>D3DTS_WORLD&nbsp; 世界变换</p>
</div>
<img src ="http://www.cppblog.com/flashboy/aggbug/96536.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flashboy/" target="_blank">RedLight</a> 2009-09-17 15:55 <a href="http://www.cppblog.com/flashboy/archive/2009/09/17/96536.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>D3d9的一些更新 (转)</title><link>http://www.cppblog.com/flashboy/archive/2009/09/12/96003.html</link><dc:creator>RedLight</dc:creator><author>RedLight</author><pubDate>Sat, 12 Sep 2009 07:40:00 GMT</pubDate><guid>http://www.cppblog.com/flashboy/archive/2009/09/12/96003.html</guid><wfw:comment>http://www.cppblog.com/flashboy/comments/96003.html</wfw:comment><comments>http://www.cppblog.com/flashboy/archive/2009/09/12/96003.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flashboy/comments/commentRss/96003.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flashboy/services/trackbacks/96003.html</trackback:ping><description><![CDATA[<div class=post>
<div class=postText>由于Aug 8造成的D3D9恐惧症已经完全消除了，这一章将会给大家介绍将3D引擎转向D3D9的各个方面，包括终于出现的全屏幕模式。从这章以后，我将使用D3D9作为讲解的语言继续D2D教程。<br><br>【OP结束，开始正片】<br><br>『Why？』<br><br>　　估计大家首先要问的就是&#8220;Why？&#8221;为什么要前进到D3D9？理由如下：<br>1、D3D9修复了D3D8已知的所有Bug，因此运行起来更稳定，速度也要快。<br>2、D3D9提供了许多便利的新功能，虽然绝大多数是面向3D的，但是也有不少2D适用的，比如IDirect3DDevice9::StretchRect，以及对IDirect3DSurface9的改进等等。D3DX库就更多了，比如D3DXSaveSurfaceToFileInMemory，一开始没发现这个函数有啥用处，现在基本离不开了。<br>3、HLSL。就像上一话我说的那样，D2D教程以后会有PixelShader的内容。我可不想拿汇编来写Shader，会死人的（祝贺我吧，终于抛弃汇编Shader了&#8230;&#8230;）。虽然说这不是决定性的理由，因为还有Cg什么的，不过我想编写显卡无关的代码，因此我不去研究Cg（反正和HLSL差不多）以及R2VB之类。<br>4、ID3DXFont，往下看你就知道了。<br><br>《D3D的变化》<br><br>『界面名称变化』<br><br>　　一句话：8改成9就行。<br><br>『&#8220;创建&#8221;型方法的一个统一变化』<br><br>　　许多Create*()方法，比如创建设备、创建纹理、创建顶点缓冲等等，多了一个HANDLE* pSharedHandle参数，无用，NULL之（看来微软原打算弄个共享句柄之类，不过被D3D10巨大的变化浮云了）<br><br>『创建D3D设备的变化』<br><br>　　D3DPRESENT_PARAMS的FullScreen_PresentationInterval变成了PresentationInterval，也就是说即使在窗口模式下也可以做到垂直同步来防止撕裂现象（2D的福音啊）。相应的，D3DSWAPEFFECT_COPY_VSYNC消失了，反正这个效果也不咋的，消失了也好。<br>　　要做到垂直同步需要给PresentationInterval赋值D3DPRESENT_INTERVAL_DEFAULT或D3DPRESENT_INTERVAL_ONE。其中D3DPRESENT_INTERVAL_ONE的效果比D3DPRESENT_INTERVAL_DEFAULT好一点，不过相应的也会占用多一点点系统资源&#8230;&#8230;真的只有一点点而已，实在是无所谓的&#8230;&#8230;<br>　　如果不要垂直同步，想要看看实际祯速的话，D3DPRESENT_INTERVAL_IMMEDIATE。<br>　　注意在窗口模式下，你只能使用这三种Present模式，全屏幕模式下就可以使用别的（但是要首先检测D3DCAPS9以查看显卡是否支持）。不过我感觉对99％的游戏来说，有这三个就足够了。<br>　　另外在窗口模式下，BackBufferFormat也可以设置成D3DFMT_UNKNOWN，D3D会自动获取当前桌面的格式设定成后备缓冲的格式，省去GetDisplayMode。实际上，窗口模式下的后备缓冲已经不需要和桌面格式相同，你可以通过IDirect3D9::CheckDeviceFormatConversion来检查，如果这个设备支持这两种颜色格式之间的转换，就可以给程序的后备缓冲设定上不同的格式。我试过在桌面格式为32Bit（D3DFMT_X8R8G8B8）时将程序的后备缓冲格式设置为D3DFMT_R5G6B5（16Bit），发现了速度提升，也就是说这个设定是有意义的。<br>　　可创建的设备类型多了一种D3DDEVTYPE_NULLREF，在安装了D3D SDK的机子上等同于D3DDEYTYPE_REF，在其他的机子上，这种设备实际上没有创建真正意义的D3D设备，只是允许你创建的纹理、表面等资源，但是Render、Present等操作都会无效（实际上这些资源都创建在了D3DPOOL_SCRATCH池里，不管你设定使用的是什么POOL）。也就是说，仅仅在模拟基本的运行而已。你可以用这个设备来编写一个利用D3DX函数库进行图像格式转换的程序，比如把一大堆不同的格式转换成易于D3D9使用的DDS格式。因为实际上没有创建设备，你甚至可以编写成控制台的，通过GetConsoleWindow的方法获得HWND。Mercury 3用的MIF格式的转换器就是这么做出来的。注意D3DDEVTYPE_NULLREF只能用在IDirect3D::CreateDevice时，其他的方法都不行。<br><br>『创建表面的变化』<br><br>　　创建表面（Surface）的方法变成了IDirect3DDevice9::CreateOffscreenPlainSurface，参数很简单不用多说，需要注意的是可以选择POOL了。<br><br>『设定FVF的变化』<br><br>　　设定FVF时，原来通过IDirect3DDevice8::<font size=+0>Set</font>VertexShader，现在有了一个专门用来设定FVF的方法：IDirect3DDevice9::<font size=+0>Set</font>FVF。这是个很好的变化，省得把FVF和Shader弄混（题外话：也就是因为这个变化，让Shader在设备Re<font size=+0>set</font>后得以保存，不错不错）<br><br>『获取后备缓冲』<br><br>　　D3D9现在允许有多个后备缓冲交换链，不过对于2D来说，基本不需要这种东西，IDirect3DDevice9::GetBackBuffer多出来的第一个参数赋值0即可。如果你有兴趣，可以去研究一下这个玩意，有时候可以用来做分场。<br><br>『<font size=+0>Set</font>StreamSource』<br><br>　　这个方法的功能被扩展了，对比参数就可以知道，多出来的Off<font size=+0>set</font>InBytes允许你选择一个顶点缓冲的Off<font size=+0>set</font>，D3D9将从这个Off<font size=+0>set</font>之后开始读取数据。因此你可以把几组用来渲染纹理的正方形顶点存储到一个顶点缓冲里面。<br><br>『<font size=+0>Set</font>SamplerState』<br><br>　　这个是D3D9的新方法，把原先<font size=+0>Set</font>TextureStageState的一些功能独立了出来，和2D关系最密切的就是纹理过滤了。原先的D3DTSS_MINFILTER变成了D3DSAMP_MINFILTER，相应的D3DTSS_MAGFILTER也变成D3DSAMP_MAGFILTER，D3DTSS_MAXANISOTROPY变成D3DSAMP_MAXANISOTROPY。另外还有更多的，比如纹理寻址等。你去看一下D3DSAMPLERSTATETYPE枚举类型的内容就知道它&#8220;迁移&#8221;了些什么。<br>　　这个变化对于Shader来说很方便。改成Sampler的东西在PixelShader过程也会有效，而没有更改的东西在PixelShader就不会有效了。D3D8时候把这些全都放在了一起，容易造成混乱。<br><br>『<font size=+0>Set</font>RenderTarget』<br><br>　　D3D9现在允许多重RenderTarget存在，不过我们基本上只用一个，RenderTargetIndex设为0，第二个参数仍然是需要设定的表面。与D3D8相同的是，在设定之前仍然需要先通过GetSurfaceLevel获得表面才行。<br><br>『顶点缓冲的锁定』<br><br>　　注意IDirect3DVertexBuffer9::Lock的第三个参数，从原来的BYTE**变成了void**。也就是这样了&#8230;&#8230;<br><br>『其他的一些变化』<br><br>1、CopyRects变成了UpdateSurface。和UpdateTexture一样，只能从D3DPOOL_SYSTEMMEM拷贝到D3DPOOL_DEFAULT<br>2、增加了一个比较有用的IDirect3DDevice9::ColorFill方法，作用是向D3DPOOL_DEFAULT的某个区域填充颜色，和Clear的功能类似，但是在使用目的上要比Clear明确的多，并且由于不牵扯深度缓冲之类，速度要快一些。<br>3、增加了一个IDirect3DDevice9::StretchRect方法，通过这个方法就可以在D3DPOOL_DEFAULT的表面或纹理之间进行带过滤器的缩放操作，免去利用Render的过程，非常有用。不过这个方法由于使用了硬件处理，限制较多，请大家仔细看SDK文档的Remarks部分。<br><br>《D3DX的变化》<br><br>　　D3DX的变化实际上相当的多，但正如我一开始所说，基本都是面向3D的。需要我们注意的有以下几种：<br>1、D3DX***FromFile之类的函数支持的图像格式增加了，不过所增加的都是很少见的格式。平时基本上还是用BMP、TGA和PNG就足够。<br>2、增加了D3DXSave***ToFileInMemory，将会把文件写入内存。这个函数的作用似乎不是很容易想到，但是如果你要写一个集成了转换、打包功能的工具，这个就很有用了，省去了通过临时文件操作造成的各种问题。另外如果你熟悉某种图形文件的格式的话，还可以通过直接访问这个文件获得RAW信息。注意，这类函数写入的是一个ID3DXBuffer，这个东西很简单，只有两个特定的方法，一看便懂，不再多言。<br>3、增加了一个ID3DXLine，可以方便你在2D上画线，创建ID3DXLine的方法是D3DXCreateLine。这个东西也不复杂，使用方法有点像ID3DXSprite，稍微研究一下就能弄懂，注意每次Draw的是D3DPT_LINESTRIP。用它比直接用顶点缓冲的好处是可以方便的打开反锯齿，效果嘛&#8230;&#8230;基本满意。<br>4、增加了一个ID3DXRenderToSurface，&#8220;理论上来说&#8221;方便了利用RenderTarget的过程&#8230;&#8230;不过我感觉反而弄得复杂了。创建的方法是D3DXCreateRenderToSurface，有心情的朋友自己研究看看吧，我就不讲了。<br><br>　　ID3DXSprite和ID3DXFont在Summer 2004的DX9 SDK（也就是第一版DX9.0c）开始发生了很大变化，下面详述：<br><br>『ID3DXSprite』<br><br>　　你会发现ID3DXSprite::DrawTransform不见了，取而代之的是其功能被整合到ID3DXSprite::<font size=+0>Set</font>Transform里面，也就是说为了缩放和旋转，我们不得不和矩阵打交道了。其实也不会太复杂，因为我们只是做一些矩阵运算，学过线性代数的朋友肯定会很熟悉，就算你不怎么熟悉线性代数，也没关系，D3DX函数库提供了现成的矩阵运算函数，你只要用就行了。<br><br>D3DXMatrixScaling<br>D3DXMatrixRotationZ<br>D3DXMatrixTranslation<br><br>　　按照顺序调用这三个函数&#8230;&#8230;或许学过3D的马上就想到这点了，的确是没错啦。注意顺序哦：Scaling -&gt; Rotation -&gt; Translation，简称SRT（看过全金属狂潮吗？看过的话这个单词很好记吧^_^），弄错了可是得不到正确结果的。<br>　　你是不是想到把同一个D3DXMATRIX当作参数使用三次？错啦！你要用矩阵乘法。创建三个D3DXMATRIX，比如mat1、mat2、mat3，分别用这三个函数将其创建为缩放矩阵、旋转矩阵和平移矩阵，然后在ID3DXSprite::<font size=+0>Set</font>Transform时，这样写：<br><br><font size=+0>Set</font>Transform(mat1 * mat2 * mat3);<br><br>　　有够麻烦的是不？ID3DXSprite方便了做3D的，可害苦了做2D的，所以我已经不直接用这个了（什么叫不直接用？往下看）。<br><br>『ID3DXFont』<br><br>　　大家来欢呼吧！Summer 2004改进的ID3DXFont彻底枪毙掉了上一话那个字体引擎&#8230;&#8230;<br>　　这东西的改进，怎么说呢，应该说是改头换面吧，速度、效果都和以前不是一个数量级。可怜的PixelFont，才存在了一话就要被抛弃了。<br>　　ID3DXFont多出来的几个方法，Preload*()这类的，就是把一些常用的字的字模提前读取到内存里面加快速度，同时还可以使用ID3DXSprite渲染，进一步加快速度。虽然内部仍然有GDI的部分，不过很明显工作方式发生了极大的变化。根据我的估计，这次的ID3DXFont很聪明的利用GDI获得文字的轮廓，然后通过纹理来渲染。这样的速度就快得多了，而且文字质量也得到了很好的控制，基本和直接用GDI的质量相同了。<br>　　由于PreloadCharacters()和PreloadGlyphs()不是那么好理解，一般用PreloadText()就行。建议将所有ASCII字符、标点符号和部分汉字预读进去。这个预读过程略微有点慢，而且根据预读的文字数量和你创建文字的字号，占用的内存也不同。这里给大家一堆文字，你Copy过去就行：<br><br>
<div class=quote>
<div class=quote-title>引用</div>
<div class=quote-content><br>const char strPreloadText[] = " 1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM~!@#$%^&amp;*()-=[]\\;',./_+{}|:\"&lt;&gt;?　、。&#183;ˉˇ&#168;〃—～‖&#8230;&#8216;&#8217;&#8220;&#8221;〔〕〈〉《》「」『』〖〗【】！＂＃￥％＆＇（）＊＋，－．／０１２３４５６７８９：；＜＝＞？＠ＡＢＣＤＥＦＧＨＩＪＫＬＭＮＯＰＱＲＳＴＵＶＷＸＹＺ［＼］＾＿｀ａｂｃｄｅｆｇｈｉｊｋｌｍｎｏｐｑｒｓｔｕｖｗｘｙｚ｛｜｝我人有的和主产不为这工要在第一上是中国经已发了民同";<br></div>
</div>
<br>　　注意第一个字符是空格哦！把空格预读进去可是很重要的^_^<br>　　看上去并不多，因为要考虑到内存占用及速度，我只预读了一些符号和五笔的一键字。这些字符在24号字时候已经占用了快1MB了，比起PixelFont字库占用的要大得多。天知道ID3DXFont到底预读了些什么&#8230;&#8230;<br>　　PreloadText()的第二个参数不要用strlen，sizeof(strPreloadText)即可。<br>　　然后就是利用ID3DXSprite来渲染。注意ID3DXFont::DrawText的第一个参数就是LPD3DXSPRITE，因此如果要利用ID3DXSprite，要将ID3DXFont::DrawText放到ID3DXSprite::Begin和ID3DXSprite::End之间。这就是我刚才说的不直接用ID3DXSprite的意思，ID3DXFont会完成ID3DXSprite的全部调用，你不用担心。<br>　　另外你应该注意到ID3DXSprite::Begin增加了参数，实际上DX文档里面没说，但是示例里面有，如果想让ID3DXSprite发挥作用并且最大幅度的提升效率，参数上设定D3DXSPRITE_ALPHABLEND | D3DXSPRITE_SORT_TEXTURE即可。意思很明白：打开Alpha过滤和纹理筛选。这里DX文档上有个错误一直没改：文档里给出的是D3DXSprite__SORT_TEXTURE，但是你可以试试，绝对报错。<br>　　剩下的就没啥了，ID3DXFont的使用方法上一话已经讲过。要注意的是D3DXCreateFont和D3DXCreateFontIndirect都发生了变化。D3DXCreateFont已经不再牵扯GDI了，D3DXCreateFontIndirect所使用的结构也变成了D3DXFONT_DESC，相对于LOGFONT结构，除去了一些用不着的参数，增加了一个MipLevels，就是MipMap等级啦，不用多说，2D下只用1。其他的上一话都有。实际上由于D3DXCreateFont已经不再关联GDI，D3DXCreateFontIndirect的存在仅仅是由于历史原因（为了兼容像我这种人的使用习惯），大家还是用D3DXCreateFont吧，省事。<br>　　截图就不贴了，没啥意义。你可能觉得直接向后备缓冲上DrawText还不够好看，那么就先画到一张纹理上，然后将纹理错位渲染到后备缓冲并且打开线型过滤，就可以达到和PixelFont相同的效果了。<br>　　速度嘛&#8230;&#8230;我画了整整一屏幕字，在不缓冲文字的情况下（这个&#8220;缓冲文字&#8221;和ID3DXFont的文字缓冲可不是一回事啊！看过上一话的都应该知道我这里指的是什么），速度仍然在120FPS以上。或许你会觉得速度还是有点慢，但是，如果用D3D8的ID3DXFont画上这么一屏幕，基本就只剩20FPS了。<br>　　使用ID3DXFont替换掉PixelFont的优势就是可以方便的自定义字体字号了，并且也不再受GB2312字库的限制。所以大家都换了吧&#8230;&#8230;都换了吧&#8230;&#8230;把PixelFont忘了吧&#8230;&#8230;<br><br>『稳定的DX9 SDK版本』<br><br>　　我现在用的是April 2006，而且应该会用很长时间。August 2006我是肯定不会去用啦！即使我不再恐惧D3D9，也会对这个SDK避让三分的。其实对于2D，我感觉用到April 2006就足够了，之后的DX9 SDK主要在D3DX的3D函数库部分进行更改&#8230;&#8230;其实也是秋后的蚂蚱蹦达不了几天，D3D10马上就要出来了。要说D3D10啊&#8230;&#8230;你还是看我另外一篇日志好了，总之打死我都不拿它做2D。<br><br>　　实际上仅仅是2D的话，从D3D8转向D3D9并没有多少变化，主要是稳定嘛！只要你不调用一些D3D9专用的功能，即使拿D3D9来做2D，在绝大多数显卡上还是能够运行的。嗯&#8230;&#8230;GF2等级以上吧，GF2之前的，也太老了，无视好了。<br><br>《再上点菜好了：全屏幕模式》<br><br>　　其实并不是多么复杂的问题，让我拖了这么久&#8230;&#8230;不拖了，这里就教给大家如何做全屏幕模式以及如何处理设备丢失的问题。<br><br>『创建全屏幕模式』<br><br>　　D3DPRESENT_PARAMS里面，Windowed设定为false，并且一定要设定BackBufferWidth和BackBufferHeight，完毕。<br>　　哈哈，就这么简单，或许早就有人尝试过了，但是你试试按下Alt+Tab，再切换回去，保证你什么都看不到。<br>　　之前曾经说过，DX8之前的版本，在全屏幕下工作比在窗口下容易，到DX8之后就则完全颠倒过来。因为在窗口模式下不用担心设备丢失（除非你更改桌面分辨率），全屏幕模式下就会有这个问题了。下面详述：<br><br>『设备、资源丢失』<br><br>　　设备丢失会发生在全屏幕模式下切换回桌面时（不论是通过Alt+Tab还是QQ上有人给你发了张图片-_-bbb），而且如果在调用IDirect3DDevice9::Re<font size=+0>set</font>（从现在开始就是D3D9了啊！忘记D3D8吧&#8230;&#8230;）的时候发生错误，设备也会丢失。<br>　　设备丢失会造成资源丢失：所有创建在D3DPOOL_DEFAULT池的资源都会丢失，需要重新创建，其内容当然也会消失，需要重写。<br>　　然而创建在D3DPOOL_SYSTEMMEM和D3DPOOL_SCRATCH池的资源不会受到影响。创建在D3DPOOL_MANAGED池的资源也不会丢失，而且在设备重新可用的时候，D3DPOOL_MANAGED池的资源也可以立即投入使用，内容也不会改变。看这个池名字：托管池就能知道，D3D帮你处理了所有问题。<br>　　因此避免设备丢失后资源丢失的简易方法就是将所有资源创建在D3DPOOL_MANAGED池内。不过这并不是个好方法，这意味着不能用渲染对象——记得吗？RenderTarget只能创建在D3DPOOL_DEFAULT。实际上最好的方法是跟踪所有D3DPOOL_DEFAULT资源，比如利用std::list，将所有D3DPOOL_DEFAULT资源勾住，在设备发生丢失的时候释放掉资源，设备可以继续使用的时候重新创建资源，记得把数据写回去。对于其他的池就不用这么折腾了。<br><br>『当设备丢失之后』<br><br>　　不论通过任何方式发生了设备丢失，所有的操作几乎都会失效，只有Release()可以用——其实D3D会保证有部分操作可以成功，但是也仅仅是&#8220;可以&#8221;成功而不是&#8220;一定&#8221;成功，所以你还不如认定丢失的时候全都会失败比较好——以及IDirect3DDevice9::TestCooperativeLevel。因此在设备丢失之后，你应该停止整个游戏循环，而通过反复调用IDirect3DDevice9::TestCooperativeLevel判断设备是否可用。<br><br>『IDirect3DDevice9::TestCooperativeLevel』<br><br>　　这个方法检测当前的设备状态。返回值有四种：D3D_OK一切正常，D3DERR_DEVICELOST设备丢失，D3DERR_DEVICENOTRE<font size=+0>SET</font>设备可以Re<font size=+0>set</font>。另外还有D3D9新增的D3DERR_DRIVERINTERNALERROR，遇到这个你就完蛋了，基本不可能恢复了，终止程序吧。<br>　　按照顺序来讲，如果游戏在正常运行，D3D_OK会返回；如果发生了设备丢失并且在这个时候不能恢复，比如全屏幕模式的时候用户切换到了Windows桌面，就会返回D3DERR_DEVICELOST；如果用户又切换回了游戏，设备可以恢复了（还没恢复呢！只是&#8220;可以&#8221;恢复而已），就会返回D3DERR_DEVICENOTRE<font size=+0>SET</font>。<br>　　另外，IDirect3DDevice9::Present也会返回类似的值，不过你最好别指望这个，老老实实的用TestCooperativeLevel。因为Present在设备可以恢复的时候还是返回D3DERR_DEVICELOST（外一句：D3D10的时候TestCooperativeLevel就会完全整合到Present里面了，可喜可贺可喜可贺）<br><br>『处理设备丢失』<br><br>　　看下面的伪代码：<br><br>switch (IDirect3DDevice9::TestCooperativeLevel()){<br>　　case D3D_OK:<br>　　　　GameLoop();<br>　　　　break;<br>　　case D3DERR_DEVICELOST:<br>　　　　break;<br>　　case D3DERR_DEVICENOTRE<font size=+0>SET</font><br>　　　　OnLostDevice();<br>　　　　IDirect3DDevice9::Re<font size=+0>set</font>();<br>　　　　OnRe<font size=+0>set</font>Device();<br>　　　　break;<br>　　default:<br>　　　　QuitGame();<br>　　　　break;<br>}<br><br>　　GameLoop()就是你的游戏运行的过程了。把这个switch写在我们游戏框架的GameMain()部分，具体的位置可以看任何一话附带的源代码。<br>　　好像我一直没有讲IDirect3DDevice9::Re<font size=+0>set</font>的参数啊？因为只有一个参数，就是指向D3DPRESENT_PARAMS的指针。把你第一次创建设备时使用的D3DPRESENT_PARAMS结构保存起来，供Re<font size=+0>set</font>来用。<br>　　OnLostDevice()就是Release掉所有D3DPOOL_DEFAULT的资源，OnRe<font size=+0>set</font>Device()就是Create*()恢复啦！你可能注意到ID3DXFont、ID3DXSprite等等都有同名的方法，就是在这个时候调用的。如果你没有这么做，也就是说还保留着任何D3DPOOL_DEFAULT的资源的话，IDirect3DDevice9::Re<font size=+0>set</font>就一定会失败。<br>　　另外在OnRe<font size=+0>set</font>Device里面你还要重新进行<font size=+0>Set</font>RenderState、<font size=+0>Set</font>SamplerState等等，Re<font size=+0>set</font>之后这些东西也丢失了。实际上Re<font size=+0>set</font>和重新创建一次设备类似，所不同的是重新创建设备的话你需要连D3DPOOL_MANAGED的资源也Release掉。这个话题就不讨论了。<br>　　从代码可以看出来，D3DERR_DEVICELOST时程序什么都没做，只是在傻等。我认为这是一个好习惯，因为实在不能保证在D3DERR_DEVICELOST时除了Release还能干什么，与其这样还不如等设备能用了再说。<br><br>　　实在懒得管资源的话，全部D3DPOOL_MANAGED好了。至于渲染对象？自己想办法。<br><br>『人工制造&#8220;设备丢失&#8221;』<br><br>　　&#8220;干嘛还要制造设备丢失啊？&#8221;如果更改游戏分辨率、色深、切换全屏幕及窗口状态，进行这样的操作也要通过Re<font size=+0>set</font>，同样的，Re<font size=+0>set</font>之前也要释放掉所有D3DPOOL_DEFAULT资源（其实严格来说，还有更多的资源也要释放，不过在2D下基本不会创建这类资源，你就不用管了）并且调用ID3DXSprite::OnLostDevice之类的方法。这就是人工制造&#8220;设备丢失&#8221;了。实际上在这个过程设备并没有真正的丢失，只是会有一段时间处于不可用的状态，此时Re<font size=+0>set</font>尚未返回，整个D3D设备就好像死了一样。举个例子，你切换桌面分辨率，会有那么一段时间显示器上什么都不显示，然后很快就正常了。和这个现象是同一个原因。Re<font size=+0>set</font>成功后记得恢复资源。<br>　　你可能注意到这里的Re<font size=+0>set</font>和上面的Re<font size=+0>set</font>不是一回事。的确是这样，这里是为了重设状态而不是恢复设备。因此更改分辨率、色深的Re<font size=+0>set</font>需要写到switch外面，也就是别和它搅和的意思-_-bb。而且你只需要OnLostDevice -&gt; Re<font size=+0>set</font> -&gt; OnRe<font size=+0>set</font>Device。记住：正确的调用Re<font size=+0>set</font>不会造成设备丢失，这个概念别弄混了。<br><br>『切换全屏幕模式时的注意事项』<br><br>　　注意WindowStyle的变化。切换成全屏幕模式后，只能使用WS_POPUP，不然显示会变得怪怪的，你可以通过<font size=+0>Set</font>WindowLongPtr函数更改窗口外观，第二个参数指定GWL_STYLE即可。别忘了WS_VISIBLE啊！不然你什么都看不见。<br><br>『更详细的文档』<br><br>　　我这里只是简单讨论了造成设备丢失的原因及处理方法，更详细的内容你可以参考DX SDK文档的Lost Device文章，人家是权威的。<br><br>【以上，正片结束，后面是ED】<br><br>　　我们前进到了D3D9，赶上了时代。<br>　　我们创建了全屏幕游戏，赶上了时代。<br>　　我却变得一脑子浆糊，被观众抛弃了。<br>　　哈哈，开玩笑啦，不过这一话很乱倒是真的，因为不论是更新到D3D9还是设备丢失，牵扯的东西都太散太杂，结果弄得这一话也是一盘散沙（居然又没有附带代码）。唉，大家就忍了吧，忍不了的话就来PIA我吧。<br><br>　　关于更新至D3D9更多的内容，你可以参考SDK文档的《Converting to Direct3D 9》。<br><br>【以上，ED结束，后面是&#8230;&#8230;】<br><br>　　第一季完结了&#8230;&#8230;<br>　　回过头来看看，从第一话创建一个Windows窗口，到这一话的设备丢失，话题的层次一直在深入，现在已经深入到了不再是&#8220;学习&#8221;而是&#8220;研究&#8221;的范围。我也不再想仅仅是搞&#8220;教学&#8221;而是想和大家&#8220;讨论&#8221;。不过第一季主要还是教学吧。能坚持着看D2D教程到现在的，应该基本能够写出完整的2D Demo来了吧。如果有什么问题的话，欢迎提出，我在看到后会立刻回答的&#8230;&#8230;只要你这个问题不太RP的话&#8230;&#8230;<br>　　那么，第二季会是什么样子？<br>　　第二季就不再是教学了，而开始我和大家的讨论过程。第二季的第一话，也就是第09话，我将提供一些高级技巧给大家，并希望有兴趣的朋友和我一起进行这些技巧的研究。另外在第二季里面，我们还要创建一个2D图形引擎。原来打算给大家讲解Medux 2，不过现在感觉这东西实在小儿科，绝对会让大家B4的。那么既然如此，干脆介绍Mercury 3好了，有意见无？<br>　　透漏一点下一话的内容吧：模糊精度和多次纹理渲染，嘿嘿，听上去挺高深的是不？实际上超级简单，就看你能不能想到而已。<br>　　希望你在看完这一话之后，返回去再把前面的内容看看，相信你会得到新的收获。搞不好你还能抓出几个Bug呢！因为我是想到什么写什么，没个章法，Bug是难免的。<br></div>
<div class=postfoot>&nbsp;</div>
</div>
<img src="http://www.cppblog.com/sunraiing9/aggbug/17200.html?webview=1" width=1 height=1> <br><br>附加:<br><br><strong>Direct3D中的字体与文本显示<br></strong>
<div width="100%">
<div style="FLOAT: right"><strong></strong></div>
<div>图形系统中为了获得当前运行程序的相关信息，往往需要在屏幕上显示文本，Direct3D的功能扩展接口ID3DXFont对此提供了方便的解决方法。
<p>&#160;</p>
<p>&#160;</p>
<p>创建ID3DXFont对象</p>
<p>使用接口ID3DXFont绘制文本，首先需要通过函数D3DXCreateFont()创建ID3DXFont字体对象。ID3DXFont接口封装了Windows字体和Direct3D设备指针，D3DXCreateFont()函数通过Windows字体和Direct3D设备指针创建ID3DXFont对象，该函数的声明如下：</p>
<p>Creates a font object for a device and font.</p>
<p>HRESULT D3DXCreateFont(&nbsp;&nbsp;LPDIRECT3DDEVICE9 pDevice,&nbsp;&nbsp;INT Height,&nbsp;&nbsp;UINT Width,&nbsp;&nbsp;UINT Weight,&nbsp;&nbsp;UINT MipLevels,&nbsp;&nbsp;BOOL Italic,&nbsp;&nbsp;DWORD CharSet,&nbsp;&nbsp;DWORD OutputPrecision,&nbsp;&nbsp;DWORD Quality,&nbsp;&nbsp;DWORD PitchAndFamily,&nbsp;&nbsp;LPCTSTR pFacename,&nbsp;&nbsp;LPD3DXFONT * ppFont);<br>Parameters<br>pDevice <br>[in] Pointer to an IDirect3DDevice9 interface, the device to be associated with the font object. <br>Height <br>[in] The height of the characters in logical units. <br>Width <br>[in] The width of the characters in logical units. <br>Weight <br>[in] Typeface weight. One example is bold. <br>MipLevels <br>[in] The number of mipmap levels. <br>Italic <br>[in] True for italic font, false otherwise. <br>CharSet <br>[in] The character set of the font. <br>OutputPrecision <br>[in] Specifies how Windows should attempt to match the desired font sizes and characteristics with actual fonts. Use OUT_TT_ONLY_PRECIS for instance, to ensure that you always get a TrueType font. <br>Quality <br>[in] Specifies how Windows should match the desired font with a real font. It applies to raster fonts only and should not affect TrueType fonts. <br>PitchAndFamily <br>[in] Pitch and family index. <br>pFacename <br>[in] String containing the typeface name. If the compiler settings require Unicode, the data type LPCTSTR resolves to LPCWSTR. Otherwise, the string data type resolves to LPCSTR. See Remarks. <br>ppFont <br>[out] Returns a pointer to an ID3DXFont interface, representing the created font object. <br>Return Values<br>If the function succeeds, the return value is S_OK. If the function fails, the return value can be one of the following: D3DERR_INVALIDCALL, D3DXERR_INVALIDDATA, E_OUTOFMEMORY.</p>
<p>Remarks<br>The creation of an ID3DXFont object requires that the device supports 32-bit color.</p>
<p>The compiler setting also determines the function version. If Unicode is defined, the function call resolves to D3DXCreateFontW. Otherwise, the function call resolves to D3DXCreateFontA because ANSI strings are being used.</p>
<p>If you want more information about font parameters, see The Logical Font.</p>
<p>示例代码如下：</p>
<p>D3DXCreateFont(g_device, 50, 20, 20, 0, FALSE, DEFAULT_CHARSET, 0, 0, 0, "Arial", &amp;g_font);</p>
</div>
</div>
<img src ="http://www.cppblog.com/flashboy/aggbug/96003.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flashboy/" target="_blank">RedLight</a> 2009-09-12 15:40 <a href="http://www.cppblog.com/flashboy/archive/2009/09/12/96003.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>优化3D图形渲染通道负载(转)</title><link>http://www.cppblog.com/flashboy/archive/2009/09/01/94944.html</link><dc:creator>RedLight</dc:creator><author>RedLight</author><pubDate>Tue, 01 Sep 2009 01:32:00 GMT</pubDate><guid>http://www.cppblog.com/flashboy/archive/2009/09/01/94944.html</guid><wfw:comment>http://www.cppblog.com/flashboy/comments/94944.html</wfw:comment><comments>http://www.cppblog.com/flashboy/archive/2009/09/01/94944.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flashboy/comments/commentRss/94944.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flashboy/services/trackbacks/94944.html</trackback:ping><description><![CDATA[一般来说， 定位渲染通道瓶颈的方法就是改变渲染通道每个步骤的工作量, 如果吞吐量也改变了, 那个步骤就是瓶颈.。找到了瓶颈就要想办法消除瓶颈, 可以减少该步骤的工作量, 增加其他步骤的工作量。 <br><br>　　 一般在光栅化之前的瓶颈称作&#8221;transform bound&#8221;, 三角形设置处理后的瓶颈称作&#8221;fill bound&#8221;定位瓶颈的办法:
<ul class=ubb-list>
    <li>1.改变帧缓冲或者渲染目标(Render Target)的颜色深度(16 到 32 位), 如果帧速改变了, 那么瓶颈应该在帧缓冲(RenderTarget)的填充率上。 <br>
    <li>2.否则试试改变贴图大小和贴图过滤设置, 如果帧速变了,那么瓶颈应该是在贴图这里。 <br>
    <li>3.否则改变分辨率.如果帧速改变了, 那么改变一下pixel shader的指令数量, 如果帧速变了, 那么瓶颈应该就是pixel shader. 否则瓶颈就在光栅化过程中。 <br>
    <li>4.否则, 改变顶点格式的大小, 如果帧速改变了, 那么瓶颈应该在显卡带宽上。 <br>
    <li>5.如果以上都不是, 那么瓶颈就在CPU这一边。 <br>
    <li>优化方法36条: <br>
    <li>1.尽量减少无用的顶点数据, 比如贴图坐标, 如果有Object使用2组有的使用1组, 那么不 要将他们放在一个vertex buffer中, 这样可以减少传输的数据量。 <br>
    <li>2.使用多个streamsource, 比如SkinMesh渲染, 可以把顶点坐标和法线这些每一帧都要修改的数据放在一个动态VB中, 其它不需要修改的(如贴图坐标)放到一个静态VB中, 这样就减少了数据传输量。 <br>
    <li>3.尽量使用16位的索引缓冲,避免32位的. 一方面浪费带宽, 一方面也不是所有的显卡都支持32位的索引缓冲。 <br>
    <li>4.可以考虑使用vertex shader来计算静态VB中的数据.比如SkinMesh的顶点可以放到vectex shader中计算, 这样就可以避免每一帧都从AGP内存中向显存传送数据. 这样也可以使用静态VB了。 <br>
    <li>5.坚决避免使用Draw**UP一族的函数来绘制多边形。 <br>
    <li>6.在设计程序之前好好规划一下显卡内存的使用, 确保framebuffer, 贴图, 静态VB能够正好放入显卡的本地内存中。 <br>
    <li>7.尽量使顶点格式大小是32字节的倍数.可以考虑使用压缩过的顶点格式然后用vertex shader去解. 或者留下冗余的部分, 使顶点大小刚好使32字节的倍数。<br>
    <li>8.顶点在顶点缓冲中的顺序尽量符合绘制的顺序, 考虑使用strips来代替list。 <br>
    <li>9.如果可能尽量多的使用static vertex buffer代替dynamic vertex buffer。 <br>
    <li>10.动态VB使用DISCARD参数来lock更新, 使用NOOVERWRITE来添加.尽量不要使用不带参数的lock调用(0)。 <br>
    <li>11.尽量减少lock的次数, 有些东西并不一定非要每一帧都更新VB, 比如人物动画一般每秒钟更新30次VB基本上就够了。 <br>
    <li>12.如果是因为需要绘制的顶点数据太多了可以考虑使用LOD, 但是现在的显卡的绘制能力都很强劲, 所以需要权衡一下LOD是否能够带来相应的好处, 如果过分的强化LOD很可能将瓶颈转移到CPU这边。 <br>
    <li>13.避免过多的顶点计算,比如过多的光源, 过于复杂的光照计算(复杂的光照模型), 纹理自动生成的开启也会增加顶点的计算量. 如果贴图坐标变换矩阵不是单位矩阵, 也会造成顶点计算量的增加, 所以如果纹理变换已经结束, 记得要将纹理变换矩阵设为单位矩阵同时调整贴图坐标。 <br>
    <li>14.避免Vertex shader指令数量太多或者分支过多, 尽量减少vertex shader的长度和复杂程度. 尽量使用swizzling代替mov。 <br>
    <li>15.如果图象质量方面的计算(pixel shader)范围很大, 并且很复杂, 可以考虑试试全屏反走样。说不定更快。 <br>
    <li>16.尽量按照front &#8211; back的顺序来绘制。 <br>
    <li>17.在shader中判断Z值可以避免绘制不可见的象素, 但是nvidia建议简单的shader不要这么做.(Don't do this in a simple shader)。 <br>
    <li>18.如果可能, 尽量使用vertex shader来代替pixel shader.将计算从逐象素变成逐顶点。 <br>
    <li>19.尽量降低贴图的大小.过大的贴图可能造成贴图cache过载, 从而导致贴图cache命中降低.过大的贴图会导致显存过载, 这时候贴图是从系统内存中取的。<br>
    <li>20.只要可能就用16位色的贴图, 如环境贴图或者shadow map.它们用32位色的贴图实在是浪费。 <br>
    <li>21.考虑使用DXT 贴图压缩。 <br>
    <li>22.如果可能,使用简单的贴图过滤或者mip map, 除非必要否则尽量不要使用三线过滤和各项异性过滤. light map 和 环境贴图基本上都不需要使用它们。 <br>
    <li>23.只有真正需要修改的贴图才使用Dynamic, 并且使用DISCRAD和WRITEONLY来lock。 <br>
    <li>24.太多的帧缓冲读写可以考虑关闭Z-Writes如有些多pass的渲染中的后续pass或者粒子系统等半透明几何物体（如果可以）。 <br>
    <li>25.可能的话尽量使用alpha test代替alpha blending。 <br>
    <li>26.如果不需要stencil buffer就尽量使用16位的Z buffer。 <br>
    <li>27.减小RenderTarget 贴图的大小, 如shadow map 环境贴图. 可能根本不需要那么大效果就很好。 <br>
    <li>28.Stencil 和 Z buffer 尽量一起clear. 他们本来就是一块缓冲。 <br>
    <li>29.尽量减少渲染状态的切换, 尽量一次画尽可能多的多边形。（根据显卡性能决定最多画多少， 不过一般再多也不会多到哪里去。 除非你根本不需要贴图和渲染状态的切换）。 <br>
    <li>30.尽量使用shader来代替Fixed Pipeline。 <br>
    <li>31.尽量使用shader来实现来取代Multipass渲染效果。 <br>
    <li>32.尽量优先先建立重要的资源, 如Render target, shaders, 贴图, VB, IB等等.以免显存过载的时候它们被创建到系统内存中。 <br>
    <li>33.坚决不要在渲染循环中调用创建资源。 <br>
    <li>34.按照shader和贴图分组后再渲染.先按照shaders分组再按贴图。 <br>
    <li>35.Color Stencil Z buffer尽量在一次Clear调用中清除。 <br>
    <li>36.一个Vertex buffer 的大小在2M-4M之间最好。 </li>
</ul>
<img src ="http://www.cppblog.com/flashboy/aggbug/94944.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flashboy/" target="_blank">RedLight</a> 2009-09-01 09:32 <a href="http://www.cppblog.com/flashboy/archive/2009/09/01/94944.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>OpenGL Performance Optimization(转)</title><link>http://www.cppblog.com/flashboy/archive/2009/08/25/94364.html</link><dc:creator>RedLight</dc:creator><author>RedLight</author><pubDate>Tue, 25 Aug 2009 06:05:00 GMT</pubDate><guid>http://www.cppblog.com/flashboy/archive/2009/08/25/94364.html</guid><wfw:comment>http://www.cppblog.com/flashboy/comments/94364.html</wfw:comment><comments>http://www.cppblog.com/flashboy/archive/2009/08/25/94364.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flashboy/comments/commentRss/94364.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flashboy/services/trackbacks/94364.html</trackback:ping><description><![CDATA[<h1>SIGGRAPH '97</h1>
<h1>Course 24: OpenGL and Window System Integration</h1>
<h1>OpenGL Performance Optimization</h1>
<br><br>
<h2>Contents</h2>
<ul>
    <li><a href="http://www.mesa3d.org/brianp/sig97/perfopt.htm#Hardware"><u><font color=#0000ff>1. Hardware vs. Software</font></u></a>
    <li><a href="http://www.mesa3d.org/brianp/sig97/perfopt.htm#Organize"><u><font color=#0000ff>2. Application Organization</font></u></a>
    <ul>
        <li><a href="http://www.mesa3d.org/brianp/sig97/perfopt.htm#Highlevel"><u><font color=#0000ff>2.1 High Level Organization</font></u></a>
        <li><a href="http://www.mesa3d.org/brianp/sig97/perfopt.htm#Lowlevel"><u><font color=#0000ff>2.2 Low Level Organization</font></u></a> </li>
    </ul>
    <li><a href="http://www.mesa3d.org/brianp/sig97/perfopt.htm#Optimization"><u><font color=#0000ff>3. OpenGL Optimization</font></u></a>
    <ul>
        <li><a href="http://www.mesa3d.org/brianp/sig97/perfopt.htm#Traversal"><u><font color=#0000ff>3.1 Traversal</font></u></a>
        <li><a href="http://www.mesa3d.org/brianp/sig97/perfopt.htm#Transformation"><u><font color=#0000ff>3.2 Transformation</font></u></a>
        <li><a href="http://www.mesa3d.org/brianp/sig97/perfopt.htm#Rasterization"><u><font color=#0000ff>3.3 Rasterization</font></u></a>
        <li><a href="http://www.mesa3d.org/brianp/sig97/perfopt.htm#Texturing"><u><font color=#0000ff>3.4 Texturing</font></u></a>
        <li><a href="http://www.mesa3d.org/brianp/sig97/perfopt.htm#Clearing"><u><font color=#0000ff>3.5 Clearing</font></u></a>
        <li><a href="http://www.mesa3d.org/brianp/sig97/perfopt.htm#Misc"><u><font color=#0000ff>3.6 Miscellaneous</font></u></a>
        <li><a href="http://www.mesa3d.org/brianp/sig97/perfopt.htm#Window"><u><font color=#0000ff>3.7 Window System Integration</font></u></a>
        <li><a href="http://www.mesa3d.org/brianp/sig97/perfopt.htm#Mesa"><u><font color=#0000ff>3.8 Mesa-specific</font></u></a> </li>
    </ul>
    <li><a href="http://www.mesa3d.org/brianp/sig97/perfopt.htm#Eval"><u><font color=#0000ff>4. Evaluation and tuning</font></u></a>
    <ul>
        <li><a href="http://www.mesa3d.org/brianp/sig97/perfopt.htm#Pipeline"><u><font color=#0000ff>4.1 Pipeline tuning</font></u></a>
        <li><a href="http://www.mesa3d.org/brianp/sig97/perfopt.htm#Double"><u><font color=#0000ff>4.2 Double buffering</font></u></a>
        <li><a href="http://www.mesa3d.org/brianp/sig97/perfopt.htm#Several"><u><font color=#0000ff>4.3 Test on several implementations</font></u></a> </li>
    </ul>
    </li>
</ul>
<p><br><br><a name=Hardware>
<h2>1. Hardware vs. Software</h2>
<p>OpenGL may be implemented by any combination of hardware and software. At the high-end, hardware may implement virtually all of OpenGL while at the low-end, OpenGL may be implemented entirely in software. In between are combination software/hardware implementations. More money buys more hardware and better performance.
<p>Intro-level workstation hardware and the recent PC 3-D hardware typically implement point, line, and polygon rasterization in hardware but implement floating point transformations, lighting, and clipping in software. This is a good strategy since the bottleneck in 3-D rendering is usually rasterization and modern CPU's have sufficient floating point performance to handle the transformation stage.
<p>OpenGL developers must remember that their application may be used on a wide variety of OpenGL implementations. Therefore one should consider using all possible optimizations, even those which have little return on the development system, since other systems may benefit greatly.
<p>From this point of view it may seem wise to develop your application on a low-end system. There is a pitfall however; some operations which are cheep in software may be expensive in hardware. The moral is: test your application on a variety of systems to be sure the performance is dependable.
<p><br><br><a name=Organize>
<h2>2. Application Organization</h2>
At first glance it may seem that the performance of interactive OpenGL applications is dominated by the performance of OpenGL itself. This may be true in some circumstances but be aware that the organization of the application is also significant.
<p><a name=Highlevel>
<h3>2.1 High Level Organization</h3>
<h4>Multiprocessing</h4>
Some graphical applications have a substantial computational component other than 3-D rendering. Virtual reality applications must compute object interactions and collisions. Scientific visualization programs must compute analysis functions and graphical representations of data.
<p>One should consider multiprocessing in these situations. By assigning rendering and computation to different threads they may be executed in parallel on multiprocessor computers.
<p>For many applications, supporting multiprocessing is just a matter of partitioning the render and compute operations into separate threads which share common data structures and coordinate with synchronization primitives.
<p>SGI's Performer is an example of a high level toolkit designed for this purpose.
<p>
<h4>Image quality vs. performance</h4>
In general, one wants high-speed animation and high-quality images in an OpenGL application. If you can't have both at once a reasonable compromise may be to render at low complexity during animation and high complexity for static images.
<p>Complexity may refer to the geometric or rendering attributes of a database. Here are a few examples.
<ul>
    <li>During interactive rotation (i.e. mouse button held down) render a reduced-polygon model. When drawing a static image draw the full polygon model.
    <li>During animation, disable dithering, smooth shading, and/or texturing. Enable them for the static image.
    <li>If texturing is required, use <code>GL_NEAREST</code> sampling and <code>glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST )</code>.
    <li>During animation, disable antialiasing. Enable antialiasing for the static image.
    <li>Use coarser NURBS/evaluator tesselation during animation. Use <code>glPolygonMode( GL_FRONT_AND_BACK, GL_LINE )</code> to inspect tesselation granularity and reduce if possible. </li>
</ul>
<h4>Level of detail management and culling</h4>
Objects which are distant from the viewer may be rendered with a reduced complexity model. This strategy reduces the demands on all stages of the graphics pipeline. Toolkits such as Inventor and Performer support this feature automatically.
<p>Objects which are entirely outside of the field of view may be culled. This type of high level cull testing can be done efficiently with bounding boxes or spheres and have a major impact on performance. Again, toolkits such as Inventor and Performer have this feature.
<p><a name=Lowlevel>
<h3>2.2 Low Level Organization</h3>
The objects which are rendered with OpenGL have to be stored in some sort of data structure. Some data structures are more efficient than others with respect to how quickly they can be rendered.
<p>Basically, one wants data structures which can be traversed quickly and passed to the graphics library in an efficient manner. For example, suppose we need to render a triangle strip. The data structure which stores the list of vertices may be implemented with a linked list or an array. Clearly the array can be traversed more quickly than a linked list. The way in which a vertex is stored in the data structure is also significant. High performance hardware can process vertexes specified by a pointer more quickly than those specified by three separate parameters.
<p>
<h4>An Example</h4>
Suppose we're writing an application which involves drawing a road map. One of the components of the database is a list of cities specified with a latitude, longitude and name. The data structure describing a city may be:
<pre>	struct city {
float latitute, longitude;	/* city location */
char *name;			/* city's name */
int large_flag;  		/* 0 = small, 1 = large */
};
</pre>
A list of cities may be stored as an array of city structs.
<p>Our first attempt at rendering this information may be:
<pre>	void draw_cities( int n, struct city citylist[] )
{
int i;
for (i=0; i &lt; n; i++) {
if (citylist[i].large_flag) {
glPointSize( 4.0 );
}
else {
glPointSize( 2.0 );
}
glBegin( GL_POINTS );
glVertex2f( citylist[i].longitude, citylist[i].latitude );
glEnd();
glRasterPos2f( citylist[i].longitude, citylist[i].latitude );
glCallLists( strlen(citylist[i].name),
GL_BYTE,
citylist[i].name );
}
}
</pre>
This is a poor implementation for a number of reasons:
<ul>
    <li><code>glPointSize</code> is called for every loop iteration.
    <li>only one point is drawn between <code>glBegin</code> and <code>glEnd</code>
    <li>the vertices aren't being specified in the most efficient manner </li>
</ul>
Here's a better implementation:
<pre>	void draw_cities( int n, struct city citylist[] )
{
int i;
/* draw small dots first */
glPointSize( 2.0 );
glBegin( GL_POINTS );
for (i=0; i &lt; n ;i++) {
if (citylist[i].large_flag==0) {
glVertex2f( citylist[i].longitude, citylist[i].latitude );
}
}
glEnd();
/* draw large dots second */
glPointSize( 4.0 );
glBegin( GL_POINTS );
for (i=0; i &lt; n ;i++) {
if (citylist[i].large_flag==1) {
glVertex2f( citylist[i].longitude, citylist[i].latitude );
}
}
glEnd();
/* draw city labels third */
for (i=0; i &lt; n ;i++) {
glRasterPos2f( citylist[i].longitude, citylist[i].latitude );
glCallLists( strlen(citylist[i].name),
GL_BYTE,
citylist[i].name );
}
}
</pre>
In this implementation we're only calling </code>glPointSize</code> twice and we're maximizing the number of vertices specified between <code>glBegin</code> and <code>glEnd</code>.
<p>We can still do better, however. If we redesign the data structures used to represent the city information we can improve the efficiency of drawing the city points. For example:
<pre>	struct city_list {
int num_cities;		/* how many cities in the list */
float *position;	/* pointer to lat/lon coordinates */
char **name;		/* pointer to city names */
float size;		/* size of city points */
};
</pre>
Now cities of different sizes are stored in separate lists. Position are stored sequentially in a dynamically allocated array. By reorganizing the data structures we've eliminated the need for a conditional inside the <code>glBegin/glEnd</code> loops. Also, we can render a list of cities using the <code>GL_EXT_vertex_array</code> extension if available, or at least use a more efficient version of <code>glVertex</code> and <code>glRasterPos</code>.
<pre>	/* indicates if server can do GL_EXT_vertex_array: */
GLboolean varray_available;
void draw_cities( struct city_list *list )
{
int i;
GLboolean use_begin_end;
/* draw the points */
glPointSize( list-&gt;size );
#ifdef GL_EXT_vertex_array
if (varray_available) {
glVertexPointerEXT( 2, GL_FLOAT, 0, list-&gt;num_cities, list-&gt;position );
glDrawArraysEXT( GL_POINTS, 0, list-&gt;num_cities );
use_begin_end = GL_FALSE;
}
else
#else
{
use_begin_end = GL_TRUE;
}
#endif
if (use_begin_end) {
glBegin(GL_POINTS);
for (i=0; i &lt; list-&gt;num_cities; i++) {
glVertex2fv( &amp;position[i*2] );
}
glEnd();
}
/* draw city labels */
for (i=0; i &lt; list-&gt;num_cities ;i++) {
glRasterPos2fv( list-&gt;position[i*2] );
glCallLists( strlen(list-&gt;name[i]),
GL_BYTE, list-&gt;name[i] );
}
}
</pre>
As this example shows, it's better to know something about efficient rendering techniques before designing the data structures. In many cases one has to find a compromize between data structures optimized for rendering and those optimized for clarity and convenience.
<p>In the following sections the techniques for maximizing performance, as seen above, are explained.
<p><br><br><a name=Optimization>
<h2>3. OpenGL Optimization</h2>
There are many possibilities to improving OpenGL performance. The impact of any single optimization can vary a great deal depending on the OpenGL implementation. Interestingly, items which have a large impact on software renderers may have no effect on hardware renderers, <em>and vice versa</em>! For example, smooth shading can be expensive in software but free in hardware While <code>glGet*</code> can be cheap in software but expensive in hardware.
<p>After each of the following techniques look for a bracketed list of symbols which relates the significance of the optimization to your OpenGL system:
<ul>
    <li>H - beneficial for high-end hardware
    <li>L - beneficial for low-end hardware
    <li>S - beneficial for software implementations
    <li>all - probably beneficial for all implementations </li>
</ul>
<p><a name=Traversal>
<h3>3.1 Traversal</h3>
Traversal is the sending of data to the graphics system. Specifically, we want to minimize the time taken to specify primitives to OpenGL.
<dl>
<dt>Use connected primitives
<dd>Connected primitives such as <code>GL_LINES, GL_LINE_LOOP, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN</code>, and <code>GL_QUAD_STRIP</code> require fewer vertices to describe an object than individual line, triangle, or polygon primitives. This reduces data transfer and transformation workload. [all] </dd></dl>
<dl>
<dt>Use the vertex array extension
<dd>On some architectures function calls are somewhat expensive so replacing many <code>glVertex/glColor/glNormal</code> calls with the vertex array mechanism may be very beneficial. [all] </dd></dl>
<dl>
<dt>Store vertex data in consecutive memory locations
<dd>When maximum performance is needed on high-end systems it's good to store vertex data in contiguous memory to maximize through put of data from host memory to graphics subsystem. [H,L] </dd></dl>
<dl>
<dt>Use the vector versions of <code>glVertex</code>, <code>glColor</code>, <code>glNormal</code> and <code>glTexCoord</code>
<dd>The <code>glVertex</code>, <code>glColor</code>, etc. functions which take a pointer to their arguments such as <code>glVertex3fv(v)</code> may be much faster than those which take individual arguments such as <code>glVertex3f(x,y,z)</code> on systems with DMA-driven graphics hardware. [H,L] </dd></dl>
<dl>
<dt>Reduce quantity of primitives
<dd>Be careful not to render primitives which are over-tesselated. Experiment with the GLU primitives, for example, to determine the best compromise of image quality vs. tesselation level. Textured objects in particular may still be rendered effectively with low geometric complexity. [all] </dd></dl>
<dl>
<dt>Display lists
<dd>Use display lists to encapsulate frequently drawn objects. Display list data may be stored in the graphics subsystem rather than host memory thereby eliminating host-to-graphics data movement. Display lists are also very beneficial when rendering remotely. [all] </dd></dl>
<dl>
<dt>Don't specify unneeded per-vertex information
<dd>If lighting is disabled don't call <code>glNormal</code>. If texturing is disabled don't call <code>glTexCoord</code>, etc. </dd></dl>
<dl>
<dt>Minimize code between <code>glBegin/glEnd</code>
<dd>For maximum performance on high-end systems it's extremely important to send vertex data to the graphics system as fast as possible. Avoid extraneous code between <code>glBegin/glEnd</code>.
<p>Example:
<p>
<pre>	glBegin( GL_TRIANGLE_STRIP );
for (i=0; i &lt; n; i++) {
if (lighting) {
glNormal3fv( norm[i] );
}
glVertex3fv( vert[i] );
}
glEnd();
</pre>
<p>This is a very bad construct. The following is much better:
<p>
<pre>	if (lighting) {
glBegin( GL_TRIANGLE_STRIP );
for (i=0; i &lt; n ;i++) {
glNormal3fv( norm[i] );
glVertex3fv( vert[i] );
}
glEnd();
}
else {
glBegin( GL_TRIANGLE_STRIP );
for (i=0; i &lt; n ;i++) {
glVertex3fv( vert[i] );
}
glEnd();
}
</pre>
Also consider manually unrolling important rendering loops to maximize the function call rate. </dd></dl><a name=Transformation>
<h3>3.2 Transformation</h3>
Transformation includes the transformation of vertices from <code>glVertex</code> to window coordinates, clipping and lighting.
<p>
<dl>
<dt>Lighting
<dd>
<ul>
    <li>Avoid using positional lights, i.e. light positions should be of the form (x,y,z,0) [L,S]
    <li>Avoid using spotlights. [all]
    <li>Avoid using two-sided lighting. [all]
    <li>Avoid using negative material and light color coefficients [S]
    <li>Avoid using the local viewer lighting model. [L,S]
    <li>Avoid frequent changes to the <code>GL_SHININESS</code> material parameter. [L,S]
    <li>Some OpenGL implementations are optimized for the case of a single light source.
    <li>Consider pre-lighting complex objects before rendering, ala radiosity. You can get the effect of lighting by specifying vertex colors instead of vertex normals. [S] </li>
</ul>
</dd></dl>
<dl>
<dt>Two sided lighting
<dd>If you want both the front and back of polygons shaded the same try using two light sources instead of two-sided lighting. Position the two light sources on opposite sides of your object. That way, a polygon will always be lit correctly whether it's back or front facing. [L,S] </dd></dl>
<dl>
<dt>Disable normal vector normalization when not needed
<dd><code>glEnable/Disable(GL_NORMALIZE)</code> controls whether normal vectors are scaled to unit length before lighting. If you do not use <code>glScale</code> you may be able to disable normalization without ill effects. Normalization is disabled by default. [L,S] </dd></dl>
<dl>
<dt>Use connected primitives
<dd>Connected primitives such as <code>GL_LINES</code>, <code>GL_LINE_LOOP</code>, <code>GL_TRIANGLE_STRIP</code>, <code>GL_TRIANGLE_FAN</code>, and <code>GL_QUAD_STRIP</code> decrease traversal and transformation load. </dd></dl>
<dl>
<dt><code>glRect</code> usage
<dd>If you have to draw many rectangles consider using <code>glBegin(GL_QUADS)</code> ... <code>glEnd()</code> instead. [all] </dd></dl><a name=Rasterization>
<h3>3.3 Rasterization</h3>
Rasterization is the process of generating the pixels which represent points, lines, polygons, bitmaps and the writing of those pixels to the frame buffer. Rasterization is often the bottleneck in software implementations of OpenGL.
<dl>
<dt>Disable smooth shading when not needed
<dd>Smooth shading is enabled by default. Flat shading doesn't require interpolation of the four color components and is usually faster than smooth shading in software implementations. Hardware may perform flat and smooth-shaded rendering at the same rate though there's at least one case in which smooth shading is faster than flat shading (E&amp;S Freedom). [S] </dd></dl>
<dl>
<dt>Disable depth testing when not needed
<dd>Background objects, for example, can be drawn without depth testing if they're drawn first. Foreground objects can be drawn without depth testing if they're drawn last. [L,S] </dd></dl>
<dl>
<dt>Disable dithering when not needed
<dd>This is easy to forget when developing on a high-end machine. Disabling dithering can make a big difference in software implementations of OpenGL on lower-end machines with 8 or 12-bit color buffers. Dithering is enabled by default. [S] </dd></dl>
<dl>
<dt>Use back-face culling whenever possible.
<dd>If you're drawing closed polyhedra or other objects for which back facing polygons aren't visible there's probably no point in drawing those polygons. [all] </dd></dl>
<dl>
<dt>The GL_SGI_cull_vertex extension
<dd>SGI's Cosmo GL supports a new culling extension which looks at vertex normals to try to improve the speed of culling. </dd></dl>
<dl>
<dt>Avoid extra fragment operations
<dd>Stenciling, blending, stippling, alpha testing and logic ops can all take extra time during rasterization. Be sure to disable the operations which aren't needed. [all] </dd></dl>
<dl>
<dt>Reduce the window size or screen resolution
<dd>A simple way to reduce rasterization time is to reduce the number of pixels drawn. If a smaller window or reduced display resolution are acceptable it's an easy way to improve rasterization speed. [L,S] </dd></dl><a name=Texturing>
<h3>3.4 Texturing</h3>
Texture mapping is usually an expensive operation in both hardware and software. Only high-end graphics hardware can offer free to low-cost texturing. In any case there are several ways to maximize texture mapping performance.
<dl>
<dt>Use efficient image formats
<dd>The <code>GL_UNSIGNED_BYTE</code> component format is typically the fastest for specifying texture images. Experiment with the internal texture formats offered by the <code>GL_EXT_texture</code> extension. Some formats are faster than others on some systems (16-bit texels on the Reality Engine, for example). [all] </dd></dl>
<dl>
<dt>Encapsulate texture maps in texture objects or display lists
<dd>This is especially important if you use several texture maps. By putting textures into display lists or texture objects the graphics system can manage their storage and minimize data movement between the client and graphics subsystem. [all] </dd></dl>
<dl>
<dt>Use smaller texture maps
<dd>Smaller images can be moved from host to texture memory faster than large images. More small texture can be stored simultaneously in texture memory, reducing texture memory swapping. [all] </dd></dl>
<dl>
<dt>Use simpler sampling functions
<dd>Experiment with the minification and magnification texture filters to determine which performs best while giving acceptable results. Generally, GL_NEAREST is fastest and GL_LINEAR is second fastest. [all] </dd></dl>
<dl>
<dt>Use the same sampling function for minification and magnification
<dd>If both the minification and magnification filters are <code>GL_NEAREST</code> or <code>GL_LINEAR</code> then there's no reason OpenGL has to compute the <em>lambda</em> value which determines whether to use minification or magnification sampling for each fragment. Avoiding the lambda calculation can be a good performace improvement. </dd></dl>
<dl>
<dt>Use a simpler texture environment function
<dd>Some texture environment modes may be faster than others. For example, the <code>GL_DECAL</code> or <code>GL_REPLACE_EXT</code> functions for 3 component textures is a simple assignment of texel samples to fragments while <code>GL_MODULATE</code> is a linear interpolation between texel samples and incoming fragments. [S,L] </dd></dl>
<dl>
<dt>Combine small textures
<dd>If you are using several small textures consider tiling them together as a larger texture and modify your texture coordinates to address the subtexture you want. This technique can eliminate texture bindings. </dd></dl>
<dl>
<dt>Use glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST)
<dd>This hint can improve the speed of texturing when perspective- correct texture coordinate interpolation isn't needed, such as when using a glOrtho() projection. </dd></dl>
<dl>
<dt>Animated textures
<dd>If you want to use an animated texture, perhaps live video textures, don't use <code>glTexImage2D</code> to repeatedly change the texture. Use <code>glTexSubImage2D</code> or <code>glTexCopyTexSubImage2D</code>. These functions are standard in OpenGL 1.1 and available as extensions to 1.0. </dd></dl><a name=Clearing>
<h3>3.5 Clearing</h3>
Clearing the color, depth, stencil and accumulation buffers can be time consuming, especially when it has to be done in software. There are a few tricks which can help.
<dl>
<dt>Use <code>glClear</code> carefully [all]
<dd>Clear all relevant color buffers with one <code>glClear</code>.
<p>Wrong:
<p>
<pre>  glClear( GL_COLOR_BUFFER_BIT );
if (stenciling) {
glClear( GL_STENCIL_BUFFER_BIT );
}
</pre>
Right:
<p>
<pre>  if (stenciling) {
glClear( GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
}
else {
glClear( GL_COLOR_BUFFER_BIT );
}
</pre>
</dd></dl>
<dl>
<dt>Disable dithering
<dd>Disable dithering before clearing the color buffer. Visually, the difference between dithered and undithered clears is usually negligable. </dd></dl>
<dl>
<dt>Use scissoring to clear a smaller area
<dd>If you don't need to clear the whole buffer use <code>glScissor()</code> to restrict clearing to a smaller area. [L]. </dd></dl>
<dl>
<dt>Don't clear the color buffer at all
<dd>If the scene you're drawing opaquely covers the entire window there is no reason to clear the color buffer. </dd></dl>
<dl>
<dt>Eliminate depth buffer clearing
<dd>If the scene you're drawing covers the entire window there is a trick which let's you omit the depth buffer clear. The idea is to only use half the depth buffer range for each frame and alternate between using GL_LESS and GL_GREATER as the depth test function.
<p>Example:
<pre>   int EvenFlag;
/* Call this once during initialization and whenever the window
* is resized.
*/
void init_depth_buffer( void )
{
glClearDepth( 1.0 );
glClear( GL_DEPTH_BUFFER_BIT );
glDepthRange( 0.0, 0.5 );
glDepthFunc( GL_LESS );
EvenFlag = 1;
}
/* Your drawing function */
void display_func( void )
{
if (EvenFlag) {
glDepthFunc( GL_LESS );
glDepthRange( 0.0, 0.5 );
}
else {
glDepthFunc( GL_GREATER );
glDepthRange( 1.0, 0.5 );
}
EvenFlag = !EvenFlag;
/* draw your scene */
}
</pre>
</dd></dl>
<dl>
<dt>Avoid glClearDepth( d ) where d!=1.0
<dd>Some software implementations may have optimized paths for clearing the depth buffer to 1.0. [S] </dd></dl><a name=Misc>
<h3>3.6 Miscellaneous</h3>
<dl>
<dt>Avoid "round-trip" calls
<dd>Calls such as <code>glGetFloatv, glGetIntegerv, glIsEnabled, glGetError, glGetString</code> require a slow, round trip transaction between the application and renderer. Especially avoid them in your main rendering code.
<p>Note that software implementations of OpenGL may actually perform these operations faster than hardware systems. If you're developing on a low-end system be aware of this fact. [H,L] </p>
</dd></dl>
<dl>
<dt>Avoid <code>glPushAttrib</code>
<dd>If only a few pieces of state need to be saved and restored it's often faster to maintain the information in the client program. <code>glPushAttrib( GL_ALL_ATTRIB_BITS )</code> in particular can be very expensive on hardware systems. This call may be faster in software implementations than in hardware. [H,L] </dd></dl>
<dl>
<dt>Check for GL errors during development
<dd>During development call <code>glGetError</code> inside your rendering/event loop to catch errors. GL errors raised during rendering can slow down rendering speed. Remove the <code>glGetError</code> call for production code since it's a "round trip" command and can cause delays. [all] </dd></dl>
<dl>
<dt>Use <code>glColorMaterial</code> instead of <code>glMaterial</code>
<dd>If you need to change a material property on a per vertex basis, <code>glColorMaterial</code> may be faster than <code>glMaterial</code>. [all] </dd></dl>
<dl>
<dt><code>glDrawPixels</code>
<ul>
    <li><code>glDrawPixels</code> often performs best with <code>GL_UNSIGNED_BYTE</code> color components [all]
    <li>Disable all unnecessary raster operations before calling <code>glDrawPixels</code>. [all]
    <li>Use the GL_EXT_abgr extension to specify color components in alpha, blue, green, red order on systems which were designed for IRIS GL. [H,L]. </li>
</ul>
</dt></dl>
<dl>
<dt>Avoid using viewports which are larger than the window
<dd>Software implementations may have to do additional clipping in this situation. [S] </dd></dl>
<dl>
<dt>Alpha planes
<dd>Don't allocate alpha planes in the color buffer if you don't need them. Specifically, they are not needed for transparency effects. Systems without hardware alpha planes may have to resort to a slow software implementation. [L,S] </dd></dl>
<dl>
<dt>Accumulation, stencil, overlay planes
<dd>Do not allocate accumulation, stencil or overlay planes if they are not needed. [all] </dd></dl>
<dl>
<dt>Be aware of the depth buffer's depth
<dd>Your OpenGL may support several different sizes of depth buffers- 16 and 24-bit for example. Shallower depth buffers may be faster than deep buffers both for software and hardware implementations. However, the precision of of a 16-bit depth buffer may not be sufficient for some applications. [L,S] </dd></dl>
<dl>
<dt>Transparency may be implemented with stippling instead of blending
<dd>If you need simple transparent objects consider using polygon stippling instead of alpha blending. The later is typically faster and may actually look better in some situations. [L,S] </dd></dl>
<dl>
<dt>Group state changes together
<dd>Try to mimimize the number of GL state changes in your code. When GL state is changed, internal state may have to be recomputed, introducing delays. [all] </dd></dl>
<dl>
<dt>Avoid using <code>glPolygonMode</code>
<dd>If you need to draw many polygon outlines or vertex points use <code>glBegin</code> with <code>GL_POINTS, GL_LINES, GL_LINE_LOOP</code> or <code>GL_LINE_STRIP</code> instead as it can be much faster. [all] </dd></dl><a name=Window>
<h3>3.7 Window System Integration</h3>
<dl>
<dt>Minimize calls to the <em>make current</em> call
<dd>The <code>glXMakeCurrent</code> call, for example, can be expensive on hardware systems because the context switch may involve moving a large amount of data in and out of the hardware. </dd></dl>
<dl>
<dt>Visual / pixel format performance
<dd>Some X visuals or pixel formats may be faster than others. On PCs for example, 24-bit color buffers may be slower to read/write than 12 or 8-bit buffers. There is often a tradeoff between performance and quality of frame buffer configurations. 12-bit color may not look as nice as 24-bit color. A 16-bit depth buffer won't have the precision of a 24-bit depth buffer.
<p>The <code>GLX_EXT_visual_rating</code> extension can help you select visuals based on performance or quality. GLX 1.2's <em>visual caveat</em> attribute can tell you if a visual has a performance penalty associated with it.
<p>It may be worthwhile to experiment with different visuals to determine if there's any advantage of one over another. </p>
</dd></dl>
<dl>
<dt>Avoid mixing OpenGL rendering with native rendering
<dd>OpenGL allows both itself and the native window system to render into the same window. For this to be done correctly synchronization is needed. The GLX <code>glXWaitX</code> and <code>glXWaitGL</code> functions serve this purpose.
<p>Synchronization hurts performance. Therefore, if you need to render with both OpenGL and native window system calls try to group the rendering calls to minimize synchronization.
<p>For example, if you're drawing a 3-D scene with OpenGL and displaying text with X, draw all the 3-D elements first, call <code>glXWaitGL</code> to synchronize, then call all the X drawing functions. </p>
</dd></dl>
<dl>
<dt>Don't redraw more than necessary
<dd>Be sure that you're not redrawing your scene unnecissarily. For example, expose/repaint events may come in batches describing separate regions of the window which must be redrawn. Since one usually redraws the whole window image with OpenGL you only need to respond to one expose/repaint event. In the case of X, look at the count field of the XExposeEvent structure. Only redraw when it is zero.
<p>Also, when responding to mouse motion events you should skip extra motion events in the input queue. Otherwise, if you try to process every motion event and redraw your scene there will be a noticable delay between mouse input and screen updates.
<p>It can be a good idea to put a print statement in your redraw and event loop function so you know exactly what messages are causing your scene to be redrawn, and when. </p>
</dd></dl>
<dl>
<dt>SwapBuffer calls and graphics pipe blocking
<dd>On systems with 3-D graphics hardware the SwapBuffers call is synchronized to the monitor's vertical retrace. Input to the OpenGL command queue may be blocked until the buffer swap has completed. Therefore, don't put more OpenGL calls immediately after SwapBuffers. Instead, put application computation instructions which can overlap with the buffer swap delay. </dd></dl><a name=Mesa>
<h3>3.8 Mesa-specific</h3>
Mesa is a free library which implements most of the OpenGL API in a compatible manner. Since it is a software library, performance depends a great deal on the host computer. There are several Mesa-specific features to be aware of which can effect performance.
<p>
<dl>
<dt>Double buffering
<dd>The X driver supports two back color buffer implementations: Pixmaps and XImages. The MESA_BACK_BUFFER environment variable controls which is used. Which of the two that's faster depends on the nature of your rendering. Experiment. </dd></dl>
<dl>
<dt>X Visuals
<dd>As described above, some X visuals can be rendered into more quickly than others. The <code>MESA_RGB_VISUAL</code> environment variable can be used to determine the quickest visual by experimentation. </dd></dl>
<dl>
<dt>Depth buffers
<dd>Mesa may use a 16 or 32-bit depth buffer as specified in the src/config.h configuration file. 16-bit depth buffers are faster but may not offer the precision needed for all applications. </dd></dl>
<dl>
<dt>Flat-shaded primitives
<dd>If one is drawing a number of flat-shaded primitives all of the same color the <code>glColor</code> command should be put before the <code>glBegin</code> call.
<p>Don't do this:
<pre>	glBegin(...);
glColor(...);
glVertex(...);
...
glEnd();
</pre>
<p>Do this:
<pre>	glColor(...);
glBegin(...);
glVertex(...);
...
glEnd();
</pre>
</dd></dl>
<dl>
<dt>glColor*() commands
<dd>The <code>glColor[34]ub[v]</code> are the fastest versions of the <code>glColor</code> command. </dd></dl>
<dl>
<dt>Avoid double precision valued functions
<dd>Mesa does all internal floating point computations in single precision floating point. API functions which take double precision floating point values must convert them to single precision. This can be expensive in the case of glVertex, glNormal, etc. </dd></dl><br><br><a name=Eval>
<h2>4. Evaluation and Tuning</h2>
To maximize the performance of an OpenGL applications one must be able to evaluate an application to learn what is limiting its speed. Because of the hardware involved it's not sufficient to use ordinary profiling tools. Several different aspects of the graphics system must be evaluated.
<p>Performance evaluation is a large subject and only the basics are covered here. For more information see "OpenGL on Silicon Graphics Systems".
<p><a name=Pipeline>
<h3>4.1 Pipeline tuning</h3>
The graphics system can be divided into three subsystems for the purpose of performance evaluation:
<ul>
    <li><strong>CPU subsystem</strong> - application code which drives the graphics subsystem
    <li><strong>Geometry subsystem</strong> - transformation of vertices, lighting, and clipping
    <li><strong>Rasterization subsystem</strong> - drawing filled polygons, line segments and per-pixel processing </li>
</ul>
At any given time, one of these stages will be the bottleneck. The bottleneck must be reduced to improve performance. The strategy is to isolate each subsystem in turn and evaluate changes in performance. For example, by decreasing the workload of the CPU subsystem one can determine if the CPU or graphics system is limiting performance.
<p>
<h4>4.1.1 CPU subsystem</h4>
To isosulate the CPU subsystem one must reduce the graphics workload while presevering the application's execution characteristics. A simple way to do this is to replace <code>glVertex()</code> and <code>glNormal</code> calls with <code>glColor</code> calls. If performance does not improve then the CPU stage is the bottleneck.
<p>
<h4>4.1.2 Geometry subsystem</h4>
To isoslate the geometry subsystem one wants to reduce the number of primitives processed, or reduce the transformation work per primitive while producing the same number of pixels during rasterization. This can be done by replacing many small polygons with fewer large ones or by simply disabling lighting or clipping. If performance increases then your application is bound by geometry/transformation speed.
<p>
<h4>4.1.3 Rasterization subsystem</h4>
A simple way to reduce the rasterization workload is to make your window smaller. Other ways to reduce rasterization work is to disable per-pixel processing such as texturing, blending, or depth testing. If performance increases, your program is <em>fill limited</em>.
<p>After bottlenecks have been identified the techniques outlined in section 3 can be applied. The process of identifying and reducing bottlenecks should be repeated until no further improvements can be made or your minimum performance threshold has been met.
<p><a name=Double>
<h3>4.2 Double buffering</h3>
For smooth animation one must maintain a high, constant frame rate. Double buffering has an important effect on this. Suppose your application needs to render at 60Hz but is only getting 30Hz. It's a mistake to think that you must reduce rendering time by 50% to achive 60Hz. The reason is the swap-buffers operation is synchronized to occur during the display's vertical retrace period (at 60Hz for example). It may be that your application is taking only a tiny bit too long to meet the 1/60 second rendering time limit for 60Hz.
<p>Measure the performance of rendering in single buffer mode to determine how far you really are from your target frame rate. <a name=Several>
<h3>4.3 Test on several implementations</h3>
The performance of OpenGL implementations varies a lot. One should measure performance and test OpenGL applications on several different systems to be sure there are no unexpected problems.
<p><br></p>
</a>
<img src ="http://www.cppblog.com/flashboy/aggbug/94364.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flashboy/" target="_blank">RedLight</a> 2009-08-25 14:05 <a href="http://www.cppblog.com/flashboy/archive/2009/08/25/94364.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Loading and displaying .X files without DirectX ----OpenGL渲染(转)</title><link>http://www.cppblog.com/flashboy/archive/2009/05/20/83507.html</link><dc:creator>RedLight</dc:creator><author>RedLight</author><pubDate>Wed, 20 May 2009 12:15:00 GMT</pubDate><guid>http://www.cppblog.com/flashboy/archive/2009/05/20/83507.html</guid><wfw:comment>http://www.cppblog.com/flashboy/comments/83507.html</wfw:comment><comments>http://www.cppblog.com/flashboy/archive/2009/05/20/83507.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/flashboy/comments/commentRss/83507.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flashboy/services/trackbacks/83507.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;&nbsp;<a href='http://www.cppblog.com/flashboy/archive/2009/05/20/83507.html'>阅读全文</a><img src ="http://www.cppblog.com/flashboy/aggbug/83507.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flashboy/" target="_blank">RedLight</a> 2009-05-20 20:15 <a href="http://www.cppblog.com/flashboy/archive/2009/05/20/83507.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>在面试一个游戏编程职位前,你需要知道的东西(转)</title><link>http://www.cppblog.com/flashboy/archive/2009/01/04/71169.html</link><dc:creator>RedLight</dc:creator><author>RedLight</author><pubDate>Sun, 04 Jan 2009 14:02:00 GMT</pubDate><guid>http://www.cppblog.com/flashboy/archive/2009/01/04/71169.html</guid><wfw:comment>http://www.cppblog.com/flashboy/comments/71169.html</wfw:comment><comments>http://www.cppblog.com/flashboy/archive/2009/01/04/71169.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/flashboy/comments/commentRss/71169.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flashboy/services/trackbacks/71169.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;&nbsp;<a href='http://www.cppblog.com/flashboy/archive/2009/01/04/71169.html'>阅读全文</a><img src ="http://www.cppblog.com/flashboy/aggbug/71169.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flashboy/" target="_blank">RedLight</a> 2009-01-04 22:02 <a href="http://www.cppblog.com/flashboy/archive/2009/01/04/71169.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用光照来表现立体感(转)</title><link>http://www.cppblog.com/flashboy/archive/2009/01/03/71060.html</link><dc:creator>RedLight</dc:creator><author>RedLight</author><pubDate>Sat, 03 Jan 2009 05:59:00 GMT</pubDate><guid>http://www.cppblog.com/flashboy/archive/2009/01/03/71060.html</guid><wfw:comment>http://www.cppblog.com/flashboy/comments/71060.html</wfw:comment><comments>http://www.cppblog.com/flashboy/archive/2009/01/03/71060.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flashboy/comments/commentRss/71060.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flashboy/services/trackbacks/71060.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;&nbsp;<a href='http://www.cppblog.com/flashboy/archive/2009/01/03/71060.html'>阅读全文</a><img src ="http://www.cppblog.com/flashboy/aggbug/71060.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flashboy/" target="_blank">RedLight</a> 2009-01-03 13:59 <a href="http://www.cppblog.com/flashboy/archive/2009/01/03/71060.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用混合来实现半透明效果 </title><link>http://www.cppblog.com/flashboy/archive/2009/01/03/71057.html</link><dc:creator>RedLight</dc:creator><author>RedLight</author><pubDate>Sat, 03 Jan 2009 05:45:00 GMT</pubDate><guid>http://www.cppblog.com/flashboy/archive/2009/01/03/71057.html</guid><wfw:comment>http://www.cppblog.com/flashboy/comments/71057.html</wfw:comment><comments>http://www.cppblog.com/flashboy/archive/2009/01/03/71057.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flashboy/comments/commentRss/71057.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flashboy/services/trackbacks/71057.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;&nbsp;<a href='http://www.cppblog.com/flashboy/archive/2009/01/03/71057.html'>阅读全文</a><img src ="http://www.cppblog.com/flashboy/aggbug/71057.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flashboy/" target="_blank">RedLight</a> 2009-01-03 13:45 <a href="http://www.cppblog.com/flashboy/archive/2009/01/03/71057.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>纹理的使用入门</title><link>http://www.cppblog.com/flashboy/archive/2009/01/03/71056.html</link><dc:creator>RedLight</dc:creator><author>RedLight</author><pubDate>Sat, 03 Jan 2009 05:43:00 GMT</pubDate><guid>http://www.cppblog.com/flashboy/archive/2009/01/03/71056.html</guid><wfw:comment>http://www.cppblog.com/flashboy/comments/71056.html</wfw:comment><comments>http://www.cppblog.com/flashboy/archive/2009/01/03/71056.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flashboy/comments/commentRss/71056.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flashboy/services/trackbacks/71056.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;&nbsp;<a href='http://www.cppblog.com/flashboy/archive/2009/01/03/71056.html'>阅读全文</a><img src ="http://www.cppblog.com/flashboy/aggbug/71056.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flashboy/" target="_blank">RedLight</a> 2009-01-03 13:43 <a href="http://www.cppblog.com/flashboy/archive/2009/01/03/71056.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>OpenGL片断测试</title><link>http://www.cppblog.com/flashboy/archive/2009/01/03/71055.html</link><dc:creator>RedLight</dc:creator><author>RedLight</author><pubDate>Sat, 03 Jan 2009 05:42:00 GMT</pubDate><guid>http://www.cppblog.com/flashboy/archive/2009/01/03/71055.html</guid><wfw:comment>http://www.cppblog.com/flashboy/comments/71055.html</wfw:comment><comments>http://www.cppblog.com/flashboy/archive/2009/01/03/71055.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flashboy/comments/commentRss/71055.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flashboy/services/trackbacks/71055.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;&nbsp;<a href='http://www.cppblog.com/flashboy/archive/2009/01/03/71055.html'>阅读全文</a><img src ="http://www.cppblog.com/flashboy/aggbug/71055.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flashboy/" target="_blank">RedLight</a> 2009-01-03 13:42 <a href="http://www.cppblog.com/flashboy/archive/2009/01/03/71055.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Opengl编程低级错误 (转载)</title><link>http://www.cppblog.com/flashboy/archive/2008/09/27/62913.html</link><dc:creator>RedLight</dc:creator><author>RedLight</author><pubDate>Sat, 27 Sep 2008 09:13:00 GMT</pubDate><guid>http://www.cppblog.com/flashboy/archive/2008/09/27/62913.html</guid><wfw:comment>http://www.cppblog.com/flashboy/comments/62913.html</wfw:comment><comments>http://www.cppblog.com/flashboy/archive/2008/09/27/62913.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/flashboy/comments/commentRss/62913.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flashboy/services/trackbacks/62913.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;&nbsp;<a href='http://www.cppblog.com/flashboy/archive/2008/09/27/62913.html'>阅读全文</a><img src ="http://www.cppblog.com/flashboy/aggbug/62913.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flashboy/" target="_blank">RedLight</a> 2008-09-27 17:13 <a href="http://www.cppblog.com/flashboy/archive/2008/09/27/62913.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>地形共享面的顶点法线的计算</title><link>http://www.cppblog.com/flashboy/archive/2008/09/19/62263.html</link><dc:creator>RedLight</dc:creator><author>RedLight</author><pubDate>Fri, 19 Sep 2008 03:45:00 GMT</pubDate><guid>http://www.cppblog.com/flashboy/archive/2008/09/19/62263.html</guid><wfw:comment>http://www.cppblog.com/flashboy/comments/62263.html</wfw:comment><comments>http://www.cppblog.com/flashboy/archive/2008/09/19/62263.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flashboy/comments/commentRss/62263.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flashboy/services/trackbacks/62263.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;&nbsp;<a href='http://www.cppblog.com/flashboy/archive/2008/09/19/62263.html'>阅读全文</a><img src ="http://www.cppblog.com/flashboy/aggbug/62263.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flashboy/" target="_blank">RedLight</a> 2008-09-19 11:45 <a href="http://www.cppblog.com/flashboy/archive/2008/09/19/62263.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>骨骼动画解释(转)</title><link>http://www.cppblog.com/flashboy/archive/2008/09/19/62260.html</link><dc:creator>RedLight</dc:creator><author>RedLight</author><pubDate>Fri, 19 Sep 2008 03:18:00 GMT</pubDate><guid>http://www.cppblog.com/flashboy/archive/2008/09/19/62260.html</guid><wfw:comment>http://www.cppblog.com/flashboy/comments/62260.html</wfw:comment><comments>http://www.cppblog.com/flashboy/archive/2008/09/19/62260.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flashboy/comments/commentRss/62260.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flashboy/services/trackbacks/62260.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;&nbsp;<a href='http://www.cppblog.com/flashboy/archive/2008/09/19/62260.html'>阅读全文</a><img src ="http://www.cppblog.com/flashboy/aggbug/62260.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flashboy/" target="_blank">RedLight</a> 2008-09-19 11:18 <a href="http://www.cppblog.com/flashboy/archive/2008/09/19/62260.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>教你如何使用maxscript调试器(转载)</title><link>http://www.cppblog.com/flashboy/archive/2008/09/19/62239.html</link><dc:creator>RedLight</dc:creator><author>RedLight</author><pubDate>Fri, 19 Sep 2008 00:33:00 GMT</pubDate><guid>http://www.cppblog.com/flashboy/archive/2008/09/19/62239.html</guid><wfw:comment>http://www.cppblog.com/flashboy/comments/62239.html</wfw:comment><comments>http://www.cppblog.com/flashboy/archive/2008/09/19/62239.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flashboy/comments/commentRss/62239.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flashboy/services/trackbacks/62239.html</trackback:ping><description><![CDATA[<span  style="font-family: simsun; font-size: 12px; ">&#160;<strong>教你如何使用maxscript调试器</strong><br>作者:李英江&#160;(转载)<br><p>&#160;</p><p>　　就我个人来说3ds max 8脚本调试器用得不多，在这里我讲一下脚本调试器的原理和简单的用法。脚本调试器只不过是一个max8的一个调试程序，它允许暂停3ds max主线程和其它线程，可以使用命令threads显示当前的3ds max所有线程及线程id，可以使用setThread 线程ID)切换当前调试的线程。一般情况调试器默认为调试最顶层(这个词不知道大家是否理解我的意思，就像是家里面的菜盘子，最顶层的盘子是最后放上去的，当然要拿开盘子只能从最上面一个开始)的线程，3ds max本身在实现一些操作时大量使用多线程，例如渲染时，就创建了一个新线程，关于线程和进程的区别，你可以找相应编程书籍。在3ds max8的目前调试器版本，还不支持鼠标选择某行设置断点（至少我没有找到这个功能，你找到了请告诉我），因此我现在断定，这是一个不实用的工具。但是3ds max8已经为我们提供了这个工具，当然是有用的了，只不过稍稍麻烦。<br>　　使用脚本调试器（我假设你已经写好了一个脚本），首先在你的脚本你想要暂停的地方设置断点，设置断点可以使用break()函数，也就是当脚本运行到这里时暂停程序。这样当你运行这个脚本时你将会在脚本调试器的"输出"编辑窗口中显示线程相应的内容。（我个人认为每运行一个脚本，3ds max就为这个脚本创建一个线程，所以才可以使用脚本调试器来跟踪脚本。谁能证实一下我的猜测 我的E-MAIL:&#160;<a href="mailto:liyingjang@21cn.com" style="color: rgb(254, 170, 85); text-decoration: none; ">liyingjang@21cn.com</a>）。当脚本执行到你设的断点处就停下来，这时你可以使用getVar ""和 setVar "" 来查看变量内容和临时设置变量内容。更方便查看变量内容的方法可以点击"监视"按钮，然后输入变量名按回车键即可显示你刚输入的变量名的值了。点击"运行"按钮可以使脚本运行到下一个断点处暂停，查看和设置变量的方法上一步所说的是一样。<br>　　总结一下：3ds max8的目前的调试器还很不完善（你也可以用丑陋来形容，希望以后的Max版本能把脚本调试器做得好点，好可惜我没有看到在MAX9调试器有一点点的变化）。脚本调试器虽不怎么样，但也不是一无是处，至少你可以少用print和format来查看运行时变量内容和调试脚本。<br>　　以下是我写了一个简单的调试脚本，运行后当i=10时会启动脚本调试器。<br>global ps=10<br>for i=0 to 100 do<br>(<br>&#160;&#160;&#160; ps = ps + i<br>　　if i==10 do break()<br>)<br>查看运行时断点的i变量，可以点击"监视"按钮，输入i 然后回车，就可以看到变量i的值为10。</p></span>
<img src ="http://www.cppblog.com/flashboy/aggbug/62239.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flashboy/" target="_blank">RedLight</a> 2008-09-19 08:33 <a href="http://www.cppblog.com/flashboy/archive/2008/09/19/62239.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>基于四叉树空间划分的地形实时渲染方法</title><link>http://www.cppblog.com/flashboy/archive/2008/05/11/49572.html</link><dc:creator>RedLight</dc:creator><author>RedLight</author><pubDate>Sun, 11 May 2008 13:54:00 GMT</pubDate><guid>http://www.cppblog.com/flashboy/archive/2008/05/11/49572.html</guid><wfw:comment>http://www.cppblog.com/flashboy/comments/49572.html</wfw:comment><comments>http://www.cppblog.com/flashboy/archive/2008/05/11/49572.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/flashboy/comments/commentRss/49572.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flashboy/services/trackbacks/49572.html</trackback:ping><description><![CDATA[地形是计算机图形的一个重要组成部分，而它又具有特殊的形态。地形往往覆盖面积极广，且精度要求很高，使得我们必须用许多多边形来描述。这样的特点使得我们不能像对待其他普通模型那样对待地形。要想实时地渲染地形，我们需要一些特殊的方法。
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px" align=left><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp; 地形渲染一直以来都是计算机图形学中一个重要的研究领域。并且在这一方面已经诞生了许多优秀的算法。其中包括基于体素的渲染方法，也有基于多边形的渲染方法。早期的游戏，如三角洲特种部队就是采用体素渲染法的成功例子。体素法类似光线追踪渲染，它从屏幕空间出发，找到地形与屏幕像素发出的射线交点，然后确定该像素的颜色。这种方法不依赖具体的图形硬件，整个渲染过程完全使用CPU处理，因此它不能使用现代硬件来加速，并且对于一个场景来说，往往不只是地形，还有其他使用多边形描述的物体，体素法渲染的图像很难与硬件渲染的多边形进行混合，因此这种方法现在用得极少。而多边形渲染方法则成为一种主流。选择多边形来描述和渲染地形有很多的理由和优点。最重要的是它能够很好地使用硬件加速，并且能够和其他多边形对象一起统一管理。</span></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px" align=left><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp; 已有大量优秀的基于多边形的地形渲染算法。比较经典的算法有</span><font style="FONT-SIZE: 10.5pt">M. Duchaineau等人提出ROAM算法。这个算法采用一棵三角二叉树来描述整个地形。一个地形在最初的层次上由两个较大的等腰直角三角形组成，这两个等腰直角三角形可以被不断地细分来展现地形的更多细节。每一次细分过程都向直角三角形的斜边的中点处增加一个由高程数据所描述的顶点，该点将所在的直角三角形一分为二，同时该算法也定义了一些规则来保证地形中不会因相邻两个三角形细节层次的不同而出现裂缝。这个算法已被许多游戏所采用。还有一类算法，通过将地形在X-Z投影面上不断地规则细分来得到不同的细节，这就是本文要介绍的四叉树空间划分算法。另外，最新提出的一个地形算法也不得不提，Hugues Hoppe在</font><cite style="FONT-STYLE: normal"><span style="FONT-SIZE: 10.5pt">2004年提出的几何裁剪图方法(Geometry Clipmaps)，算法使用了最新硬件所支持的顶点纹理来定义地形的外观，并且对于距离摄影机不同远近的地方采用不同的纹理层，最大限度地使用硬件加速了地形渲染的过程。这个方法听起来非常美妙，但它目前只被较少的硬件支持。因为顶点纹理是Shader Model 3.0才支持的功能，也就是说只有DirectX 9.0c级别的显卡才能支持这种算法。这对于某些有普及性要求的图形应用程序，尤其是对游戏来讲不是一件好的事情。因此大多数人现在还在使用经典的地形渲染方法。</span></cite></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px" align=left><cite style="FONT-STYLE: normal"><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp; 首先，基于四叉树的地形渲染方法使用高程数据作为数据源。且算法要求高程数据的大小必须为2<sup>n</sup>+1的正方形。所谓高程数据，即色彩范围在0-255的灰度图片，不同的灰度代表了不同的高度值。如果某高程数据指出这个高程数据最高处的Y坐标值是4000，那么在高程数据中一个值为255的像素点就表示这个点所代表的地形区域的高度是4000，同理如果该像素值是127那么就表示这个点所代表的地形区域的高度是4000&#215;(127/255)=2000。高程数据的每个像素都对应所渲染网格中的一个顶点。另外还有一个参数描述顶点与顶点之间的水平距离，以及一个描述最大高度的参数。因此地形的基本数据结构如下：</span></cite></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px" align=left><cite style="FONT-STYLE: normal"><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp; struct Terrain<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char **DEM; //一个描述高程数据的二维数组<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; float CellSpace;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; float HeightScale;&nbsp; <br>&nbsp;&nbsp;&nbsp; }</span></cite></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px" align=left><cite style="FONT-STYLE: normal"><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp; 其中，各变量的具体意义如下图所示：</span></cite></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px" align=center><cite style="FONT-STYLE: normal"><span style="FONT-SIZE: 10.5pt"><img src="http://www.graphixer.com.cn/ArtImages/TerrainQuad/Terrai1.jpg" border=0></span></cite></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px" align=left><cite style="FONT-STYLE: normal"><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp; 有了这些参数，我们可以很容易地由高程数据的参数值得到它所表述的多边形网格。得到这个网格之后，可以简单地把它放入顶点数组，并为之建立一个顶点索引，就可以传入硬件进行渲染了。然而，事情并不是这么简单。对于较小尺寸的高程数据(如129&#215;129)，这样做确实可行，但随着高程数据规模的增大，所需的顶点数和描述网格的三角形数会急剧膨胀。这个数值很快就会大到最新的显卡也无法接受。比如一个1025&#215;1025的高程数据，我们需要1025&#215;1025=1050625个顶点，以及1050625&#215;2=2101250个三角形。就算你的显卡每秒能够渲染1000万个三角形，你也只能得到不到5fps的渲染速度，况且你的场景可能还不只包括地形。因此我们必须想办法在不影响视觉效果的情况下缩减所渲染的三角形数量，另外还应该注意一次性将最多的数据预先传给硬件以节约带宽。</span></cite></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px" align=left><cite style="FONT-STYLE: normal"><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp; 这里要讲解的算法，目的就是在不影响或在视觉可以接受的范围内缩减所渲染三角形的数量，以达到实时渲染的要求。根据测试，本算法在漫游大小为1025*1025的地形时速度稳定在150fps以上(在nVidia Geforce 6200 + P4 1.6GHz的硬件上得到)。</span></cite></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px" align=left><cite style="FONT-STYLE: normal"><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp; 由于地形覆盖范围广，但它的投影在XZ平面上均匀分布(以下采用OpenGL中的右手坐标系，Y轴为竖直向上的坐标轴)，因此我们有必要考虑对地形进行空间划分。正是由于这样的均匀分布，给我们的划分过程带来了便利。我们不需要具体地去分割某个三角形，只要选择那些过顶点且和X或Z轴垂直的平面作为划分面即可。例如对于一个高程数据，我们可以以坐标原点作为地形的中心点，然后沿着X轴和Z轴依次展开来分布各个顶点。如下如所示。</span></cite></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px" align=center><span style="FONT-SIZE: 10.5pt"><img src="http://www.graphixer.com.cn/ArtImages/TerrainQuad/Terrai2.gif" border=0></span></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px" align=left><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp; 首先，我们可以选择X=0和Z=0这两个平面，将地形划分为等大的四个区域，然后对划分出来的四个子区域进行递归划分，每次划分都选择交于区域中心点并且互相垂直的两个平面作为划分面，直到每个子区域都只包含一个地形单元块（即两个三角形）而不能再划分为止。例如对于上图所示9*9大小的地形块，经过划分之后如下图所示：</span></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px" align=center><span style="FONT-SIZE: 10.5pt"><img src="http://www.graphixer.com.cn/ArtImages/TerrainQuad/Terrai3.gif" border=0></span></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px" align=left><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp; 由图可知，只有高程数据满足<cite style="FONT-STYLE: normal">大小2<sup>n</sup>+1的正方形这个条件，我们才可能对地形进行均匀划分。</cite>我们可以把划分结果用一棵树来表述，由于每次划分之后产生四个子节点，因此这棵树叫四叉树。那么，这棵树中应该存储那些信息呢？首先对于每个节点，应该指定这个节点所代表的地形的区域范围。并不是把地形网格中实际的顶点放入树中，而是要在树中说明这个节点覆盖了地形的那些区域。比如一个子节点应该有一个Center(X,Y)变量，指定这个节点的中心点所对应的顶点索引，或编号。为了方便起见，可以把地形中心点编号为(0,0)然后沿着坐标轴递增。此外还要有个变量指定这个节点到底覆盖了地形的多少个顶点。如下图所示。</span></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px" align=center><span style="FONT-SIZE: 10.5pt"><img src="http://www.graphixer.com.cn/ArtImages/TerrainQuad/Terrai4.gif" border=0></span></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px" align=left><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp; 我们目前的四叉树的数据结构如下：</span></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px" align=left><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp; <strong>struct</strong> QuadTreeNode<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>QuadTreeNode</strong> *Children[4];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>int</strong> CenterX,CenterY;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>int</strong> HalfRange;<br>&nbsp;&nbsp;&nbsp; }</span></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px" align=left><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp; 有了四叉树之后，如何利用它的优势呢？首先我们考虑简单的视见体裁剪(View Frustum Culling，以下简称VFC)。相信很多接触过基本图形优化的人都应该熟悉VFC，VFC的作用既是对那些明显位于可见平截头体之外的多边形在把它们传给显卡之前剔除掉。这个过程由CPU来完成。虽然简单，但它却非常有效。VFC过程如下：</span></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px" align=left><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp; 1.为每个节点计算包围球。包围球可以简单的以中心顶点为球心，最大坐标值点(节点所覆盖的所有顶点的最大X、Y、Z值作为此点的坐标值)到球心的距离为半径。</span></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px" align=left><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp; 2.根据当前的投影和变换矩阵计算此时可视平截头体的六个平面方程。这一步可以参考Azure的Blog上的一篇文章，这篇文章给出了VFC的具体代码。<a href="http://www.azure.com.cn/article.asp?id=155" target=_blank><font color=#0022cc>单击这里</font></a>。</span></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px" align=left><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp; 3.从树的根结点以深度优先的顺序遍历树。每次访问节点时，测试该节点包围球与视见体的相交情况。在下面的情况下，包围球与视见体相交：</span></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px" align=left><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1) 球心在六个平面所包围的凸状区域内部。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2) 球心在六个平面所包围的凸状区域外部，但球心到某个平面的距离小于半径。</span></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px" align=left><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp; 4.如果相交测试显示包围球和视见体存在交集，继续递归遍历此节点的4个子节点，如果此节点已经是叶节点，则这个节点应被绘制。如果不存在交集，放弃这个节点，对于这个节点的所有子节点不再递归检查。因为如果一个节点不可见，那么其子节点一定不可见。</span></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px" align=left><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp; 这样，我们剔除了那些不在视见体内的地形区域，节约了一些资源。但这还不够。在某些情况下，VFC可能还会指出整个地形都可见，在这种情况下，将这么多三角形都画出显然是不可取的。</span></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px" align=left><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp; 因此还要考虑地形的细节层次(LOD)。我们应该考虑到，地形不可能所有部分都一样平坦或陡峭。对于平坦的部分，我们用过多的三角形去描述是没有意义的。而对于起伏程度较大的区域，只有较多的三角形数量才不让人感到尖锐的棱角。再者，无论地形起伏程度如何，那些距离视点很远的区域，也没有必要花费太多的资源去渲染，毕竟它们投影到屏幕上的面积很小，对其进行简化也是必要的。</span></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px" align=left><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp; 既然我们要对起伏程度不同的区域采用不同的细节级别，我们首先必须找到一种描述地形起伏程度的量。与其说起伏程度，不如说是地形的某个顶点因为被简化后而产生的误差。要计算这个误差，我们先要了解地形是如何被简化的。</span></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px" align=left><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp; 考虑下图所示的地形块，它的渲染结果如下图右图所示。</span></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px" align=center><span style="FONT-SIZE: 10.5pt"><img src="http://www.graphixer.com.cn/ArtImages/TerrainQuad/Terrai6.gif" border=0></span></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px" align=left><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp; 现在如果要对所需渲染的三角形进行简化，我们可以考虑这个地形块每条边中间的顶点(下图左侧红色点)：</span></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px" align=center><span style="FONT-SIZE: 10.5pt"><img src="http://www.graphixer.com.cn/ArtImages/TerrainQuad/Terrai7.gif" border=0></span></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px" align=left><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp; 如果将这些红色的顶点剔除，我们可以得到上图右边所示的简化后的网格。误差就在这一步产生。由于红色的顶点被剔除后，原本由红色顶点所表示的地形高度现在变成了两侧黑色顶点插值后的高度。这个高度就是误差。如下图。</span></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px" align=center><span style="FONT-SIZE: 10.5pt"><img src="http://www.graphixer.com.cn/ArtImages/TerrainQuad/Terrai8.gif" border=0></span></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px" align=left><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp; 因此，对于每个节点，我们先计算这个节点所有边中点被删除后所造成的误差，分别记为&#916;H1, &#916;H2, &#916;H3, &#916;H4。如果这个节点包含子节点，递归计算子节点的误差，并把四个子节点的误差记为&#916;Hs1, &#916;Hs2, &#916;Hs3, &#916;Hs4。这个节点的误差就是这八个误差值中的最大值。由于这是一个递归的过程，因此应该把这个过程加到四叉树的生成过程中，并向四叉树的数据结构中加入一个误差变量。如下。</span></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px" align=left><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp; struct QuadTreeNode<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; QuadTreeNode *Children;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int CenterX,CenterY;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int HalfRange;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; float DeltaH;&nbsp; //节点误差值<br>&nbsp;&nbsp;&nbsp; }</span></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px" align=left><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp; 下面来看一下地形的具体渲染过程。</span></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px" align=left><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp; 首先，我们位于四叉树的根结点。我们此时考虑根结点的误差，如果这个误差小于一个阈值，直接使用根结点的中心点以及此节点的四个边角点作为顶点渲染一个三角扇形，这个三角扇形就是渲染出来的地形。但是更经常的情况下，根结点的误差值是很大的，因此算法认为要对根结点进行细分，以展现更多细节。于是对于根结点的每个子节点，重复这个步骤，即检查它的误差值是否大于阈值，如果大于，直接渲染这个节点，如果小于，递归细分节点。目前我们的算法伪代码如下。</span></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px" align=left><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp; procedure DrawTerrain(QuadTreeNode *node)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (node-&gt;DeltaH &gt; k)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (i=0;i&lt;4;i++)<br>&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; DrawTerrain(node-&gt;Children[i]);//递归划分<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; GraphicsAPI-&gt;DrawPrimitive(node);//以节点的中心点和四个边角点绘制三角扇形 <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; }</span></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px" align=left><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp; 这个伪代码在一个较高的层次上表述了算法的基本思想。然而我们还有许多问题要考虑。其一是目前我们仅仅考虑了地形的细节层次和地形表面起伏程度的关系，但还应该考虑地形块距离视点远近跟地形细节层次的关系。解决这个问题很简单，我们只需在伪代码的条件中加入距离这一因素即可。即把</span></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px" align=left><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (node-&gt;DeltaH &gt; k)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else ...</span></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px" align=left><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp; 改为：</span></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px" align=left><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (node-&gt;DeltaH / d &gt; k)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else ...</span></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px" align=left><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp; 其中d为节点中心点与视点之间的距离。而事实上，当细节程度与距离的平方成反比时，能够减少更多的三角形，而且视觉效果更好，只要阈值k设置得当，根本感觉不出地形因为视点的移动而发生几何形变。因此，我们最终的条件式为：</span></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px" align=left><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp; node-&gt;DeltaH / d<sup>2</sup> &gt; k</span></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px" align=left><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp; 还有一个很重要的问题，就是这个算法所产生的地形会因为节点之间细节层次的不同而产生裂缝。下图说明了裂缝的产生原因。</span></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px" align=center><span style="FONT-SIZE: 10.5pt"><img src="http://www.graphixer.com.cn/ArtImages/TerrainQuad/Terrai9.gif" border=0></span></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px" align=left><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp; 有两个方法可以解决这个问题，一个方法是删除左侧节点中产生裂缝的顶点，使两条边能够重合。另一种方法是人为地在右侧地形块中插入一条边，这条边连接中心点和造成裂缝的顶点，从而消除裂缝。在渲染地形时，可以采取下面的办法避免裂缝的产生：</span></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px" align=left><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp; 1.在预处理阶段，为所有顶点创建一个标记数组，标记以该顶点为中心点的节点在某一帧是否被细分。如果被细分则标记为1，否则标记0。</span></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px" align=left><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp; 2.从根节点开始，以广度优先的顺序遍历四叉树，使用之前提出的条件式判断节点是否需要分割。如果公式表明需要分割，并且与节点相邻的四个节点的中心点都被标记为1，那么把这个节点及其四个子节点的标记设为1，并递归细分这个节点。否则，将这个节点的标记设为1，把这个节点的四个子节点的标记设为0，然后采用下面的方法绘制这个地形块：</span></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px" align=left><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1)将节点的中心顶点和四个边角点添加到即将绘制的三角扇形列表中。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2)依次检查与四条边相邻的节点的标记数组，如果相应的标记为1，那么将该点添加到三角扇形的顶点列表中，否则跳过该点。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3)绘制三角扇形。</span></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px" align=left><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp; 我们最终的伪代码如下。</span></p>
<div align=center>
<table id=table1 width="72%" border=1>
    <tbody>
        <tr>
            <td>
            <p style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; LINE-HEIGHT: 18px"><font face="Courier New"><strong></strong><span style="FONT-SIZE: 10.5pt"><strong>bool</strong> IsNodeInFrustum(QuadTreeNode *node)</span></font></p>
            <p style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; LINE-HEIGHT: 18px"><font face="Courier New"><span style="FONT-SIZE: 10.5pt">{</span></font></p>
            <p style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; LINE-HEIGHT: 18px"><font face="Courier New"><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp; <strong>return</strong> (node-&gt;BoudingSphere in frustum);</span></font></p>
            <p style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; LINE-HEIGHT: 18px"><font face="Courier New"><span style="FONT-SIZE: 10.5pt">}</span></font></p>
            <p style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; LINE-HEIGHT: 18px"><font face="Courier New"><strong></strong><span style="FONT-SIZE: 10.5pt"><strong>bool</strong> NeighbourIsValid(QuadTreeNode *node)</span></font></p>
            <p style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; LINE-HEIGHT: 18px"><font face="Courier New"><span style="FONT-SIZE: 10.5pt">{</span></font></p>
            <p style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; LINE-HEIGHT: 18px"><font face="Courier New"><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp; <strong>return</strong> (all four neighbours of node are identified as 1)</span></font></p>
            <p style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; LINE-HEIGHT: 18px"><font face="Courier New"><span style="FONT-SIZE: 10.5pt">}</span></font></p>
            <p style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; LINE-HEIGHT: 18px"><font face="Courier New"><span style="FONT-SIZE: 10.5pt"><strong>void</strong> RenderTerrain()</span></font></p>
            <p style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; LINE-HEIGHT: 18px"><font face="Courier New"><span style="FONT-SIZE: 10.5pt">{</span></font></p>
            <p style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; LINE-HEIGHT: 18px"><font face="Courier New"><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp; list&lt;QuadTreeNode *&gt;next,current,draw;</span></font></p>
            <p style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; LINE-HEIGHT: 18px"><font face="Courier New"><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp; <strong>int</strong> level =0;<br>&nbsp;&nbsp; current.push_back(root);<br>&nbsp;&nbsp; <strong>while</strong> (current.size()!=0)</span></font></p>
            <p style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; LINE-HEIGHT: 18px"><font face="Courier New"><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp; {</span></font></p>
            <p style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; LINE-HEIGHT: 18px"><font face="Courier New"><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>for</strong> <strong>each</strong> thisNode in current</span></font></p>
            <p style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; LINE-HEIGHT: 18px"><font face="Courier New"><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>if</strong> (!IsNodeInFrustum(thisNode))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>continue</strong>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>if</strong> (level == MaxResolution)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; draw.push_back(thisNode);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>else</strong></span></font></p>
            <p style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; LINE-HEIGHT: 18px"><font face="Courier New"><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>if</strong> (thisNode-&gt;DeltaH/(distance*distance) &gt; k</span></font></p>
            <p style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; LINE-HEIGHT: 18px"><font face="Courier New"><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;&amp; NeighbourIsValid(thisNode) )</span></font></p>
            <p style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; LINE-HEIGHT: 18px"><font face="Courier New"><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</span></font></p>
            <p style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; LINE-HEIGHT: 18px"><font face="Courier New"><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SetFlag(thisNode,1); </span></font></p>
            <p style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; LINE-HEIGHT: 18px"><font face="Courier New"><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>for</strong> j= 1 <strong>to</strong> 4</span></font></p>
            <p style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; LINE-HEIGHT: 18px"><font face="Courier New"><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</span></font></p>
            <p style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; LINE-HEIGHT: 18px"><font face="Courier New"><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; next.push_back(thisNode-&gt;Children[j]);</span></font></p>
            <p style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; LINE-HEIGHT: 18px"><font face="Courier New"><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SetFlag(thisNode-&gt;Children[j],1)</span></font></p>
            <p style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; LINE-HEIGHT: 18px"><font face="Courier New"><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } </span></font></p>
            <p style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; LINE-HEIGHT: 18px"><font face="Courier New"><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></font></p>
            <p style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; LINE-HEIGHT: 18px"><font face="Courier New"><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>else</strong></span></font></p>
            <p style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; LINE-HEIGHT: 18px"><font face="Courier New"><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</span></font></p>
            <p style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; LINE-HEIGHT: 18px"><font face="Courier New"><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SetFlag(thisNode,1);&nbsp; </span></font></p>
            <p style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; LINE-HEIGHT: 18px"><font face="Courier New"><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>for</strong> j= 1 <strong>to</strong> 4</span></font></p>
            <p style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; LINE-HEIGHT: 18px"><font face="Courier New"><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</span></font></p>
            <p style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; LINE-HEIGHT: 18px"><font face="Courier New"><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; draw.push_back(thisNode-&gt;Children[j]);</span></font></p>
            <p style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; LINE-HEIGHT: 18px"><font face="Courier New"><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SetFlag(thisNode-&gt;Children[j],0);</span></font></p>
            <p style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; LINE-HEIGHT: 18px"><font face="Courier New"><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } </span></font></p>
            <p style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; LINE-HEIGHT: 18px"><font face="Courier New"><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp; </span></font></p>
            <p style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; LINE-HEIGHT: 18px"><font face="Courier New"><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></font></p>
            <p style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; LINE-HEIGHT: 18px"><font face="Courier New"><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SwapList(current,next);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; next.clear();</span></font></p>
            <p style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; LINE-HEIGHT: 18px"><font face="Courier New"><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; level++;</span></font></p>
            <p style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; LINE-HEIGHT: 18px"><font face="Courier New"><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp; }</span></font></p>
            <p style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; LINE-HEIGHT: 18px"><font face="Courier New"><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp; GraphicsAPI-&gt;DrawPrimitives(draw);&nbsp;&nbsp; </span></font></p>
            <p style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; LINE-HEIGHT: 18px"><font face="Courier New"><span style="FONT-SIZE: 10.5pt">}</span></font></p>
            </td>
        </tr>
    </tbody>
</table>
</div>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px" align=left><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp; 另外，一个重要的优化是利用硬件的缓冲区或顶点数组(对于不支持顶点缓冲的硬件而言)。因为地形无论怎样简化，顶点数据总是固定不变的。我们在每一帧动态产生的仅仅是顶点索引，因此我们有必要实现将地形的所有顶点数据输入到顶点缓冲中，然后在渲染时一次性将所有的索引传给显卡，以提高速度。实验表明，使用顶点缓冲比直接使用glBegin/glEnd绘制图形要快5倍以上。</span></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px" align=left><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp; 以上讲述了如何做到实时地渲染大型地形。主要应用了LOD和VFC两种手段来精简三角形数量。然而VFC只能剔除不在视见体内的图形，而对于在视见体内但被其他更近的物体遮挡的情况却无能为力。如果要实现地形的自遮挡剔除，地平线算法是一个好的选择。然而当你的场景不仅仅是包含地形时，地平线算法也只能处理地形的自遮挡情况。因为地平线算法只对2.5D的地图(即在XZ平面上无重合投影的场景)有效。对于完全3D场景，地平线并不能很好的工作。所以当你在引擎中使用地形时，可以考虑将地形分块后放入场景的管理树中，如BSP或Octree等。然后根据引擎的性质使用入口(Portal)、PVS或者遮挡测试(Occlusion Culling)等方法进行遮挡剔除。值得强调的是，遮挡测试是一个非常灵活的实时的剔除算法，且无需任何预计算过程。但要想有效的实现它并不是一件容易的事。我曾将地形分块后使用遮挡剔除来完成地形的自遮挡，但是渲染速度不但没有提升，反而有轻微的下降。因此如果要使用遮挡剔除的话必须和引擎结合起来统一进行遮挡测试，才有可能提高效率。</span></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px" align=left><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp; 现在你应该了解了基本的地形实时渲染方法。要想让地形的外观更加真实，我们还需要更多的工作。我们需要为地形加上纹理贴图和光照。首先考虑地形的光照。由于地形的多边形网格是实时产生的，它会随着视点的移动而变化，因此如果你直接使用OpenGL内置的顶点光照，你会得到极度不稳定的光照效果。你会看到地形表面会因为你的移动而不断跳动。因此我们必须使用其他的光照方法来避免这个问题。我们想到了光照贴图。光照贴图是一个游戏中常用的光照技术。它是一个覆盖了场景中所有多边形的贴图。通过给贴图赋值，我们可以得到多边形表面复杂的光照效果。使用好的算法计算出来的光照贴图可以模拟极度逼真的光影效果。它给我们带来的视觉享受远远地超过了OpenGL的内置光照。有关光照贴图的计算可以参考我翻译的一篇文章：</span><span style="FONT-SIZE: 10.5pt"><a href="http://program.stedu.net/showArticle.asp?index=68"><font color=#0022cc>辐射度算法(Radiosity)</font></a></span></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px" align=center><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp; <img src="http://www.graphixer.com.cn/ArtImages/TerrainQuad/Terrai11.jpg" border=0></span></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px" align=left><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp; 你可以简单地为地形覆盖上单一的纹理，这看起来些许增加了地形的真实性：</span></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px" align=center><span style="FONT-SIZE: 10.5pt"><img src="http://www.graphixer.com.cn/ArtImages/TerrainQuad/Terrai12.jpg" border=0></span></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px"><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp; 在上图中，我们创建了一个地形，并运用了一个重复的纹理。这个过程让地形的无论哪一个区域看起来都是一样的（例如都是草地）。这显然不太真实，也过于乏味。或许你会创建了一幅超大的图片，以拉伸覆盖的方式映射到地形表面。这样做的后果是内存开销过于庞大，这样做也很会受到硬件的限制。因此我们应该使用一种更好的纹理贴图方式，纹理索引贴图。</span></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px"><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp; 纹理索引贴图对三个可重复的纹理进行索引贴图。所谓索引贴图，就是对三个可重复纹理进行索引，以决定地形的哪些区域需要使用哪些纹理的混合来贴图。因为对于任意的贴图，都由一组包含3个颜色通道（即R、G、B）的像素组成。用于索引的贴图的像素并不表示地形的某个区域的具体颜色，而是表示地形的某个区域用何种具体的纹理贴图。因为具体的纹理细节存储在这三个可重复的纹理中，因此索引贴图的贴图方式也为拉伸到地形表面，但它的分辨率可以大大降低。</span></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px"><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp; 纹理索引贴图的工作方式如下：对于地形投影到屏幕上的像素，查找该像素所映射到索引贴图上的像素。然后根据这一像素R、G、B分量的不同，决定R、G、B分量所代表的具体纹理贴图的混合因子。根据这个混合因子混合三个可重复贴图后，将混合得到的最终颜色值输出到屏幕上。</span></p>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px"><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp; 例如，令索引贴图的R分量代表沙滩的纹理，G分量代表草地，B分量代表岩石。如果索引贴图上一个像素的值是(0,255,0)，即绿色，则这个像素所对应的地形区域的具体纹理就为草地。如果该像素颜色值是(127,127,0)，即黄色，则该像素所对应的地形区域的纹理为草地和沙滩的混合，看起来既有草，又有沙。又如下图显示了一个样本索引贴图，以及使用该贴图索引纹理之后的渲染效果。</span></p>
<div align=center>
<table id=table2 width="20%" border=0>
    <tbody>
        <tr>
            <td align=middle>
            <p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px"><span style="FONT-SIZE: 10.5pt"><img height=238 src="http://www.graphixer.com.cn/ArtImages/TerrainQuad/Terrai13.jpg" width=236 border=0></span></p>
            </td>
            <td align=middle>
            <p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px"><span style="FONT-SIZE: 10.5pt"><img height=242 src="http://www.graphixer.com.cn/ArtImages/TerrainQuad/Terrai14.jpg" width=327 border=0></span></p>
            </td>
        </tr>
        <tr>
            <td align=middle>
            <p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px"><span style="FONT-SIZE: 10.5pt">索引贴图（R=沙滩，G=草地,B=岩石）</span></p>
            </td>
            <td align=middle>
            <p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px"><span style="FONT-SIZE: 10.5pt">渲染效果</span></p>
            </td>
        </tr>
    </tbody>
</table>
</div>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px"><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp; 原理很简单，下面讲解一下具体的实现过程。首先，我们准备4个纹理，其中1个纹理索引贴图，它将被拉伸覆盖整个地形，然后3张细节贴图，并将它们绑定到相应的纹理通道上。然后使用Vertex Shader为每个顶点自动计算索引贴图的纹理坐标，在Fragment Shader里，对索引贴图进行纹理查找，使用查找得到的颜色值的RGB颜色信息混合3张细节贴图，得到当前像素的颜色。最后还应该把这个颜色和光照贴图中的值相乘，得到最终的结果。下面是相关的Shader代码，使用GLSL编写。</span></p>
<div align=center>
<table id=table3 width="68%" border=1>
    <tbody>
        <tr>
            <td><font face="Courier New"><strong><span style="FONT-SIZE: 10.5pt">Vertex Shader:</span></strong></font>
            <p><font face="Courier New"><span style="FONT-SIZE: 10.5pt">uniform float TexInc;&nbsp;&nbsp; //纹理缩放值,用于查找索引纹理<br>void main()<br>{<br>&nbsp; gl_TexCoord[6] = gl_Vertex;<br>&nbsp; gl_TexCoord[0] = gl_MultiTexCoord0;<br>&nbsp; gl_TexCoord[2] = TexInc*vec4(gl_Vertex.xz,0.0,0.0);<br>&nbsp; gl_Position = ftransform();<br>}</span></font></p>
            </td>
        </tr>
        <tr>
            <td><font face="Courier New"><strong><span style="FONT-SIZE: 10.5pt">Fragment Shader:</span></strong></font>
            <p><font face="Courier New"><span style="FONT-SIZE: 10.5pt">uniform sampler2D IndexMap;<br>uniform sampler2D LightMap;<br>uniform sampler2D texR,texG,texB,texA;<br>void main()<br>{<br>&nbsp; vec4 idx,lm,r,g,b,color;<br>&nbsp; idx = texture2D(IndexMap,gl_TexCoord[0].xy); //索引值<br>&nbsp; lm = texture2D(LightMap,gl_TexCoord[0].xy);&nbsp; //光照度<br>&nbsp; r = texture2D(texR,gl_TexCoord[2].xy);&nbsp;&nbsp; //R通道纹理<br>&nbsp; g = texture2D(texG,gl_TexCoord[2].xy);&nbsp;&nbsp; //G通道纹理<br>&nbsp; b = texture2D(texB,gl_TexCoord[2].xy);&nbsp;&nbsp; //B通道纹理 <br>&nbsp; color = lm*(idx.x*r + idx.y*g+idx.z*b);&nbsp; //混合颜色<br>&nbsp; gl_FragColor = color;<br>}</span></font></p>
            </td>
        </tr>
    </tbody>
</table>
</div>
<p style="MARGIN-TOP: 9px; MARGIN-BOTTOM: 9px; LINE-HEIGHT: 18px"><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp; 最后，如果你对本文有不解之处，欢迎和我共同讨论。</span></p>
<img src ="http://www.cppblog.com/flashboy/aggbug/49572.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flashboy/" target="_blank">RedLight</a> 2008-05-11 21:54 <a href="http://www.cppblog.com/flashboy/archive/2008/05/11/49572.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>X文件的使用（完整）</title><link>http://www.cppblog.com/flashboy/archive/2008/05/11/49571.html</link><dc:creator>RedLight</dc:creator><author>RedLight</author><pubDate>Sun, 11 May 2008 13:32:00 GMT</pubDate><guid>http://www.cppblog.com/flashboy/archive/2008/05/11/49571.html</guid><wfw:comment>http://www.cppblog.com/flashboy/comments/49571.html</wfw:comment><comments>http://www.cppblog.com/flashboy/archive/2008/05/11/49571.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flashboy/comments/commentRss/49571.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flashboy/services/trackbacks/49571.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;&nbsp;<a href='http://www.cppblog.com/flashboy/archive/2008/05/11/49571.html'>阅读全文</a><img src ="http://www.cppblog.com/flashboy/aggbug/49571.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flashboy/" target="_blank">RedLight</a> 2008-05-11 21:32 <a href="http://www.cppblog.com/flashboy/archive/2008/05/11/49571.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>投影矩阵的实现以及如何从投影矩阵中获取各视裁体平面(教程)</title><link>http://www.cppblog.com/flashboy/archive/2008/04/16/47292.html</link><dc:creator>RedLight</dc:creator><author>RedLight</author><pubDate>Wed, 16 Apr 2008 09:49:00 GMT</pubDate><guid>http://www.cppblog.com/flashboy/archive/2008/04/16/47292.html</guid><wfw:comment>http://www.cppblog.com/flashboy/comments/47292.html</wfw:comment><comments>http://www.cppblog.com/flashboy/archive/2008/04/16/47292.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flashboy/comments/commentRss/47292.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flashboy/services/trackbacks/47292.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;&nbsp;<a href='http://www.cppblog.com/flashboy/archive/2008/04/16/47292.html'>阅读全文</a><img src ="http://www.cppblog.com/flashboy/aggbug/47292.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flashboy/" target="_blank">RedLight</a> 2008-04-16 17:49 <a href="http://www.cppblog.com/flashboy/archive/2008/04/16/47292.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>DirectX 9的坐标系统变换 </title><link>http://www.cppblog.com/flashboy/archive/2008/04/16/47289.html</link><dc:creator>RedLight</dc:creator><author>RedLight</author><pubDate>Wed, 16 Apr 2008 09:45:00 GMT</pubDate><guid>http://www.cppblog.com/flashboy/archive/2008/04/16/47289.html</guid><wfw:comment>http://www.cppblog.com/flashboy/comments/47289.html</wfw:comment><comments>http://www.cppblog.com/flashboy/archive/2008/04/16/47289.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flashboy/comments/commentRss/47289.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flashboy/services/trackbacks/47289.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;&nbsp;<a href='http://www.cppblog.com/flashboy/archive/2008/04/16/47289.html'>阅读全文</a><img src ="http://www.cppblog.com/flashboy/aggbug/47289.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flashboy/" target="_blank">RedLight</a> 2008-04-16 17:45 <a href="http://www.cppblog.com/flashboy/archive/2008/04/16/47289.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Direct3D中实现图元的鼠标拾取</title><link>http://www.cppblog.com/flashboy/archive/2008/04/16/47288.html</link><dc:creator>RedLight</dc:creator><author>RedLight</author><pubDate>Wed, 16 Apr 2008 09:44:00 GMT</pubDate><guid>http://www.cppblog.com/flashboy/archive/2008/04/16/47288.html</guid><wfw:comment>http://www.cppblog.com/flashboy/comments/47288.html</wfw:comment><comments>http://www.cppblog.com/flashboy/archive/2008/04/16/47288.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flashboy/comments/commentRss/47288.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flashboy/services/trackbacks/47288.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;&nbsp;<a href='http://www.cppblog.com/flashboy/archive/2008/04/16/47288.html'>阅读全文</a><img src ="http://www.cppblog.com/flashboy/aggbug/47288.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flashboy/" target="_blank">RedLight</a> 2008-04-16 17:44 <a href="http://www.cppblog.com/flashboy/archive/2008/04/16/47288.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>