﻿<?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++博客-&lt;font size=5&gt;李锦俊的blog&lt;/font&gt; 本Blog永久域名:www.lijinjun.com-随笔分类-数学、几何和图形学</title><link>http://www.cppblog.com/mybios/category/3034.html</link><description>游戏开发 C++ DirectX 数学 计算机图形学 SQL Server
&lt;BR&gt;
&lt;BR&gt;
&lt;a href="http://www.lijinjun.com" style="font-size:14px;color:#FFFFFF"&gt;返回我的Blog首页&lt;/a&gt;</description><language>zh-cn</language><lastBuildDate>Sun, 26 Jul 2009 02:32:15 GMT</lastBuildDate><pubDate>Sun, 26 Jul 2009 02:32:15 GMT</pubDate><ttl>60</ttl><item><title>【转贴】Ogre的官僚主义批判</title><link>http://www.cppblog.com/mybios/archive/2006/11/19/15418.html</link><dc:creator>李锦俊</dc:creator><author>李锦俊</author><pubDate>Sun, 19 Nov 2006 00:40:00 GMT</pubDate><guid>http://www.cppblog.com/mybios/archive/2006/11/19/15418.html</guid><wfw:comment>http://www.cppblog.com/mybios/comments/15418.html</wfw:comment><comments>http://www.cppblog.com/mybios/archive/2006/11/19/15418.html#Feedback</comments><slash:comments>6</slash:comments><wfw:commentRss>http://www.cppblog.com/mybios/comments/commentRss/15418.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mybios/services/trackbacks/15418.html</trackback:ping><description><![CDATA[
		<div class="postbody">
				<p>Ogre的Manager真是多啊,就象个官僚公司,干事的没几个,一堆的Manager,个个丫还都是Singleton.说自己是Singleton却又public构造函数,让人来new它.在第1次被new的时候,偷偷地将this赋给静态成员ms_Singleton,第2次new它就要assert喊冤了,当然这都是叫Singleton的模板基类的那个构造函数干的(典型的小人行径,见不得光).Manager一多,本来不复杂的事,也变得复杂了.</p>
				<p>1.LogManager<br />a.全局单体LogManager组合Log指针Map集合,控制有多个Log对象,1个Log对象表达对一个Log文件的输入(老一套).<br />b.同时LogManager维护1个当前默认主事的Log对象mDefaultLog,以展现Log对象功能而隐藏自己实际是个Manager的身份(邀功自赏,其心可诛).<br />c.维护一个监控者接口LogListener列表,以履行作为一个Manager受外部监督的职责(实为推清责任).</p>
				<p>2.DynLibManager<br />a.全局单体DynLibManager组合DynLib*指针Map集合,控制多个DynLib对象,1个Dynlib表达对动态库的装载和卸载(典型的官僚主义,人浮于事,这点屁事都要有Manager).</p>
				<p>3.ResourceGroupManager<br />  a.全局单体ResourceGroupManager组合ResourceManager指针Map?Manager的Manager,靠,好大的官)<br />  b.一上任就安插了个亲信Manager,建立个叫General的ResourceManager.</p>
				<p>4.SceneManagerEnumerator<br />  a.连它都是系统单体,一个迭代器,小人得志啊,不说了,它可管着一堆SceneManager,惹不起.<br />  b.一开始就安排了什么事都作不了的SceneManager(基类)作默认SceneManager,大事小事都是它,室内室外一手包.</p>
				<p>5.一堆中层干部,一堆的MaterialManager,MeshManager,ParticleSystemManager,SkeletonManager......,都是Singleton,有的还是ResourceManager的门生,一开始都干了2件事.<br />a.定名分:向ResourceGroupManager注册说自己是某方面的Manager,_registerResourceManager.<br />b.要资源:向ResourceGroupManager要求控制对某种脚本的解析_registerScriptLoader(就是瓜分资源).<br />c.有个OverlayManager,ArchiveManager最牛,手下还有几个工厂给它干活,无非是自己作不了,又推给下级.</p>
				<p>6.一堆插件外劳,开始加载,这些外劳到系统中来当然要注册.</p>
				<p>7.透明实体mTransparentObjects按z序排序,不透明实体按material分组.</p>
				<p>8.我一开始认为Materail是个很聪明的概念,后来才发现上当受骗了,它管理了mTechniques数组,而Techniche管理了mPasses数组(Multipass render),最后实质的内容在Pass类中,Pass类表达了一个渲染的环境.由于这个官僚系统实在太官僚,所以按对象每次渲染是不可接受的(跨部门合作太多,相互扯皮),渲染状态切换频繁,效率极低,透明渲染z序混乱.所以需要按Materail分组和z轴分序,而RenderSystem的最重要方法接受RenderOperation参数.</p>
				<p>9.Pass和RenderOperation是两位最能干的劳动人民,都是受人管制的对象,整个系统的灵魂和核心,Pass封装了最重要的渲染状态(即进行glDrawXXX时管线所处的状态,着色系统),RenderOperation封装了带纹理几何渲染操作(glDrawElements几何系统),两者构成一个完整的渲染.即,先,调用SceneManager的setPass(在其中其仍将调用RenderSystem以设置管线状态),再,调用RenderSystem的_render.</p>
				<p>10.SceneManager的虚方法_findVisibleObjects是场景管理的核心,由它将Renderable分组,分透明和不透明分送入不同的渲染队列,Renderable组合RenderOperation和Pass(但出头的都是Materail),然后SceneManager对之排序,整理,送入RenderSystem渲染.</p>
				<p>11.SceneManager的_renderScene是基本渲染核心,它完成若干任务<br />   a.帧动画驱动_applySceneAnimations <br />   b.场景图更新_updateSceneGraph<br />   c.渲染队列更新,_findVisibleObjects更新主场景,_queueOverlaysForRendering更新上层主界面,_queueSkiesForRendering更新天空盒,最后_renderVisibleObjects.</p>
				<p>12.渲染队列RenderQueue,维护std::map&lt;RenderQueueGroupID, RenderQueueGroup*&gt;对象,以表达其内部的多个方面(世界,前景,背景)的渲染分队列.RenderQueueGroup再往下分std::map&lt;ushort, RenderPriorityGroup*, std::less&lt;ushort&gt; &gt;,以表达其(如前景)内部按ushort优先级维护渲染分队列.RenderPriorityGroup维护一个mTransparentPasses,若干个SolidRenderablePassMap.</p>
		</div>
<img src ="http://www.cppblog.com/mybios/aggbug/15418.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mybios/" target="_blank">李锦俊</a> 2006-11-19 08:40 <a href="http://www.cppblog.com/mybios/archive/2006/11/19/15418.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>【转贴】使用非2次方幂的图像纹理的问题</title><link>http://www.cppblog.com/mybios/archive/2006/11/19/15417.html</link><dc:creator>李锦俊</dc:creator><author>李锦俊</author><pubDate>Sun, 19 Nov 2006 00:26:00 GMT</pubDate><guid>http://www.cppblog.com/mybios/archive/2006/11/19/15417.html</guid><wfw:comment>http://www.cppblog.com/mybios/comments/15417.html</wfw:comment><comments>http://www.cppblog.com/mybios/archive/2006/11/19/15417.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/mybios/comments/commentRss/15417.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mybios/services/trackbacks/15417.html</trackback:ping><description><![CDATA[
		<div class="postbody">
				<p>图像使用2次方是很讨厌的问题,不在技术难度上,而在技术妥协上.实际上要求美工作出2次方纹理,并且整张图的空间利用率要高,是很难的.<br />当有些策划丢过来奇奇怪怪的尺寸的图像,我都要吐血,当知道2n次方限制后,他们把图像尺寸扩大一下,再丢给我空余大量空白的2n次方纹理,再吐血.</p>
				<p>玻璃渣资源里标准的2n次方人物纹理,图像挤的满满的,每个身体部位纹理还是一个矩形,利用率之高不得不令人佩服,然而这对美工要求是极高的.<br />1024*768的图像是要拆成4*3的256*256图像的,而不是一整大张纹理,因为768不是2的n次方,更不要扩大为1024*1024,加大25%的内存.<br />还有Wow里Loading界面的图像都压成512*512,因为Loading图像模糊一点不要紧,很简单却都是很重要的细节.<br />所以当Wow运行在我Geforce2的显卡上时,我觉的很cool.</p>
				<p>Nvidia的驱动程序也很讨厌,实际Geforce6显卡才支持non power of two texture,Geforce 5200级的显卡,在硬件能力上不足以支持NPOT,但是最新的驱动程序使用了软件模式进行模拟支持,而软件模拟根本毫无实用价值,渲染变得超级缓慢,因为驱动程序每次纹理渲染都会很聪明地把非2次方尺寸图像自动Scale到2次方尺寸,对于一个800*600的图像,驱动程序在这个步骤就吃光了CPU.<br />所以总有些人喊着为什么OpenGL没有软件渲染支持,DX很体贴都有(实际上DX也没有,比如那个所谓的8层纹理),而我认为如果软件渲染能解决问题,那要硬件作什么!不能解决问题的方案我们支持它作什么!</p>
				<p>// OpenGL动态执行2n次方图像限制<br />inline int next_p2(int a)<br />{<br /> int rval=1;<br /> while(rval&lt;a) rval&lt;&lt;=1;<br /> return rval;<br />}</p>
				<p>int nWidthPowerOfTwo = next_p2(tex.nWidth);<br />int nHeightPowerOfTwo = next_p2(tex.nHeight);</p>
				<p>if(tex.nWidth == nWidthPowerOfTwo  &amp;&amp;  tex.nHeight == nHeightPowerOfTwo)<br />{ <br /> glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, nWidthPowerOfTwo, nHeightPowerOfTwo, 0, ilGetInteger(IL_IMAGE_FORMAT), GL_UNSIGNED_BYTE, ilGetData());<br /> tex.fScaleX = tex.fScaleY = 1.0f;<br />} <br />else<br />{ <br /> glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, nWidthPowerOfTwo, nHeightPowerOfTwo, 0, ilGetInteger(IL_IMAGE_FORMAT), GL_UNSIGNED_BYTE, NULL);<br /> glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, tex.nWidth, tex.nHeight, ilGetInteger(IL_IMAGE_FORMAT), GL_UNSIGNED_BYTE, ilGetData());<br /> tex.fScaleX = (float)tex.nWidth / (float)nWidthPowerOfTwo;<br /> tex.fScaleY = (float)tex.nHeight/ (float)nHeightPowerOfTwo;<br />}<br /><br />然而对这个问题,正确的解决方案是事先规划,强制执行,be clever.</p>
		</div>
<img src ="http://www.cppblog.com/mybios/aggbug/15417.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mybios/" target="_blank">李锦俊</a> 2006-11-19 08:26 <a href="http://www.cppblog.com/mybios/archive/2006/11/19/15417.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>【转贴】高级碰撞检测技术</title><link>http://www.cppblog.com/mybios/archive/2006/11/19/15408.html</link><dc:creator>李锦俊</dc:creator><author>李锦俊</author><pubDate>Sat, 18 Nov 2006 17:46:00 GMT</pubDate><guid>http://www.cppblog.com/mybios/archive/2006/11/19/15408.html</guid><wfw:comment>http://www.cppblog.com/mybios/comments/15408.html</wfw:comment><comments>http://www.cppblog.com/mybios/archive/2006/11/19/15408.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mybios/comments/commentRss/15408.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mybios/services/trackbacks/15408.html</trackback:ping><description><![CDATA[
		<span class="content">       自从计算机游戏出现以来，程序员就不断地想办法来更精确地模拟现实世界。就拿乒乓游戏为例子（译者：Pong—被誉为电子游戏的祖先，有幸见过一次：），能见到祖先做的游戏感觉真是爽啊，想看的可以到FTP上下载“地球故事”就可以看到了：），游戏中有一个象征性的小方块（球）和两支拍子，游戏者需要在恰当的时间将拍子移动到恰当的地点，将小球反弹回去。这个基本操作的背后（以现在的标准来看）就是最原初的碰撞检测了。今天的游戏比“乒乓”要高级得多，并且基本上是基于3D的。3D游戏中的碰撞检测比“乒乓”游戏里的要更加难实现。玩一些早期模拟飞行游戏的体验向我们展现出糟糕的碰撞检测是如何毁灭一个游戏的。当穿过一座大山的尖顶的时候仍然活着，感觉很不真实。即便是现在的一些游戏也还是有碰撞上的问题，许多玩家曾经失望地看着他们喜爱的英雄或女英雄的部分身体穿进了墙里。甚至更糟的是，许多玩家都有过这样糟糕的体验，就是被那些离得很远的子弹或火箭击中。因为游戏者们要求提升真实性，我们开发者就不得不绞尽脑汁想办法让我们的游戏世界尽可能地接近现实世界。<br /><br />　　阅读这篇文章前首先假设你对与碰撞检测相关的几何和数学知识已经有了基本的了解。在文章的最后，我将提供一些这方面的参考资料，以免你对它们感觉有点生疏。另外我还假设你已经读过 Jeff Lander 的图形专栏里关于碰撞检测文章（“Crashing into the New Year,” ; “When Two Hearts Collide,”;和 “Collision Response: Bouncy, Trouncy, Fun,”）。我将首先进行一个大概的描述，然后快速地切入到核心内容里，通过这两步从上至下地深入到碰撞检测中。我将讨论两种类型的图形引擎中的碰撞检测：基于 portal 的和基于 BSP 的。每种引擎中多边形的组织各不相同，因此在 world-object 型的碰撞检测上存在很大的差别。而object-object 型的碰撞检测绝大多数地方在上述两种引擎里的是一样的，主要看你是如何实现的了。当我们接触到多边形的碰撞检测时，我们还会实验如何将其扩展到我们学过的凸型物体上。<br /><br /><br /><b>预览</b><br /><br />　　为了创建一个理想的碰撞检测程序，我们不得不在开发一个游戏的的图形管道的同时就开始计划并创建它的框架。在项目的最后加入碰撞检测是相当困难的。想在开发周期的末尾创建快速的碰撞检测将很有可能会使整个游戏被毁掉，因为我们不可能使它能高效地运行。在好的游戏引擎中，碰撞检测应该是精确、有效并且十分快速的。这些要求意味着碰撞检测将要与场景的多边形管理管道紧紧地联系起来。这也意味着穷举法将无法工作――今天的3D游戏中每帧处理的数据量很可能导致打格，当你还在检测一个物体的各多边形是否与场景中的其它多边形碰撞时，时间已经过去了。<br /><br />　　让我们从基本的游戏引擎循环开始吧（列表1）。快速浏览这些代码来得到碰撞检测的相关策略。我们先假设碰撞没有发生，然后更新物体的位置，如果发现发生了碰撞，我们将把物体移回原来的位置不允许它穿越边界（或将物体销毁或执行一些预防措施）。然而，这个假设太过简单因为我们无法得知物体原来的位置是否仍然有效。你必须为这种情况设计一个方案（否则你可能会体验到坠机或被子弹击中的感觉――就是前面举的例子）。如果你是一个热心的玩家，你可能已经注意到了在一些游戏当中，当你挨着墙壁并试图穿过去的时候，摄像机就开始震动。你正经历的就是将主角移回原位的情况。震动是因为取了较大的时间片引起的。<br /><br />Listing 1. Extremely Simplified Game Loop<br />while(1){<br />    process_input();<br /><font face="Arial">    </font>update_objects();<br /><font face="Arial">    </font>render_world();<br />}<br />update_objects(){<br /><font face="Arial">    </font>for (each_object)<br /><font face="Arial">        </font>save_old_position();<br /><font face="Arial">    </font>calc new_object_position <br />{based on velocity accel. etc.}<br />if (collide_with_other_objects())<br /><font face="Arial">    </font>new_object_position = old_position();<br />{or if destroyed object remove it etc.}<br /><img height="190" src="http://dev.gameres.com/Program/Visual/3D/bobic_01.gif" width="200" border="0" /><br />Figure 1. Time gradient and collision tests.<br /><br />　　但是我们的方法有缺陷，我们忘了在等式中加入时间。图1告诉我们时间太重要了不能忘了它。即便物体在 t1 或 t2 时刻没有发生碰撞，它仍有可能在 t 时刻穿过边界（t1&lt;t&lt;t2）。这会在两个连续帧中产生大幅度地跨越（就好象击中了燃料室或其它类似的东西）。我们不得不找一个好的方法来解决这个问题。<br /><br />　　我们可以把时间看成是第四维并将所有运算在4维空间中进行。然而这可能会让运算变得十分复杂，所以我们会避开这些。我们还可以创建一个以 t1、t2时刻的物体为起始点的实心体，然后用它来与墙进行测试（见图2）<br /><img height="190" src="http://dev.gameres.com/Program/Visual/3D/bobic_02.gif" width="200" border="0" /><br />Figure 2. Solid created from the space that an object spans over a given time frame.<br /><br />　　一个简单的方法就是创建一个凸壳来罩住两个不同时刻的物体。这种方法效率低下可能会明显地降低你的游戏速度。以其创建一个凸壳，还不如创建一个围绕实心体的包围盒。我们学习其它的技术后再回来讨论这个问题。<br /><br />　　有另一种比较容易执行但精度较低的方法，就是把给定的时间段分为两分，然后测试时间中点的相交关系。我们还可以递归地依次测定各段的时间中点。这个方法比先前的方法要快得多，但不能保证能捕捉到所有的碰撞情况。<br /><br /><img height="50" src="http://dev.gameres.com/Program/Visual/3D/bobic_eq_01.gif" width="41" border="0" /><br /><br />　　另一个暗藏着的问题是collide_with_other_objects()方法的实现――即判断一个物体是否与场景中的其它物体相交。如果场景中有很多的物体，这个方法可能消耗很大。如果要判断各物体与场景中其它物体是否相交，我们将不得不进行大概N选2次比较。因此比较次数会是N的平方次冪（或表示成O(N2)）。但我们可以用几种方法来避免进行O(N2)对的比较。举个例子，我们可以把场景中的物体分成静态的（被撞物）和动态的（碰撞物――即使它的速度为0也行）。就好象房间中的墙壁是被撞物，而一个扔向墙壁的小球是碰撞物。我们可以创建两棵独立的树（每一棵对应一类物体），然后测试那些物体可能会碰撞的树。我们甚至可以对环境进行约定让一些碰撞物之间不发生碰撞――比如我们不需要在两颗子弹之间进行判断。现在在继续之前，（经过改进之后）我们可以说处理过程变得更加清晰了。（另一个减少场景中成对的比较的方法就是建立八叉树。这已经超出了这篇文章的范围，你可以在Spatial Data Structures: Quadtree, Octrees and Other Hierarchical Methods文章中的For Further Info一节里读到更多关于八叉的信息）。现在让我们来看一下基于 Portal 引擎，了解为什么在这类引擎中一提到碰撞检测就会那么痛苦。<br /><br /><br /><b>Portal引擎和Object-Object型碰撞</b><br /><br />　　基于 Portal 的引擎把场景或世界分割成较小的凸方形区域。凸方形区域很适合图形管道因为它们能避免重绘现象。不幸的是，对碰撞检测来说，凸方形区域会给我们带来一些困难。在我最近的一些测试中，一个引擎中平均大约有400到500个凸方形区域。当然，这个数字会随着不同的引擎而有所变化，因为不同的引擎使用不同的多边形技术。而且多边形的数目也会因场景的大小而有所不同。<br /><br />　　判断一个物体的多边形是否穿过了场景中的多边形产生的运算量可能会很大。一个最简单的碰撞检测法就是用球形来近似地表示物体或物体的一部分，然后再判断这些包围球是否相交。这样我们仅仅需要测试两个球体中心的距离是否小于它们的半径合（这表示发生了碰撞）。如果我们是用中心点距离的平方和半径合的平方进行比较，那更好，这样我们可以在计算距离时除去拙劣的开方运算。但是，简单的运算也导致了精确度的降低（见图3）。<br /><img height="152" src="http://dev.gameres.com/Program/Visual/3D/bobic_03.gif" width="200" border="0" /><br />Figure 3. In a sphere-sphere intersection, the routine may report that collision has occurred when it really hasn’t.<br /><br />　　但我们仅仅是将这个不太精确的方法做为我们的第一步。我们用一个大的球体代表整个对象，然后检测它是否和其它的球体相交。如果检测到发生了碰撞，那么我们就要进一步提高精度，我们可以将大的球体分割成一系列小的球体，并检查与各小球体是否发生碰撞。我们不断地分割检查直到得到满意的近似值为止。分层并分割的基本思想就是我们要尽可能达到适合需要的理想的情况。<br /><img height="176" src="http://dev.gameres.com/Program/Visual/3D/bobic_04.gif" width="320" border="0" /><br />Figure 4. Sphere subdivision.<br /><br />　　用球体去近似地代表物体运算量很小，但在游戏中的大多数物体是方的，我们应该用方盒来代表物体。开发者一直用包围盒和这种递归的快速方法来加速光线追踪算法。在实际中，这些算法已经以八叉和AABB（axis-aligned bounding boxes）的方式出现了。图5展示了一个AABB和它里面的物体。<br /><img height="200" src="http://dev.gameres.com/Program/Visual/3D/bobic_05.gif" width="328" border="0" /><br />Figure 5. An object and its AABB.<br /><br />　　坐标轴平行（“Axis-aligned”）不仅指盒体与世界坐标轴平行，同时也指盒体的每个面都和一条坐标轴垂直。这样一个基本信息就能减少转换盒体时操作的次数。AABBs 在当今的许多游戏中都得到了应用，开发者经常用它们作为模型的包围盒。再次指出，提高精度的同时也会降低速度。因为 AABBs 总是与坐标思平行，我们不能在旋转物体的时候简单地旋转 AABBs --- 它们应该在每一帧都重新计算过。如果我们知道每个对象的内容，这个计算就不算困难并不会降低游戏的速度。 然而，我们还面临着精度的问题。假如我们有一个3D的细长刚性直棒，并且要在每一帧动画中都重建它的AABB。我们可以看到每一帧中的包围盒的都不一样而且精度也会随之改变。<br /><img height="150" src="http://dev.gameres.com/Program/Visual/3D/bobic_06.gif" width="320" border="0" /><br />Figure 6. Successive AABBs for a spinning <br />rod (as viewed from the side).<br /><br />　　所以以其用 AABBs，为什么我们不用任意方向能最小化空白区域的包围盒呢。这是一种基于叫 oriented bounding boxes—OBBs 的技术，它已经广泛用于光线追踪和碰撞检测中。这种技术不但比 AABBs 技术更精确而且还更健壮。但 OBBs 实现起来比较困难，执行速度慢，并且不太适合动态的或柔性的物体。特别注意的是当我们把一个物体分得越来越小的时候，我们事实上在创建一棵有层次的树。<br /><br />　　我们是选择 AABBs 还是选择 OBBs 应该根据我们所需的精确程度而定。对一个需要快速反应的3D射击游戏来说，我们可能用 AABB 来进行碰撞检测更好些――我们可以牺牲一些精度来换取速度和实现的简单化。这篇文章附带的代码已经传到 Game Developer 网页上了。里面是从 AABBs 开始讲起，同时还提供了一些实现 OBBs 的碰撞检测包里的代码例子。好了，现在我们已经有了关于每一部分是如果工作的认识了，下面我们来看看实现的细节。<br /><br /><br /><b>创建树</b><br /><br />　　为任意的网格模型创建 OBB 树可能是算法里最难的一个部分，而且它还要调整以适合特定的引擎或游戏类型。图7示出了从最初的模型创建一个OBB树的整个过程。可以看到，我们不得不找出包围给定模型的最近似的包围盒（或者其它3D体）。<br /><img height="150" src="http://dev.gameres.com/Program/Visual/3D/bobic_07.gif" width="409" border="0" /><br />Figure 7. Recursive build of an OBB and its tree.<br /><br />　　有几种方法可以事先计算OBB，这其中包括了许多的数学运算。其中一个基本的方法是计算顶点分布的均值，将它作为包围盒的中心，然后计算协方差矩阵。然后我们用协方差矩阵的三个特征向量中的两个把多边形和包围盒结合起来。我们可以凸盒方法进一步加速和优化树的创建。你可以在Gottschalk, Lin, 和 Manocha的文章中的“For Further Info”一节找到相关信息。建立AABB树要简单得多，因为我们不需要找出物体的最小的包围体和它们的轴。我们只需决定在哪分开模型，而且包围盒可以自由创建（只要包围盒平行于坐标轴并且包含分割面其中一侧的所有顶点）。<br /><br />　　现在我们得到了所有的包围盒，下一步我们来构造一棵树。我们从最初的包围盒开始从上至下地反复分割它。另外，我们还可以用从下至上的方式，逐步地合并小包围盒从而得到最大的包围盒。把大的包围盒分割成小的包围盒，我们应该遵守以下几条原则。我们应该用一个面（这个面垂直于包围盒中的一条坐标轴）来分割包围盒上最长的轴，然后根据多边形处在分割轴的哪一边把多边形分离开来（如图7）。如果不能沿着最长的轴进行分割，那我们就沿第二长的边分割。我们持续地分割直到包围盒不能再分割为止。依据我们需要的精度（比如，是否我们真的要判断单个三角形的碰撞），我们可以按我们的选择的方式（如是按树的深度或是按包围盒中多边形的数目）以任意的条件停止分割。<br /><br />　　正如你所看到的，创建阶段相当复杂，其中包括了大量的运算。很明显不能实时地创建树――只能是事先创建。事先创建可以免去实时改变多边形的可能。另一个缺点是OBB要求进行大量的矩阵运算，我们不得不把它们定位在适当的地方，并且每棵子树必须与矩阵相乘。<br /><br /><br /><b>使用树进行碰撞检测</b><br /><br />　　现在假设我们已经有了OBB或者AABB树。那么我们该怎么进行碰撞检测呢？我们先检测最大的包围盒是否相交，如果相交了，他们可能发生了碰撞，接下来我们将进一步地递归处理它们（不断地递归用下一级进行处理）。如果我们沿着下一级，发现子树并没有发生相交，这时我们就可以停止并得出结论没有发生碰撞。如果我们发现子树也相交了，那么要进一步处理它的子树直到到达叶子节点，并最终得出结论。进行相交测试时，我们可以把包围盒投影到空间坐标轴上并检查它们是否线性相交。这种给定的坐标轴称为分离坐标轴（separating axis）如图8所示。<br /><img height="210" src="http://dev.gameres.com/Program/Visual/3D/bobic_08.gif" width="308" border="0" /><br />Figure 8. Separating axis (intervals<br />A and B don’t overlap).<br /><br />　　为了快速地判断相交性，我们使用一种叫分离坐标的方法。这种方法告诉我们，只有15条潜在的分离坐标。如果跌交的情况在每一条分离坐标上都发生了，那么包围盒是相交的。因此，很容易就能判断出两个包围盒是否相交。<br /><br />　　有趣的是，前面提到的时间片大小的问题用分离坐标技术很容易就能解决。回忆一下关于在两个给定时间内是否发生碰撞的问题。如果我们把速度加上，并且在所有15条坐标轴上的投影都跌交，说明会发生碰撞。我们可以用类似于AABB树那样的数据结构区分碰撞物和受碰物，并判断他们是否有可能发生碰撞。这种运算可以快速地排除在场景中的大多数情况，产生一个接近理想的O次幂（N logN）的效率。<br /><br /><br /><b>基于BSP树的碰撞检测技术</b><br /><br />　　BSP（二叉空间分割）树是另一种类型的空间分割技术，其已经在游戏工业上应用了许多年（Doom是第一个使用BSP树的商业游戏）。尽管在今天BSP树已经没像过去那么受欢迎了，但现在三个被认可的游戏引擎――Quake II, Unreal, and Lithtech（译者：这是2000年的文章，所以指出的这些游戏才这么老：）仍在广泛地采用这项技术。当你看一下BSP在碰撞检测方面那极度干净漂亮和高速的效率，立刻能让你眼前一亮。不但BSP树在多边形剪切方面表现出色，而且还能让我们有效地自由运用world-object式的碰撞检测。BSP树的遍历是使用BSP的一个基本技术。碰撞检测本质上减少了树的遍历或搜索。这种方法很有用因为它能在早期排除大量的多边形，所以在最后我们仅仅是对少数面进行碰撞检测。正如我前面所说的，用找出两个物体间的分隔面的方法适合于判断两个物体是否相交。如果分隔面存在，就没有发生碰撞。因此我们递归地遍历world树并判断分割面是否和包围球或包围盒相交。我们还可以通过检测每一个物体的多边形来提高精确度。进行这种检测最简单的一个方法是测试看看物体的所有部分是否都在分割面的一侧。这种运算真的很简单，我们用迪卡尔平面等式 ax + by + cz + d = 0 去判断点位于平面的哪一侧。如果满足等式，点在平面上；如果ax + by + cz + d &gt; 0那么点在平面的正面；如果ax + by + cz + d &lt; 0点在平面的背面。<br /><br />　　在碰撞没发生的时候有一个重要的事情需要注意，就是一个物体（或它的包围盒）必须在分割面的正面或背面。如果在平面的正面和背面都有顶点，说明物体与这个平面相交了。<br /><br />　　不幸的是，我们还没有一个很好的方法检测在一个时间间隔内的碰撞（在文章开头提到的方法现在仍在使用）。然而，我已经看到有另外的数据结构像BSP树一样开始广泛使用了。<br /><br /><br /><b>曲面物体及碰撞检测</b><br /><br />　　现在我们已经看到了两种多边形物体的碰撞检测，下面一看看如何计算弯曲物体的碰撞。99年发布的几款游戏已经大量地采用曲面了，因此在接下来几年里高效的曲面碰撞检测将变得十分重要。曲面碰撞检测（要求有给定点上精确的曲面等式）运算开销极大，所以我们要尽量避开它。实际上我们已经讨论了几种能用在这种情况下的方法。最明显的方法就是用低网格来近似表示曲面，然后使用这个多面体进行碰撞检测。甚至还有更简单的（但精度比较低），就是在曲面的控制顶点（译者：大概意思就是说每隔一定量的顶点就构造一个凸壳）上构造凸壳用来做碰撞检测。在这种情况下，曲面的碰撞检测十分近拟于传统的多面体碰撞检测。如图9显示了曲面及它在控制顶点上形成的凸壳。<br /><img height="181" src="http://dev.gameres.com/Program/Visual/3D/bobic_09.gif" width="320" border="0" /><br />Figure 9. Hull of a curved object.<br /><br />是否我们可以结合这两种技术形成一种混合方法？首先我们用凸壳进行碰撞检测然后逐步在凸壳所属的部分细分下去，这样就增加了精度。<br /><br /><br /><b>由你决定</b><br /><br />　　现在我们已经浏览了一些高级的碰撞检测（有一些也是基本的），你应该能够决定什么样的系统更适合你的游戏。你要决定的主要事情是精度、速度、实现的简单程度及系统的适应性。<br /><br /></span>
<img src ="http://www.cppblog.com/mybios/aggbug/15408.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mybios/" target="_blank">李锦俊</a> 2006-11-19 01:46 <a href="http://www.cppblog.com/mybios/archive/2006/11/19/15408.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>【转贴】A*算法的泛型抽象实现</title><link>http://www.cppblog.com/mybios/archive/2006/11/19/15407.html</link><dc:creator>李锦俊</dc:creator><author>李锦俊</author><pubDate>Sat, 18 Nov 2006 17:40:00 GMT</pubDate><guid>http://www.cppblog.com/mybios/archive/2006/11/19/15407.html</guid><wfw:comment>http://www.cppblog.com/mybios/comments/15407.html</wfw:comment><comments>http://www.cppblog.com/mybios/archive/2006/11/19/15407.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mybios/comments/commentRss/15407.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mybios/services/trackbacks/15407.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: #ifndef __ASTAR_SERACH_ALGO_H__				#define				 __ASTAR_SERACH_ALGO_H__																												#include 				&lt;				Algorithm				&gt;																    				namespace	...&nbsp;&nbsp;<a href='http://www.cppblog.com/mybios/archive/2006/11/19/15407.html'>阅读全文</a><img src ="http://www.cppblog.com/mybios/aggbug/15407.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mybios/" target="_blank">李锦俊</a> 2006-11-19 01:40 <a href="http://www.cppblog.com/mybios/archive/2006/11/19/15407.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>【转贴】A*寻路算法（简明教程）</title><link>http://www.cppblog.com/mybios/archive/2006/11/18/15330.html</link><dc:creator>李锦俊</dc:creator><author>李锦俊</author><pubDate>Sat, 18 Nov 2006 01:06:00 GMT</pubDate><guid>http://www.cppblog.com/mybios/archive/2006/11/18/15330.html</guid><wfw:comment>http://www.cppblog.com/mybios/comments/15330.html</wfw:comment><comments>http://www.cppblog.com/mybios/archive/2006/11/18/15330.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mybios/comments/commentRss/15330.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mybios/services/trackbacks/15330.html</trackback:ping><description><![CDATA[
		<p>由于非计算机专业毕业,对于一些经典有用的算法不是很了解,最近在网上看了些关于寻路算法的,虽然没有立刻拿来所具体的项目,对于平时的一些思维还是很有用.高级的用法还需继续探索.</p>
		<p>用A*算法寻路有个比较郁闷的问题，虽然每一步都可以选择到最优路径，但是并不是起点到终点的最优路径，特别是在有障碍物的时候，可以饶开障碍物，但也会走“冤枉路”。</p>
		<p>我个人认为的解决方法：</p>
		<p>1、确定障碍范围。障碍物所围成的凸多边形内的可走路径会成为“冤枉路”的陷阱。</p>
		<p>2、障碍物所围成的凸多边形的众横向延伸范围应为低可走优先级。</p>
		<p>下图中，橘红色的线是用A*算法做的，很明显，不是最优路径，绿色和紫色是两条最优路径，但是黑色如果都到当前的位置它应该怎么选择下一步呢？</p>
		<p>
				<img alt="" src="http://p.blog.csdn.net/images/p_blog_csdn_net/banmuhuangci/bst.jpg" />
		</p>
		<p>或则通过计算，如果在某路径下遇到不合理情况（代价+目标距离大于了前一步的代价+目标距离），那么退回到前一个岔口选择另一个路径，但是这样的计算会比较复杂，而且在路径长并且障碍多的时候效率会很低。</p>
		<p>以下是在网上找到的一篇比较简要明了的文章</p>
		<p>
				<span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体; mso-ansi-language: ZH-CN">如果通过图像模拟搜索点，可以发现：非启发式的寻路算法实际上是一种穷举法，通过固定顺序依次搜索人物周围的路点，直到找到目的地，搜索点在图像上的表现为一个不断扩大的矩形。如下：<?XML:NAMESPACE PREFIX = O /?><o:p></o:p></span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 20.5pt; TEXT-ALIGN: center; mso-layout-grid-align: none" align="center">
				<span lang="EN-US">
						<?XML:NAMESPACE PREFIX = V /?>
						<v:shapetype id="_x0000_t75" stroked="f" filled="f" path="m@4@5l@4@11@9@11@9@5xe" o:preferrelative="t" o:spt="75" coordsize="21600,21600">
								<v:stroke joinstyle="miter">
								</v:stroke>
								<v:formulas>
										<v:f eqn="if lineDrawn pixelLineWidth 0">
										</v:f>
										<v:f eqn="sum @0 1 0">
										</v:f>
										<v:f eqn="sum 0 0 @1">
										</v:f>
										<v:f eqn="prod @2 1 2">
										</v:f>
										<v:f eqn="prod @3 21600 pixelWidth">
										</v:f>
										<v:f eqn="prod @3 21600 pixelHeight">
										</v:f>
										<v:f eqn="sum @0 0 1">
										</v:f>
										<v:f eqn="prod @6 1 2">
										</v:f>
										<v:f eqn="prod @7 21600 pixelWidth">
										</v:f>
										<v:f eqn="sum @8 21600 0">
										</v:f>
										<v:f eqn="prod @7 21600 pixelHeight">
										</v:f>
										<v:f eqn="sum @10 21600 0">
										</v:f>
								</v:formulas>
								<v:path o:connecttype="rect" gradientshapeok="t" o:extrusionok="f">
								</v:path>
								<o:lock aspectratio="t" v:ext="edit">
								</o:lock>
						</v:shapetype>
						<v:shape id="_x0000_i1025" style="WIDTH: 186.75pt; HEIGHT: 109.5pt" type="#_x0000_t75" o:ole="">
								<v:imagedata o:title="" src="file:///C:\DOCUME~1\Zeta.net\LOCALS~1\Temp\msohtmlclip1\01\clip_image001.png">
								</v:imagedata>
						</v:shape>
						<font size="3">
								<font face="Times New Roman">
										<span style="mso-spacerun: yes"> <img alt="" src="http://p.blog.csdn.net/images/p_blog_csdn_net/banmuhuangci/p.jpg" />   </span>
										<v:shape id="_x0000_i1026" style="WIDTH: 186.75pt; HEIGHT: 109.5pt" type="#_x0000_t75" o:ole="">
												<v:imagedata o:title="" src="file:///C:\DOCUME~1\Zeta.net\LOCALS~1\Temp\msohtmlclip1\01\clip_image003.png">
												</v:imagedata>
										</v:shape>
								</font>
						</font>
				</span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 20.5pt; TEXT-ALIGN: center; mso-layout-grid-align: none" align="center">
				<span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体; mso-ansi-language: ZH-CN">
						<o:p> </o:p>
				</span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 20.5pt; mso-layout-grid-align: none">
				<span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体; mso-ansi-language: ZH-CN">很快人们发现如此穷举导致搜索速度过慢，而且不是很符合逻辑，试想：如果要从（0，0）点到达（100，0）点，如果每次向东搜索时能够走通，那么干吗还要搜索其他方向呢？所以，出现了启发式的A*寻路算法，一般通过 <strong>已经走过的路程 + 到达目的地的直线距离 </strong>代价值作为搜索时的启发条件，每个点建立一个代价值，每次搜索时就从代价低的最先搜索，如下：</span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 20.5pt; mso-layout-grid-align: none">
				<span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体; mso-ansi-language: ZH-CN">
				</span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 20.5pt; mso-layout-grid-align: none">
				<span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体; mso-ansi-language: ZH-CN">
						<o:p>
								<img alt="" src="http://p.blog.csdn.net/images/p_blog_csdn_net/banmuhuangci/ax.jpg" />
						</o:p>
				</span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 20.5pt; TEXT-ALIGN: center; mso-layout-grid-align: none" align="center">
				<span lang="EN-US">
						<v:shape id="_x0000_i1027" style="WIDTH: 186.75pt; HEIGHT: 109.5pt" type="#_x0000_t75" o:ole="">
								<v:imagedata o:title="" src="file:///C:\DOCUME~1\Zeta.net\LOCALS~1\Temp\msohtmlclip1\01\clip_image001.png">
								</v:imagedata>
						</v:shape>
				</span>
				<span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体; mso-ansi-language: ZH-CN">
						<span style="mso-spacerun: yes">    </span>
				</span>
				<span lang="EN-US">
						<v:shape id="_x0000_i1028" style="WIDTH: 186.75pt; HEIGHT: 109.5pt" type="#_x0000_t75" o:ole="">
								<v:imagedata o:title="" src="file:///C:\DOCUME~1\Zeta.net\LOCALS~1\Temp\msohtmlclip1\01\clip_image005.png">
								</v:imagedata>
						</v:shape>
				</span>
				<span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体; mso-ansi-language: ZH-CN">
						<o:p>
						</o:p>
				</span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 20.5pt; mso-layout-grid-align: none">
				<span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体; mso-ansi-language: ZH-CN">综上所述，以上的搜索是一种矩阵式的不断逼近终点的搜索做法。优点是比较直观，缺点在于距离越远、搜索时间越长。<o:p></o:p></span>
		</p>
<img src ="http://www.cppblog.com/mybios/aggbug/15330.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mybios/" target="_blank">李锦俊</a> 2006-11-18 09:06 <a href="http://www.cppblog.com/mybios/archive/2006/11/18/15330.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>【转贴】几个物体的移动补间插值函数</title><link>http://www.cppblog.com/mybios/archive/2006/11/17/15321.html</link><dc:creator>李锦俊</dc:creator><author>李锦俊</author><pubDate>Fri, 17 Nov 2006 14:27:00 GMT</pubDate><guid>http://www.cppblog.com/mybios/archive/2006/11/17/15321.html</guid><wfw:comment>http://www.cppblog.com/mybios/comments/15321.html</wfw:comment><comments>http://www.cppblog.com/mybios/archive/2006/11/17/15321.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mybios/comments/commentRss/15321.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mybios/services/trackbacks/15321.html</trackback:ping><description><![CDATA[繁体的<font color="#008000">間補</font>，也就是我们常说的补间插值，或者线性插值。<br /><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" /><span style="COLOR: #008000">//</span><span style="COLOR: #008000">=====================================</span><span style="COLOR: #008000"><br /><img id="Codehighlighter1_40_167_Open_Image" onclick="this.style.display='none'; Codehighlighter1_40_167_Open_Text.style.display='none'; Codehighlighter1_40_167_Closed_Image.style.display='inline'; Codehighlighter1_40_167_Closed_Text.style.display='inline';" src="http://www.cppblog.com/images/OutliningIndicators/ExpandedBlockStart.gif" align="top" /><img id="Codehighlighter1_40_167_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_40_167_Closed_Text.style.display='none'; Codehighlighter1_40_167_Open_Image.style.display='inline'; Codehighlighter1_40_167_Open_Text.style.display='inline';" src="http://www.cppblog.com/images/OutliningIndicators/ContractedBlock.gif" align="top" /></span><span id="Codehighlighter1_40_167_Closed_Text" style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">/**/</span><span id="Codehighlighter1_40_167_Open_Text"><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">*<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />等速運動用的間補<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" /><br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />@param start [in] 起點<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />@param end  [in] 終點<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />@param all  [in] 起點到終點經過的時間<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />@param now  [in] 現在時間<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" /><br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />@return 以now為基礎的間補值<br /><img src="http://www.cppblog.com/images/OutliningIndicators/ExpandedBlockEnd.gif" align="top" /></span><span style="COLOR: #008000">*/</span></span><span style="COLOR: #000000"><br /><img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">=====================================</span><span style="COLOR: #008000"><br /><img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #0000ff">float</span><span style="COLOR: #000000"> FlatSpeed( </span><span style="COLOR: #0000ff">float</span><span style="COLOR: #000000"> start, </span><span style="COLOR: #0000ff">float</span><span style="COLOR: #000000"> end, </span><span style="COLOR: #0000ff">long</span><span style="COLOR: #000000"> all, </span><span style="COLOR: #0000ff">long</span><span style="COLOR: #000000"> now )<br /><img id="Codehighlighter1_271_319_Open_Image" onclick="this.style.display='none'; Codehighlighter1_271_319_Open_Text.style.display='none'; Codehighlighter1_271_319_Closed_Image.style.display='inline'; Codehighlighter1_271_319_Closed_Text.style.display='inline';" src="http://www.cppblog.com/images/OutliningIndicators/ExpandedBlockStart.gif" align="top" /><img id="Codehighlighter1_271_319_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_271_319_Closed_Text.style.display='none'; Codehighlighter1_271_319_Open_Image.style.display='inline'; Codehighlighter1_271_319_Open_Text.style.display='inline';" src="http://www.cppblog.com/images/OutliningIndicators/ContractedBlock.gif" align="top" /></span><span id="Codehighlighter1_271_319_Closed_Text" style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/images/dot.gif" /></span><span id="Codehighlighter1_271_319_Open_Text"><span style="COLOR: #000000">{<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" /></span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000"> (start </span><span style="COLOR: #000000">+</span><span style="COLOR: #000000"> ((end </span><span style="COLOR: #000000">-</span><span style="COLOR: #000000"> start) </span><span style="COLOR: #000000">*</span><span style="COLOR: #000000"> now </span><span style="COLOR: #000000">/</span><span style="COLOR: #000000"> all));<br /><img src="http://www.cppblog.com/images/OutliningIndicators/ExpandedBlockEnd.gif" align="top" />}</span></span><span style="COLOR: #000000"><br /><img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" /><br /><img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">=============================================</span><span style="COLOR: #008000"><br /><img id="Codehighlighter1_370_497_Open_Image" onclick="this.style.display='none'; Codehighlighter1_370_497_Open_Text.style.display='none'; Codehighlighter1_370_497_Closed_Image.style.display='inline'; Codehighlighter1_370_497_Closed_Text.style.display='inline';" src="http://www.cppblog.com/images/OutliningIndicators/ExpandedBlockStart.gif" align="top" /><img id="Codehighlighter1_370_497_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_370_497_Closed_Text.style.display='none'; Codehighlighter1_370_497_Open_Image.style.display='inline'; Codehighlighter1_370_497_Open_Text.style.display='inline';" src="http://www.cppblog.com/images/OutliningIndicators/ContractedBlock.gif" align="top" /></span><span id="Codehighlighter1_370_497_Closed_Text" style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">/**/</span><span id="Codehighlighter1_370_497_Open_Text"><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">*<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />加速運動用的間補<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" /><br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />@param start [in] 起點<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />@param end  [in] 終點<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />@param all  [in] 起點到終點經過的時間<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />@param now  [in] 現在時間<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" /><br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />@return 以now為基礎的間補值<br /><img src="http://www.cppblog.com/images/OutliningIndicators/ExpandedBlockEnd.gif" align="top" /></span><span style="COLOR: #008000">*/</span></span><span style="COLOR: #000000"><br /><img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">===========================================</span><span style="COLOR: #008000"><br /><img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #0000ff">float</span><span style="COLOR: #000000"> AddSpeed( </span><span style="COLOR: #0000ff">float</span><span style="COLOR: #000000"> start, </span><span style="COLOR: #0000ff">float</span><span style="COLOR: #000000"> end, </span><span style="COLOR: #0000ff">long</span><span style="COLOR: #000000"> all, </span><span style="COLOR: #0000ff">long</span><span style="COLOR: #000000"> now )<br /><img id="Codehighlighter1_606_705_Open_Image" onclick="this.style.display='none'; Codehighlighter1_606_705_Open_Text.style.display='none'; Codehighlighter1_606_705_Closed_Image.style.display='inline'; Codehighlighter1_606_705_Closed_Text.style.display='inline';" src="http://www.cppblog.com/images/OutliningIndicators/ExpandedBlockStart.gif" align="top" /><img id="Codehighlighter1_606_705_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_606_705_Closed_Text.style.display='none'; Codehighlighter1_606_705_Open_Image.style.display='inline'; Codehighlighter1_606_705_Open_Text.style.display='inline';" src="http://www.cppblog.com/images/OutliningIndicators/ContractedBlock.gif" align="top" /></span><span id="Codehighlighter1_606_705_Closed_Text" style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/images/dot.gif" /></span><span id="Codehighlighter1_606_705_Open_Text"><span style="COLOR: #000000">{<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" /></span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000"> ( (start)</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">((end)</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">(start)) </span><span style="COLOR: #000000">*</span><span style="COLOR: #000000"> (((           (now)</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">)</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">(now))</span><span style="COLOR: #000000">&gt;&gt;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">) </span><span style="COLOR: #000000">/</span><span style="COLOR: #000000"> ((((all)</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">)</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">(all))</span><span style="COLOR: #000000">&gt;&gt;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">) );<br /><img src="http://www.cppblog.com/images/OutliningIndicators/ExpandedBlockEnd.gif" align="top" />}</span></span><span style="COLOR: #000000"><br /><img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" /><br /><img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">=============================================</span><span style="COLOR: #008000"><br /><img id="Codehighlighter1_756_883_Open_Image" onclick="this.style.display='none'; Codehighlighter1_756_883_Open_Text.style.display='none'; Codehighlighter1_756_883_Closed_Image.style.display='inline'; Codehighlighter1_756_883_Closed_Text.style.display='inline';" src="http://www.cppblog.com/images/OutliningIndicators/ExpandedBlockStart.gif" align="top" /><img id="Codehighlighter1_756_883_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_756_883_Closed_Text.style.display='none'; Codehighlighter1_756_883_Open_Image.style.display='inline'; Codehighlighter1_756_883_Open_Text.style.display='inline';" src="http://www.cppblog.com/images/OutliningIndicators/ContractedBlock.gif" align="top" /></span><span id="Codehighlighter1_756_883_Closed_Text" style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">/**/</span><span id="Codehighlighter1_756_883_Open_Text"><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">*<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />減速運動用的間補<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" /><br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />@param start [in] 起點<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />@param end  [in] 終點<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />@param all  [in] 起點到終點經過的時間<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />@param now  [in] 現在時間<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" /><br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />@return 以now為基礎的間補值<br /><img src="http://www.cppblog.com/images/OutliningIndicators/ExpandedBlockEnd.gif" align="top" /></span><span style="COLOR: #008000">*/</span></span><span style="COLOR: #000000"><br /><img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">============================================</span><span style="COLOR: #008000"><br /><img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #0000ff">float</span><span style="COLOR: #000000"> SubSpeed( </span><span style="COLOR: #0000ff">float</span><span style="COLOR: #000000"> start, </span><span style="COLOR: #0000ff">float</span><span style="COLOR: #000000"> end, </span><span style="COLOR: #0000ff">long</span><span style="COLOR: #000000"> all, </span><span style="COLOR: #0000ff">long</span><span style="COLOR: #000000"> now )<br /><img id="Codehighlighter1_993_1093_Open_Image" onclick="this.style.display='none'; Codehighlighter1_993_1093_Open_Text.style.display='none'; Codehighlighter1_993_1093_Closed_Image.style.display='inline'; Codehighlighter1_993_1093_Closed_Text.style.display='inline';" src="http://www.cppblog.com/images/OutliningIndicators/ExpandedBlockStart.gif" align="top" /><img id="Codehighlighter1_993_1093_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_993_1093_Closed_Text.style.display='none'; Codehighlighter1_993_1093_Open_Image.style.display='inline'; Codehighlighter1_993_1093_Open_Text.style.display='inline';" src="http://www.cppblog.com/images/OutliningIndicators/ContractedBlock.gif" align="top" /></span><span id="Codehighlighter1_993_1093_Closed_Text" style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/images/dot.gif" /></span><span id="Codehighlighter1_993_1093_Open_Text"><span style="COLOR: #000000">{<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" /></span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000"> ( (start)</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">((end)</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">(start)) </span><span style="COLOR: #000000">*</span><span style="COLOR: #000000"> (((((all)</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">)</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">(now)</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">)</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">(now))</span><span style="COLOR: #000000">&gt;&gt;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">) </span><span style="COLOR: #000000">/</span><span style="COLOR: #000000"> ((((all)</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">)</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">(all))</span><span style="COLOR: #000000">&gt;&gt;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">) );<br /><img src="http://www.cppblog.com/images/OutliningIndicators/ExpandedBlockEnd.gif" align="top" />}</span></span><span style="COLOR: #000000"><br /><img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" /><br /><img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">==============================================</span><span style="COLOR: #008000"><br /><img id="Codehighlighter1_1145_1261_Open_Image" onclick="this.style.display='none'; Codehighlighter1_1145_1261_Open_Text.style.display='none'; Codehighlighter1_1145_1261_Closed_Image.style.display='inline'; Codehighlighter1_1145_1261_Closed_Text.style.display='inline';" src="http://www.cppblog.com/images/OutliningIndicators/ExpandedBlockStart.gif" align="top" /><img id="Codehighlighter1_1145_1261_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_1145_1261_Closed_Text.style.display='none'; Codehighlighter1_1145_1261_Open_Image.style.display='inline'; Codehighlighter1_1145_1261_Open_Text.style.display='inline';" src="http://www.cppblog.com/images/OutliningIndicators/ContractedBlock.gif" align="top" /></span><span id="Codehighlighter1_1145_1261_Closed_Text" style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">/**/</span><span id="Codehighlighter1_1145_1261_Open_Text"><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">*<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />Bezier用的間補計算<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" /><br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />@param p1 [in] 起點<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />@param p2 [in] 中間點<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />@param p3 [in] 終點<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />@param t [in] 現在的時間(01)<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" /><br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />@return  在t方面的值<br /><img src="http://www.cppblog.com/images/OutliningIndicators/ExpandedBlockEnd.gif" align="top" /></span><span style="COLOR: #008000">*/</span></span><span style="COLOR: #000000"><br /><img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">============================================</span><span style="COLOR: #008000"><br /><img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #0000ff">float</span><span style="COLOR: #000000"> Bezier( </span><span style="COLOR: #0000ff">float</span><span style="COLOR: #000000"> p1, </span><span style="COLOR: #0000ff">float</span><span style="COLOR: #000000"> p2, </span><span style="COLOR: #0000ff">float</span><span style="COLOR: #000000"> p3, </span><span style="COLOR: #0000ff">float</span><span style="COLOR: #000000"> t )<br /><img id="Codehighlighter1_1364_1423_Open_Image" onclick="this.style.display='none'; Codehighlighter1_1364_1423_Open_Text.style.display='none'; Codehighlighter1_1364_1423_Closed_Image.style.display='inline'; Codehighlighter1_1364_1423_Closed_Text.style.display='inline';" src="http://www.cppblog.com/images/OutliningIndicators/ExpandedBlockStart.gif" align="top" /><img id="Codehighlighter1_1364_1423_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_1364_1423_Closed_Text.style.display='none'; Codehighlighter1_1364_1423_Open_Image.style.display='inline'; Codehighlighter1_1364_1423_Open_Text.style.display='inline';" src="http://www.cppblog.com/images/OutliningIndicators/ContractedBlock.gif" align="top" /></span><span id="Codehighlighter1_1364_1423_Closed_Text" style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/images/dot.gif" /></span><span id="Codehighlighter1_1364_1423_Open_Text"><span style="COLOR: #000000">{<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" /></span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000"> ( ((</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">t)</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">(</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">t)</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">p1) </span><span style="COLOR: #000000">+</span><span style="COLOR: #000000"> (</span><span style="COLOR: #000000">2</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">(</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">t)</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">t</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">p2) </span><span style="COLOR: #000000">+</span><span style="COLOR: #000000"> (t</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">t</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">p3) );<br /><img src="http://www.cppblog.com/images/OutliningIndicators/ExpandedBlockEnd.gif" align="top" />}</span></span><span style="COLOR: #000000"><br /><img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" /><br /><img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">===========================================</span><span style="COLOR: #008000"><br /><img id="Codehighlighter1_1472_1589_Open_Image" onclick="this.style.display='none'; Codehighlighter1_1472_1589_Open_Text.style.display='none'; Codehighlighter1_1472_1589_Closed_Image.style.display='inline'; Codehighlighter1_1472_1589_Closed_Text.style.display='inline';" src="http://www.cppblog.com/images/OutliningIndicators/ExpandedBlockStart.gif" align="top" /><img id="Codehighlighter1_1472_1589_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_1472_1589_Closed_Text.style.display='none'; Codehighlighter1_1472_1589_Open_Image.style.display='inline'; Codehighlighter1_1472_1589_Open_Text.style.display='inline';" src="http://www.cppblog.com/images/OutliningIndicators/ContractedBlock.gif" align="top" /></span><span id="Codehighlighter1_1472_1589_Closed_Text" style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">/**/</span><span id="Codehighlighter1_1472_1589_Open_Text"><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">*<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />Neville用的間補計算<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" /><br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />@param p1 [in] 起點<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />@param p2 [in] 中間點<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />@param p3 [in] 終點<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />@param t [in] 現在的時間(01)<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" /><br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />@return  在t方面的值<br /><img src="http://www.cppblog.com/images/OutliningIndicators/ExpandedBlockEnd.gif" align="top" /></span><span style="COLOR: #008000">*/</span></span><span style="COLOR: #000000"><br /><img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">=========================================</span><span style="COLOR: #008000"><br /><img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #0000ff">float</span><span style="COLOR: #000000"> Neville( </span><span style="COLOR: #0000ff">float</span><span style="COLOR: #000000"> p1, </span><span style="COLOR: #0000ff">float</span><span style="COLOR: #000000"> p2, </span><span style="COLOR: #0000ff">float</span><span style="COLOR: #000000"> p3, </span><span style="COLOR: #0000ff">float</span><span style="COLOR: #000000"> t )<br /><img id="Codehighlighter1_1690_1814_Open_Image" onclick="this.style.display='none'; Codehighlighter1_1690_1814_Open_Text.style.display='none'; Codehighlighter1_1690_1814_Closed_Image.style.display='inline'; Codehighlighter1_1690_1814_Closed_Text.style.display='inline';" src="http://www.cppblog.com/images/OutliningIndicators/ExpandedBlockStart.gif" align="top" /><img id="Codehighlighter1_1690_1814_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_1690_1814_Closed_Text.style.display='none'; Codehighlighter1_1690_1814_Open_Image.style.display='inline'; Codehighlighter1_1690_1814_Open_Text.style.display='inline';" src="http://www.cppblog.com/images/OutliningIndicators/ContractedBlock.gif" align="top" /></span><span id="Codehighlighter1_1690_1814_Closed_Text" style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/images/dot.gif" /></span><span id="Codehighlighter1_1690_1814_Open_Text"><span style="COLOR: #000000">{<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />t </span><span style="COLOR: #000000">*=</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">2</span><span style="COLOR: #000000">;<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />p1 </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> p2 </span><span style="COLOR: #000000">+</span><span style="COLOR: #000000"> ( p2 </span><span style="COLOR: #000000">-</span><span style="COLOR: #000000"> p1 ) </span><span style="COLOR: #000000">*</span><span style="COLOR: #000000"> ( t </span><span style="COLOR: #000000">-</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">1</span><span style="COLOR: #000000"> );<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />p2 </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> p3 </span><span style="COLOR: #000000">+</span><span style="COLOR: #000000"> ( p3 </span><span style="COLOR: #000000">-</span><span style="COLOR: #000000"> p2 ) </span><span style="COLOR: #000000">*</span><span style="COLOR: #000000"> ( t </span><span style="COLOR: #000000">-</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">2</span><span style="COLOR: #000000"> );<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" /></span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000"> p2 </span><span style="COLOR: #000000">+</span><span style="COLOR: #000000"> ( p2 </span><span style="COLOR: #000000">-</span><span style="COLOR: #000000"> p1 ) </span><span style="COLOR: #000000">*</span><span style="COLOR: #000000"> ( t </span><span style="COLOR: #000000">-</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">2</span><span style="COLOR: #000000"> ) </span><span style="COLOR: #000000">*</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">0.5f</span><span style="COLOR: #000000">;<br /><img src="http://www.cppblog.com/images/OutliningIndicators/ExpandedBlockEnd.gif" align="top" />}</span></span></div><img src ="http://www.cppblog.com/mybios/aggbug/15321.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mybios/" target="_blank">李锦俊</a> 2006-11-17 22:27 <a href="http://www.cppblog.com/mybios/archive/2006/11/17/15321.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>【原创】《3D数学基础：图形与游戏开发》读书笔记1</title><link>http://www.cppblog.com/mybios/archive/2006/11/17/15302.html</link><dc:creator>李锦俊</dc:creator><author>李锦俊</author><pubDate>Fri, 17 Nov 2006 09:09:00 GMT</pubDate><guid>http://www.cppblog.com/mybios/archive/2006/11/17/15302.html</guid><wfw:comment>http://www.cppblog.com/mybios/comments/15302.html</wfw:comment><comments>http://www.cppblog.com/mybios/archive/2006/11/17/15302.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.cppblog.com/mybios/comments/commentRss/15302.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mybios/services/trackbacks/15302.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 左手坐标系的直观表示：向量的表示（轴对齐包围盒（AABB（axially aligned bounding box）））：2D向量的长度：||v|| = sqrt(vx*vx + vy*vy)3D向量的长度：||v|| = sqrt(vx*vx + vy*vy + vz*vz)标准化向量＝此向量/此向量的长度＝vx / ||v|| , vy / ||v|| , vz / ||v||标准化后的向量的...&nbsp;&nbsp;<a href='http://www.cppblog.com/mybios/archive/2006/11/17/15302.html'>阅读全文</a><img src ="http://www.cppblog.com/mybios/aggbug/15302.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mybios/" target="_blank">李锦俊</a> 2006-11-17 17:09 <a href="http://www.cppblog.com/mybios/archive/2006/11/17/15302.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>【原创】计算Bezier曲线的线性插值点的函数</title><link>http://www.cppblog.com/mybios/archive/2006/11/17/15298.html</link><dc:creator>李锦俊</dc:creator><author>李锦俊</author><pubDate>Fri, 17 Nov 2006 08:16:00 GMT</pubDate><guid>http://www.cppblog.com/mybios/archive/2006/11/17/15298.html</guid><wfw:comment>http://www.cppblog.com/mybios/comments/15298.html</wfw:comment><comments>http://www.cppblog.com/mybios/archive/2006/11/17/15298.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.cppblog.com/mybios/comments/commentRss/15298.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mybios/services/trackbacks/15298.html</trackback:ping><description><![CDATA[
		<p> </p>
		<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee">
				<img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" />
				<span style="COLOR: #008000">//</span>
				<span style="COLOR: #008000"> 计算Bezier曲线的线性插值点<br /><img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" /></span>
				<span style="COLOR: #008000">//</span>
				<span style="COLOR: #008000">D3DXVECTOR3 *vecPoint1 第1个Bezier关键点<br /><img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" /></span>
				<span style="COLOR: #008000">//</span>
				<span style="COLOR: #008000">D3DXVECTOR3 *vecPoint2 第2个Bezier关键点<br /><img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" /></span>
				<span style="COLOR: #008000">//</span>
				<span style="COLOR: #008000">D3DXVECTOR3 *vecPoint3 第3个Bezier关键点<br /><img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" /></span>
				<span style="COLOR: #008000">//</span>
				<span style="COLOR: #008000">D3DXVECTOR3 *vecPoint4 第4个Bezier关键点<br /><img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" /></span>
				<span style="COLOR: #008000">//</span>
				<span style="COLOR: #008000">float Scalar    要计算插值的点所在的位置（0～1）         <br /><img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" /></span>
				<span style="COLOR: #008000">//</span>
				<span style="COLOR: #008000">D3DXVECTOR3 *vecOut  输出的3D坐标</span>
				<span style="COLOR: #008000">
						<br />
						<img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" />
				</span>
				<span style="COLOR: #0000ff">void</span>
				<span style="COLOR: #000000"> CubicBezierCurve(D3DXVECTOR3 </span>
				<span style="COLOR: #000000">*</span>
				<span style="COLOR: #000000">vecPoint1,<br /><img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" />                      D3DXVECTOR3 </span>
				<span style="COLOR: #000000">*</span>
				<span style="COLOR: #000000">vecPoint2,<br /><img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" />                      D3DXVECTOR3 </span>
				<span style="COLOR: #000000">*</span>
				<span style="COLOR: #000000">vecPoint3,<br /><img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" />                      D3DXVECTOR3 </span>
				<span style="COLOR: #000000">*</span>
				<span style="COLOR: #000000">vecPoint4,<br /><img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" />                      </span>
				<span style="COLOR: #0000ff">float</span>
				<span style="COLOR: #000000"> Scalar, <br /><img src="http://www.cppblog.com/images/OutliningIndicators/None.gif" align="top" />                      D3DXVECTOR3 </span>
				<span style="COLOR: #000000">*</span>
				<span style="COLOR: #000000">vecOut)<br /><img id="Codehighlighter1_512_850_Open_Image" onclick="this.style.display='none'; Codehighlighter1_512_850_Open_Text.style.display='none'; Codehighlighter1_512_850_Closed_Image.style.display='inline'; Codehighlighter1_512_850_Closed_Text.style.display='inline';" src="http://www.cppblog.com/images/OutliningIndicators/ExpandedBlockStart.gif" align="top" /><img id="Codehighlighter1_512_850_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_512_850_Closed_Text.style.display='none'; Codehighlighter1_512_850_Open_Image.style.display='inline'; Codehighlighter1_512_850_Open_Text.style.display='inline';" src="http://www.cppblog.com/images/OutliningIndicators/ContractedBlock.gif" align="top" /></span>
				<span id="Codehighlighter1_512_850_Closed_Text" style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">
						<img src="http://www.cppblog.com/images/dot.gif" />
				</span>
				<span id="Codehighlighter1_512_850_Open_Text">
						<span style="COLOR: #000000">{<br /><img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />  </span>
						<span style="COLOR: #008000">//</span>
						<span style="COLOR: #008000"> C(s) =</span>
						<span style="COLOR: #008000">
								<br />
								<img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />
						</span>
						<span style="COLOR: #000000">  </span>
						<span style="COLOR: #000000">*</span>
						<span style="COLOR: #000000">vecOut </span>
						<span style="COLOR: #000000">=</span>
						<span style="COLOR: #000000">
								<br />
								<img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />    </span>
						<span style="COLOR: #008000">//</span>
						<span style="COLOR: #008000"> P0 * (1 - s)3 +</span>
						<span style="COLOR: #008000">
								<br />
								<img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />
						</span>
						<span style="COLOR: #000000">    (</span>
						<span style="COLOR: #000000">*</span>
						<span style="COLOR: #000000">vecPoint1)</span>
						<span style="COLOR: #000000">*</span>
						<span style="COLOR: #000000">(</span>
						<span style="COLOR: #000000">1.0f</span>
						<span style="COLOR: #000000">-</span>
						<span style="COLOR: #000000">Scalar)</span>
						<span style="COLOR: #000000">*</span>
						<span style="COLOR: #000000">(</span>
						<span style="COLOR: #000000">1.0f</span>
						<span style="COLOR: #000000">-</span>
						<span style="COLOR: #000000">Scalar)</span>
						<span style="COLOR: #000000">*</span>
						<span style="COLOR: #000000">(</span>
						<span style="COLOR: #000000">1.0f</span>
						<span style="COLOR: #000000">-</span>
						<span style="COLOR: #000000">Scalar) </span>
						<span style="COLOR: #000000">+</span>
						<span style="COLOR: #000000">
								<br />
								<img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />    </span>
						<span style="COLOR: #008000">//</span>
						<span style="COLOR: #008000"> P1 * 3 * s * (1 - s)2 +</span>
						<span style="COLOR: #008000">
								<br />
								<img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />
						</span>
						<span style="COLOR: #000000">    (</span>
						<span style="COLOR: #000000">*</span>
						<span style="COLOR: #000000">vecPoint2)</span>
						<span style="COLOR: #000000">*</span>
						<span style="COLOR: #000000">3.0f</span>
						<span style="COLOR: #000000">*</span>
						<span style="COLOR: #000000">Scalar</span>
						<span style="COLOR: #000000">*</span>
						<span style="COLOR: #000000">(</span>
						<span style="COLOR: #000000">1.0f</span>
						<span style="COLOR: #000000">-</span>
						<span style="COLOR: #000000">Scalar)</span>
						<span style="COLOR: #000000">*</span>
						<span style="COLOR: #000000">(</span>
						<span style="COLOR: #000000">1.0f</span>
						<span style="COLOR: #000000">-</span>
						<span style="COLOR: #000000">Scalar) </span>
						<span style="COLOR: #000000">+</span>
						<span style="COLOR: #000000">
								<br />
								<img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />    </span>
						<span style="COLOR: #008000">//</span>
						<span style="COLOR: #008000"> P2 * 3 * s2 * (1 - s) +</span>
						<span style="COLOR: #008000">
								<br />
								<img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />
						</span>
						<span style="COLOR: #000000">    (</span>
						<span style="COLOR: #000000">*</span>
						<span style="COLOR: #000000">vecPoint3)</span>
						<span style="COLOR: #000000">*</span>
						<span style="COLOR: #000000">3.0f</span>
						<span style="COLOR: #000000">*</span>
						<span style="COLOR: #000000">Scalar</span>
						<span style="COLOR: #000000">*</span>
						<span style="COLOR: #000000">Scalar</span>
						<span style="COLOR: #000000">*</span>
						<span style="COLOR: #000000">(</span>
						<span style="COLOR: #000000">1.0f</span>
						<span style="COLOR: #000000">-</span>
						<span style="COLOR: #000000">Scalar) </span>
						<span style="COLOR: #000000">+</span>
						<span style="COLOR: #000000">
								<br />
								<img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />    </span>
						<span style="COLOR: #008000">//</span>
						<span style="COLOR: #008000"> P3 * s3 </span>
						<span style="COLOR: #008000">
								<br />
								<img src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" align="top" />
						</span>
						<span style="COLOR: #000000">    (</span>
						<span style="COLOR: #000000">*</span>
						<span style="COLOR: #000000">vecPoint4)</span>
						<span style="COLOR: #000000">*</span>
						<span style="COLOR: #000000">Scalar</span>
						<span style="COLOR: #000000">*</span>
						<span style="COLOR: #000000">Scalar</span>
						<span style="COLOR: #000000">*</span>
						<span style="COLOR: #000000">Scalar;<br /><img src="http://www.cppblog.com/images/OutliningIndicators/ExpandedBlockEnd.gif" align="top" />}</span>
				</span>
		</div>
<img src ="http://www.cppblog.com/mybios/aggbug/15298.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mybios/" target="_blank">李锦俊</a> 2006-11-17 16:16 <a href="http://www.cppblog.com/mybios/archive/2006/11/17/15298.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>【转贴】了解凹凸映射Bump Map技术的内幕</title><link>http://www.cppblog.com/mybios/archive/2006/11/17/15272.html</link><dc:creator>李锦俊</dc:creator><author>李锦俊</author><pubDate>Thu, 16 Nov 2006 16:00:00 GMT</pubDate><guid>http://www.cppblog.com/mybios/archive/2006/11/17/15272.html</guid><wfw:comment>http://www.cppblog.com/mybios/comments/15272.html</wfw:comment><comments>http://www.cppblog.com/mybios/archive/2006/11/17/15272.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mybios/comments/commentRss/15272.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mybios/services/trackbacks/15272.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 　　我翻译这篇教程的目的是为了帮助那些对图形渲染技术有兴趣却又苦于找不到免费中文学习资料的人。在我的身边没有任何一位从事计算机专业的前辈，从刚学会WINDOWS的基本操作到现在，我的计算机技术完全都是一步步自学过来的，算算学编程的历史也近5年时间了。我往往要花一半以上的学习时间用来查学习资料（记得我学GIF图像压缩解压算法时，用了近2个星期时间才编出了显示GIF图片的程序，主要原因就是资料不够，只看一两篇短篇幅的教程来写程序，其中多数时间花在调试代码和猜格式上）........请点击<a href="http://www.cppblog.com/mybios/archive/2006/11/17/15272.html">这里</a>阅读全文&nbsp;&nbsp;<a href='http://www.cppblog.com/mybios/archive/2006/11/17/15272.html'>阅读全文</a><img src ="http://www.cppblog.com/mybios/aggbug/15272.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mybios/" target="_blank">李锦俊</a> 2006-11-17 00:00 <a href="http://www.cppblog.com/mybios/archive/2006/11/17/15272.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>【转贴】OCTREE 教程</title><link>http://www.cppblog.com/mybios/archive/2006/11/17/15273.html</link><dc:creator>李锦俊</dc:creator><author>李锦俊</author><pubDate>Thu, 16 Nov 2006 16:00:00 GMT</pubDate><guid>http://www.cppblog.com/mybios/archive/2006/11/17/15273.html</guid><wfw:comment>http://www.cppblog.com/mybios/comments/15273.html</wfw:comment><comments>http://www.cppblog.com/mybios/archive/2006/11/17/15273.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mybios/comments/commentRss/15273.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mybios/services/trackbacks/15273.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 对OCTREE的描述<br><br>　　OCTREE 是对3D空间进行划分，也可以叫空间分割。他允许你只对你的3D世界中摄象机照射的区域进行作画。他也能用于冲突检测。下面讲一下为什么要进行空间分割。假设你建立了一个游戏世界，这个世界有超过100，000个多边形要画。如果你建立一个循环并传递这些多边形，那速度是很慢的。即使你有一块很好的显示卡，他也会有很大的麻烦。但是玩你游戏的玩家的显示卡不会超过300$。有没有一种方法只渲染摄象机看见的多边形？那就是美丽的OCTREE。他允许你快速的找到你要渲染的多边形...............<a href='http://www.cppblog.com/mybios/archive/2006/11/17/15273.html'>阅读全文</a>&nbsp;&nbsp;<a href='http://www.cppblog.com/mybios/archive/2006/11/17/15273.html'>阅读全文</a><img src ="http://www.cppblog.com/mybios/aggbug/15273.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mybios/" target="_blank">李锦俊</a> 2006-11-17 00:00 <a href="http://www.cppblog.com/mybios/archive/2006/11/17/15273.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>【转贴】矩阵的本质-运动的描述</title><link>http://www.cppblog.com/mybios/archive/2006/11/16/15222.html</link><dc:creator>李锦俊</dc:creator><author>李锦俊</author><pubDate>Thu, 16 Nov 2006 07:01:00 GMT</pubDate><guid>http://www.cppblog.com/mybios/archive/2006/11/16/15222.html</guid><wfw:comment>http://www.cppblog.com/mybios/comments/15222.html</wfw:comment><comments>http://www.cppblog.com/mybios/archive/2006/11/16/15222.html#Feedback</comments><slash:comments>7</slash:comments><wfw:commentRss>http://www.cppblog.com/mybios/comments/commentRss/15222.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mybios/services/trackbacks/15222.html</trackback:ping><description><![CDATA[前不久chensh出于不可告人的目的，要充当老师，教别人线性代数。于是我被揪住就线性代数中一些务虚性的问题与他讨论了几次。很明显，chensh觉得，要让自己在讲线性代数的时候不被那位强势的学生认为是神经病，还是比较难的事情。<br /><font size="3">可怜的chensh，谁让你趟这个地雷阵？！色令智昏啊！</font><br /><font size="3">线性代数课程，无论你从行列式入手还是直接从矩阵入手，从一开始就充斥着莫名其妙。比如说，在全国一般工科院系教学中应用最广泛的同济线性代数教材（现在到了第四版），一上来就介绍逆序数这个“前无古人，后无来者”的古怪概念，然后用逆序数给出行列式的一个极不直观的定义，接着是一些简直犯傻的行列式性质和习题——把这行乘一个系数加到另一行上，再把那一列减过来，折腾得那叫一个热闹，可就是压根看不出这个东西有嘛用。大多数像我一样资质平庸的学生到这里就有点犯晕：连这是个什么东西都模模糊糊的，就开始钻火圈表演了，这未免太“无厘头”了吧！于是开始有人逃课，更多的人开始抄作业。这下就中招了，因为其后的发展可以用一句峰回路转来形容，紧跟着这个无厘头的行列式的，是一个同样无厘头但是伟大的无以复加的家伙的出场——矩阵来了！多年之后，我才明白，当老师犯傻似地用中括号把一堆傻了吧叽的数括起来，并且不紧不慢地说：“这个东西叫做矩阵”的时候，我的数学生涯掀开了何等悲壮辛酸、惨绝人寰的一幕！自那以后，在几乎所有跟“学问”二字稍微沾点边的东西里，矩阵这个家伙从不缺席。对于我这个没能一次搞定线性代数的笨蛋来说，矩阵老大的不请自来每每搞得我灰头土脸，头破血流。长期以来，我在阅读中一见矩阵，就如同阿Q见到了假洋鬼子，揉揉额角就绕道走。</font><br /><font size="3">事实上，我并不是特例。一般工科学生初学线性代数，通常都会感到困难。这种情形在国内外皆然。瑞典数学家Lars Garding在其名著Encounter with Mathematics中说：“<font color="#ff0000"><b>如果不熟悉线性代数的概念，要去学习自然科学，现在看来就和文盲差不多。</b></font>”，然而<b><font color="#ff0000">“按照现行的国际标准，线性代数是通过公理化来表述的，它是第二代数学模型，...，这就带来了教学上的困难。”</font></b>事实上，当我们开始学习线性代数的时候，不知不觉就进入了“第二代数学模型”的范畴当中，这意味着数学的表述方式和抽象性有了一次全面的进化，对于从小一直在“第一代数学模型”，即以实用为导向的、具体的数学模型中学习的我们来说，在没有并明确告知的情况下进行如此剧烈的paradigm shift，不感到困难才是奇怪的。</font><br /><font size="3">大部分工科学生，往往是在学习了一些后继课程，如数值分析、数学规划、矩阵论之后，才逐渐能够理解和熟练运用线性代数。即便如此，不少人即使能够很熟练地以线性代数为工具进行科研和应用工作，但对于很多这门课程的初学者提出的、看上去是很基础的问题却并不清楚。比如说：</font><br /><font size="3"><font color="#0000ff"><b>* 矩阵究竟是什么东西？向量可以被认为是具有n个相互独立的性质（维度）的对象的表示，矩阵又是什么呢？我们如果认为矩阵是一组列（行）向量组成的新的复合向量的展开式，那么为什么这种展开式具有如此广泛的应用？特别是，为什么偏偏二维的展开式如此有用？如果矩阵中每一个元素又是一个向量，那么我们再展开一次，变成三维的立方阵，是不是更有用？</b></font></font><br /><font size="3"><font color="#0000ff"><b>* 矩阵的乘法规则究竟为什么这样规定？为什么这样一种怪异的乘法规则却能够在实践中发挥如此巨大的功效？很多看上去似乎是完全不相关的问题，最后竟然都归结到矩阵的乘法，这难道不是很奇妙的事情？难道在矩阵乘法那看上去莫名其妙的规则下面，包含着世界的某些本质规律？如果是的话，这些本质规律是什么？</b></font></font><br /><font size="3"><font color="#0000ff"><b>* 行列式究竟是一个什么东西？为什么会有如此怪异的计算规则？行列式与其对应方阵本质上是什么关系？为什么只有方阵才有对应的行列式，而一般矩阵就没有（不要觉得这个问题很蠢，如果必要，针对m x n矩阵定义行列式不是做不到的，之所以不做，是因为没有这个必要，但是为什么没有这个必要）？而且，行列式的计算规则，看上去跟矩阵的任何计算规则都没有直观的联系，为什么又在很多方面决定了矩阵的性质？难道这一切仅是巧合？</b></font></font><br /><font size="3"><font color="#0000ff"><b>* 矩阵为什么可以分块计算？分块计算这件事情看上去是那么随意，为什么竟是可行的？</b></font></font><br /><font size="3"><font color="#0000ff"><b>* 对于矩阵转置运算AT，有(AB)T = BTAT，对于矩阵求逆运算A-1，有(AB)-1 = B-1A-1。两个看上去完全没有什么关系的运算，为什么有着类似的性质？这仅仅是巧合吗？</b></font></font><br /><font size="3"><font color="#0000ff"><b>* 为什么说P-1AP得到的矩阵与A矩阵“相似”？这里的“相似”是什么意思？</b></font></font><br /><font size="3"><font color="#0000ff"><b>* 特征值和特征向量的本质是什么？它们定义就让人很惊讶，因为Ax =λx，一个诺大的矩阵的效应，竟然不过相当于一个小小的数λ，确实有点奇妙。但何至于用“特征”甚至“本征”来界定？它们刻划的究竟是什么？</b></font></font><br /><font size="3">这样的一类问题，经常让使用线性代数已经很多年的人都感到为难。就好像大人面对小孩子的刨根问底，最后总会迫不得已地说“就这样吧，到此为止”一样，面对这样的问题，很多老手们最后也只能用：“就是这么规定的，你接受并且记住就好”来搪塞。然而，这样的问题如果不能获得回答，线性代数对于我们来说就是一个粗暴的、不讲道理的、莫名其妙的规则集合，我们会感到，自己并不是在学习一门学问，而是被不由分说地“抛到”一个强制的世界中，只是在考试的皮鞭挥舞之下被迫赶路，全然无法领略其中的美妙、和谐与统一。直到多年以后，我们已经发觉这门学问如此的有用，却仍然会非常迷惑：怎么这么凑巧？</font><br /><font size="3">我认为，这是我们的线性代数教学中直觉性丧失的后果。上述这些涉及到“如何能”、“怎么会”的问题，仅仅通过纯粹的数学证明来回答，是不能令提问者满意的。比如，如果你通过一般的证明方法论证了矩阵分块运算确实可行，那么这并不能够让提问者的疑惑得到解决。他们真正的困惑是：矩阵分块运算为什么竟然是可行的？究竟只是凑巧，还是说这是由矩阵这种对象的某种本质所必然决定的？如果是后者，那么矩阵的这些本质是什么？只要对上述那些问题稍加考虑，我们就会发现，所有这些问题都不是单纯依靠数学证明所能够解决的。像我们的教科书那样，凡事用数学证明，最后培养出来的学生，只能熟练地使用工具，却欠缺真正意义上的理解。</font><br /><font size="3">自从1930年代法国布尔巴基学派兴起以来，数学的公理化、系统性描述已经获得巨大的成功，这使得我们接受的数学教育在严谨性上大大提高。然而数学公理化的一个备受争议的副作用，就是一般数学教育中直觉性的丧失。数学家们似乎认为直觉性与抽象性是矛盾的，因此毫不犹豫地牺牲掉前者。然而包括我本人在内的很多人都对此表示怀疑，我们不认为直觉性与抽象性一定相互矛盾，特别是在数学教育中和数学教材中，帮助学生建立直觉，有助于它们理解那些抽象的概念，进而理解数学的本质。反之，如果一味注重形式上的严格性，学生就好像被迫进行钻火圈表演的小白鼠一样，变成枯燥的规则的奴隶。</font><br /><font size="3">对于线性代数的类似上述所提到的一些直觉性的问题，两年多来我断断续续地反复思考了四、五次，为此阅读了好几本国内外线性代数、数值分析、代数和数学通论性书籍，其中像前苏联的名著《数学：它的内容、方法和意义》、龚昇教授的《线性代数五讲》、前面提到的Encounter with Mathematics（《数学概观》）以及Thomas A. Garrity的《数学拾遗》都给我很大的启发。不过即使如此，我对这个主题的认识也经历了好几次自我否定。比如以前思考的一些结论曾经写在自己的blog里，但是现在看来，这些结论基本上都是错误的。因此打算把自己现在的有关理解比较完整地记录下来，一方面是因为我觉得现在的理解比较成熟了，可以拿出来与别人探讨，向别人请教。另一方面，如果以后再有进一步的认识，把现在的理解给推翻了，那现在写的这个snapshot也是很有意义的。</font><br /><font size="3">因为打算写得比较多，所以会分几次慢慢写。也不知道是不是有时间慢慢写完整，会不会中断，写着看吧。</font><br />--------------------------------------------------------------------------<br /><font size="3">今天先谈谈对线形空间和矩阵的几个核心概念的理解。这些东西大部分是凭着自己的理解写出来的，基本上不抄书，可能有错误的地方，希望能够被指出。但我希望做到直觉，也就是说能把数学背后说的实质问题说出来。</font><br /><font size="3">首先说说空间(space)，这个概念是现代数学的命根子之一，从拓扑空间开始，一步步往上加定义，可以形成很多空间。线形空间其实还是比较初级的，如果在里面定义了范数，就成了赋范线性空间。赋范线性空间满足完备性，就成了巴那赫空间；赋范线性空间中定义角度，就有了内积空间，内积空间再满足完备性，就得到希尔伯特空间。</font><br /><font size="3">总之，空间有很多种。你要是去看某种空间的数学定义，大致都是“存在一个集合，在这个集合上定义某某概念，然后满足某些性质”，就可以被称为空间。这未免有点奇怪，为什么要用“空间”来称呼一些这样的集合呢？大家将会看到，其实这是很有道理的。</font><br /><font size="3">我们一般人最熟悉的空间，毫无疑问就是我们生活在其中的（按照牛顿的绝对时空观）的三维空间，从数学上说，这是一个三维的欧几里德空间，我们先不管那么多，先看看我们熟悉的这样一个空间有些什么最基本的特点。仔细想想我们就会知道，这个三维的空间：1. 由很多（实际上是无穷多个）位置点组成；2. 这些点之间存在相对的关系；3. 可以在空间中定义长度、角度；4. <b><font color="#ff0000">这个空间可以容纳运动，这里我们所说的运动是从一个点到另一个点的移动（变换），而不是微积分意义上的“连续”性的运动，</font></b></font><br /><font size="3">上面的这些性质中，最最关键的是第4条。第1、2条只能说是空间的基础，不算是空间特有的性质，凡是讨论数学问题，都得有一个集合，大多数还得在这个集合上定义一些结构（关系），并不是说有了这些就算是空间。而第3条太特殊，其他的空间不需要具备，更不是关键的性质。只有第4条是空间的本质，也就是说，<b><font color="#ff0000">容纳运动是空间的本质特征。</font></b></font><br /><font size="3">认识到了这些，我们就可以把我们关于三维空间的认识扩展到其他的空间。<b><font color="#ff0000">事实上，不管是什么空间，都必须容纳和支持在其中发生的符合规则的运动（变换）。你会发现，在某种空间中往往会存在一种相对应的变换，比如拓扑空间中有拓扑变换，线性空间中有线性变换，仿射空间中有仿射变换，其实这些变换都只不过是对应空间中允许的运动形式而已。</font></b></font><br /><font size="3">因此只要知道<b><font color="#ff0000">，“空间”是容纳运动的一个对象集合，而变换则规定了对应空间的运动。</font></b></font><br /><font size="3">下面我们来看看线性空间。线性空间的定义任何一本书上都有，但是既然我们承认线性空间是个空间，那么有两个最基本的问题必须首先得到解决，那就是：</font><br /><font size="3">1. 空间是一个对象集合，线性空间也是空间，所以也是一个对象集合。那么线性空间是什么样的对象的集合？或者说，线性空间中的对象有什么共同点吗？</font><br /><font size="3">2. 线性空间中的运动如何表述的？也就是，线性变换是如何表示的？</font><br /><font size="3">我们先来回答第一个问题，回答这个问题的时候其实是不用拐弯抹角的，可以直截了当的给出答案。<b><font color="#ff0000">线性空间中的任何一个对象，通过选取基和坐标的办法，都可以表达为向量的形式。</font></b>通常的向量空间我就不说了，举两个不那么平凡的例子：</font><br /><font size="3">L1. 最高次项不大于n次的多项式的全体构成一个线性空间，也就是说，这个线性空间中的每一个对象是一个多项式。如果我们以x0, x1, ..., xn为基，那么任何一个这样的多项式都可以表达为一组n+1维向量，其中的每一个分量ai其实就是多项式中x(i-1)项的系数。值得说明的是，基的选取有多种办法，只要所选取的那一组基线性无关就可以。这要用到后面提到的概念了，所以这里先不说，提一下而已。</font><br /><font size="3">L2. 闭区间[a, b]上的n阶连续可微函数的全体，构成一个线性空间。也就是说，这个线性空间的每一个对象是一个连续函数。对于其中任何一个连续函数，根据魏尔斯特拉斯定理，一定可以找到最高次项不大于n的多项式函数，使之与该连续函数的差为0，也就是说，完全相等。这样就把问题归结为L1了。后面就不用再重复了。</font><br /><font size="3">所以说，向量是很厉害的，只要你找到合适的基，用向量可以表示线性空间里任何一个对象。这里头大有文章，因为向量表面上只是一列数，但是其实由于它的有序性，所以除了这些数本身携带的信息之外，还可以在每个数的对应位置上携带信息。为什么在程序设计中数组最简单，却又威力无穷呢？根本原因就在于此。这是另一个问题了，这里就不说了。</font><br /><font size="3">下面来回答第二个问题，这个问题的回答会涉及到线性代数的一个最根本的问题。</font><br /><font size="3">线性空间中的运动，被称为线性变换。也就是说，你从线性空间中的一个点运动到任意的另外一个点，都可以通过一个线性变化来完成。那么，线性变换如何表示呢？<b><font color="#ff0000">很有意思，在线性空间中，当你选定一组基之后，不仅可以用一个向量来描述空间中的任何一个对象，而且可以用矩阵来描述该空间中的任何一个运动（变换）。而使某个对象发生对应运动的方法，就是用代表那个运动的矩阵，乘以代表那个对象的向量。</font></b></font><br /><font size="3">简而言之，<b><font color="#ff0000">在线性空间中选定基之后，向量刻画对象，矩阵刻画对象的运动，用矩阵与向量的乘法施加运动。</font></b></font><br /><font size="3">是的，矩阵的本质是运动的描述。如果以后有人问你矩阵是什么，那么你就可以响亮地告诉他，<u><b><font color="#ff0000">矩阵的本质是运动的描述</font></b></u>。（chensh，说你呢！）</font><br /><font size="3">可是多么有意思啊，向量本身不是也可以看成是n x 1矩阵吗？这实在是很奇妙，<b><font color="#ff0000">一个空间中的对象和运动竟然可以用相类同的方式表示。</font></b>能说这是巧合吗？如果是巧合的话，那可真是幸运的巧合！可以说，线性代数中大多数奇妙的性质，均与这个巧合有直接的关系。</font><br /><font size="3">接着理解矩阵。</font><br /><br /><font size="3">上面说“矩阵是运动的描述”，到现在为止，好像大家都还没什么意见。但是我相信早晚会有数学系出身的网友来拍板转。因为运动这个概念，在数学和物理里是跟微积分联系在一起的。我们学习微积分的时候，总会有人照本宣科地告诉你，初等数学是研究常量的数学，是研究静态的数学，高等数学是变量的数学，是研究运动的数学。大家口口相传，差不多人人都知道这句话。但是真知道这句话说的是什么意思的人，好像也不多。简而言之，在我们人类的经验里，运动是一个连续过程，从A点到B点，就算走得最快的光，也是需要一个时间来<b><u>逐点</u></b>地经过AB之间的路径，这就带来了连续性的概念。而连续这个事情，如果不定义极限的概念，根本就解释不了。古希腊人的数学非常强，但就是缺乏极限观念，所以解释不了运动，被芝诺的那些著名悖论（飞箭不动、飞毛腿阿喀琉斯跑不过乌龟等四个悖论）搞得死去活来。因为这篇文章不是讲微积分的，所以我就不多说了。有兴趣的读者可以去看看齐民友教授写的《重温微积分》。我就是读了这本书开头的部分，才明白“高等数学是研究运动的数学”这句话的道理。</font><br /><font size="3">不过在我这个《理解矩阵》的文章里，“运动”的概念不是微积分中的连续性的运动，而是瞬间发生的变化。比如这个时刻在A点，经过一个“运动”，一下子就“<b><u>跃迁</u></b>”到了B点，其中不需要经过A点与B点之间的任何一个点。这样的“运动”，或者说“跃迁”，是违反我们日常的经验的。不过了解一点量子物理常识的人，就会立刻指出，量子（例如电子）在不同的能量级轨道上跳跃，就是瞬间发生的，具有这样一种跃迁行为。所以说，自然界中并不是没有这种运动现象，只不过宏观上我们观察不到。但是不管怎么说，“运动”这个词用在这里，还是容易产生歧义的，说得更确切些，应该是“跃迁”。因此这句话可以改成：</font><br /><font size="3">“矩阵是线性空间里跃迁的描述”。</font><br /><font size="3">可是这样说又太物理，也就是说太具体，而不够数学，也就是说不够抽象。因此我们最后换用一个正牌的数学术语——<b><u>变换</u></b>，来描述这个事情。这样一说，大家就应该明白了，<font color="#ff0000"><b>所谓变换，其实就是空间里从一个点（元素/对象）到另一个点（元素/对象）的跃迁</b></font>。比如说，拓扑变换，就是在拓扑空间里从一个点到另一个点的跃迁。再比如说，仿射变换，就是在仿射空间里从一个点到另一个点的跃迁。附带说一下，这个仿射空间跟向量空间是亲兄弟。做计算机图形学的朋友都知道，尽管描述一个三维对象只需要三维向量，但所有的计算机图形学变换矩阵都是4 x 4的。说其原因，很多书上都写着“为了使用中方便”，这在我看来简直就是企图蒙混过关。真正的原因，是因为在计算机图形学里应用的图形变换，实际上是在仿射空间而不是向量空间中进行的。想想看，在向量空间里相一个向量平行移动以后仍是相同的那个向量，而现实世界等长的两个平行线段当然不能被认为同一个东西，所以计算机图形学的生存空间实际上是仿射空间。而仿射变换的矩阵表示根本就是4 x 4的。又扯远了，有兴趣的读者可以去看《计算机图形学——几何工具算法详解》。</font><br /><font size="3">一旦我们理解了“变换”这个概念，矩阵的定义就变成：</font><br /><font size="3"><font color="#ff0000"><b>“矩阵是线性空间里的变换的描述。”</b></font></font><br /><font size="3">到这里为止，我们终于得到了一个看上去比较数学的定义。不过还要多说几句。教材上一般是这么说的，在一个线性空间V里的一个线性变换T，当选定一组基之后，就可以表示为矩阵。因此我们还要说清楚到底什么是线性变换，什么是基，什么叫选定一组基。线性变换的定义是很简单的，设有一种变换T，使得对于线性空间V中间任何两个不相同的对象x和y，以及任意实数a和b，有：<br />T(ax + by) = aT(x) + bT(y)，<br />那么就称T为线性变换。</font><br /><font size="3">定义都是这么写的，但是光看定义还得不到直觉的理解。线性变换究竟是一种什么样的变换？我们刚才说了，变换是从空间的一个点跃迁到另一个点，而线性变换，就是从一个线性空间V的某一个点跃迁到另一个线性空间W的另一个点的运动。这句话里蕴含着一层意思，就是说一个点不仅可以变换到同一个线性空间中的另一个点，而且可以变换到另一个线性空间中的另一个点去。不管你怎么变，只要变换前后都是线性空间中的对象，这个变换就一定是线性变换，也就一定可以用一个非奇异矩阵来描述。而你用一个非奇异矩阵去描述的一个变换，一定是一个线性变换。有的人可能要问，这里为什么要强调非奇异矩阵？所谓非奇异，只对方阵有意义，那么非方阵的情况怎么样？这个说起来就会比较冗长了，最后要把线性变换作为一种映射，并且讨论其映射性质，以及线性变换的核与像等概念才能彻底讲清楚。我觉得这个不算是重点，如果确实有时间的话，以后写一点。<b>以下我们只探讨最常用、最有用的一种变换，就是在同一个线性空间之内的线性变换。也就是说，下面所说的矩阵，不作说明的话，就是方阵，而且是非奇异方阵。学习一门学问，最重要的是把握主干内容，迅速建立对于这门学问的整体概念，不必一开始就考虑所有的细枝末节和特殊情况，自乱阵脚。</b></font><br /><font size="3">接着往下说，什么是基呢？这个问题在后面还要大讲一番，这里只要<b><font color="#ff0000">把基看成是线性空间里的坐标系就可以了。</font></b>注意是坐标系，不是坐标值，这两者可是一个“对立矛盾统一体”。这样一来，“选定一组基”就是说在线性空间里选定一个坐标系。就这意思。</font><br /><font size="3">好，最后我们把矩阵的定义完善如下：</font><br /><font size="3"><font color="#ff0000"><b>“矩阵是线性空间中的线性变换的一个描述。在一个线性空间中，只要我们选定一组基，那么对于任何一个线性变换，都能够用一个确定的矩阵来加以描述。”</b></font></font><br /><font size="3"><b>理解这句话的关键，在于把“线性变换”与“线性变换的一个描述”区别开。</b>一个是那个对象，一个是对那个对象的表述。就好像我们熟悉的面向对象编程中，一个对象可以有多个引用，每个引用可以叫不同的名字，但都是指的同一个对象。如果还不形象，那就干脆来个很俗的类比。</font><br /><font size="3">比如有一头猪，你打算给它拍照片，只要你给照相机选定了一个镜头位置，那么就可以给这头猪拍一张照片。这个照片可以看成是这头猪的一个描述，但只是一个片面的的描述，因为换一个镜头位置给这头猪拍照，能得到一张不同的照片，也是这头猪的另一个片面的描述。所有这样照出来的照片都是这同一头猪的描述，但是又都不是这头猪本身。</font><br /><font size="3"><font color="#ff0000"><b>同样的，对于一个线性变换，只要你选定一组基，那么就可以找到一个矩阵来描述这个线性变换。换一组基，就得到一个不同的矩阵。所有这些矩阵都是这同一个线性变换的描述，但又都不是线性变换本身。</b></font></font><br /><font size="3">但是这样的话，问题就来了如果你给我两张猪的照片，我怎么知道这两张照片上的是同一头猪呢？同样的，你给我两个矩阵，我怎么知道这两个矩阵是描述的同一个线性变换呢？如果是同一个线性变换的不同的矩阵描述，那就是本家兄弟了，见面不认识，岂不成了笑话。</font><br /><font size="3">好在，我们可以找到同一个线性变换的矩阵兄弟们的一个性质，那就是：</font><br /><font size="3">若矩阵A与B是同一个线性变换的两个不同的描述（之所以会不同，是因为选定了不同的基，也就是选定了不同的坐标系），则一定能找到一个非奇异矩阵P，使得A、B之间满足这样的关系：</font><br /><font size="3">A = P-1BP</font><br /><font size="3">线性代数稍微熟一点的读者一下就看出来，这就是相似矩阵的定义。没错，<b><font color="#ff0000">所谓相似矩阵，就是同一个线性变换的不同的描述矩阵。</font></b>按照这个定义，同一头猪的不同角度的照片也可以成为相似照片。俗了一点，不过能让人明白。</font><br /><font size="3">而在上面式子里那个矩阵P，其实就是A矩阵所基于的基与B矩阵所基于的基这两组基之间的一个变换关系。关于这个结论，可以用一种非常直觉的方法来证明（而不是一般教科书上那种形式上的证明），如果有时间的话，我以后在blog里补充这个证明。</font><br /><font size="3">这个发现太重要了。<b><font color="#ff0000">原来一族相似矩阵都是同一个线性变换的描述啊！</font></b>难怪这么重要！工科研究生课程中有矩阵论、矩阵分析等课程，其中讲了各种各样的相似变换，比如什么相似标准型，对角化之类的内容，都要求变换以后得到的那个矩阵与先前的那个矩阵式相似的，为什么这么要求？因为只有这样要求，才能保证变换前后的两个矩阵是描述同一个线性变换的。当然，同一个线性变换的不同矩阵描述，从实际运算性质来看并不是不分好环的。有些描述矩阵就比其他的矩阵性质好得多。这很容易理解，同一头猪的照片也有美丑之分嘛。所以矩阵的相似变换可以把一个比较丑的矩阵变成一个比较美的矩阵，而保证这两个矩阵都是描述了同一个线性变换。</font><br /><font size="3">这样一来，矩阵作为线性变换描述的一面，基本上说清楚了。但是，事情没有那么简单，或者说，线性代数还有比这更奇妙的性质，那就是，<b><font color="#ff0000">矩阵不仅可以作为线性变换的描述，而且可以作为一组基的描述。</font><font color="#ff0000">而作为变换的矩阵，不但可以把线性空间中的一个点给变换到另一个点去，而且也能够把线性空间中的一个坐标系（基）表换到另一个坐标系（基）去。而且，变换点与变换坐标系，具有异曲同工的效果。线性代数里最有趣的奥妙，就蕴含在其中。理解了这些内容，线性代数里很多定理和规则会变得更加清晰、直觉。</font></b></font><img src ="http://www.cppblog.com/mybios/aggbug/15222.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mybios/" target="_blank">李锦俊</a> 2006-11-16 15:01 <a href="http://www.cppblog.com/mybios/archive/2006/11/16/15222.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>