• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            牽著老婆滿街逛

            嚴(yán)以律己,寬以待人. 三思而后行.
            GMail/GTalk: yanglinbo#google.com;
            MSN/Email: tx7do#yahoo.com.cn;
            QQ: 3 0 3 3 9 6 9 2 0 .

            向量幾何在游戲編程中的使用

            來源:http://www.vbgamedev.com/newc/VECTOR_MATH.htm

            向量幾何在游戲編程中的使用

            -Twinsen編寫

             

             

              Andre Lamothe說:“向量幾何是游戲程序員最好的朋友”。一點(diǎn)不假,向量幾何在游戲編程中的地位不容忽視,因?yàn)樵谟螒虺绦騿T的眼中,顯示屏幕就是一個(gè)坐標(biāo)系,運(yùn)動(dòng)物體的軌跡就是物體在這個(gè)坐標(biāo)系曲線運(yùn)動(dòng)結(jié)果,而描述這些曲線運(yùn)動(dòng)的,就是向量。使用向量可以很好的模擬物理現(xiàn)象以及基本的AI。

             

            <1>簡(jiǎn)單的2-D追蹤
            Andre Lamothe說:“向量幾何是游戲程序員最好的朋友”。一點(diǎn)不假,向量幾何在游戲編程中的地位不容忽視,因?yàn)樵谟螒虺绦騿T的眼中,顯示屏幕就是一個(gè)坐標(biāo)系,運(yùn)動(dòng)物體的軌跡就是物體在這個(gè)坐標(biāo)系曲線運(yùn)動(dòng)結(jié)果,而描述這些曲線運(yùn)動(dòng)的,就是向量。使用向量可以很好的模擬物理現(xiàn)象以及基本的AI。

            現(xiàn)在,先來點(diǎn)輕松的,復(fù)習(xí)一下中學(xué)知識(shí)

            向量v
            (用粗體字母表示向量)也叫矢量,是一個(gè)有大小有方向的量。長(zhǎng)度為1的向量稱為單位向量,也叫幺矢,這里記為E。長(zhǎng)度為0的向量叫做零向量,記為0,零向量沒有確定方向,換句話說,它的方向是任意的。

            一、向量的基本運(yùn)算



            1、向量加法:a+b等于使b的始點(diǎn)與a的終點(diǎn)重合時(shí),以a的始點(diǎn)為始點(diǎn),以b的終點(diǎn)為終點(diǎn)的向量。

            2、向量減法:a-b等于使b的始點(diǎn)與a的始點(diǎn)重合時(shí),以b的終點(diǎn)為始點(diǎn),以a的終點(diǎn)為終點(diǎn)的向量。

            3、 數(shù)量乘向量:k*a,k>0時(shí),等于a的長(zhǎng)度擴(kuò)大k倍;k=0時(shí),等于0向量;k<0時(shí),等于a的長(zhǎng)度擴(kuò)大|k|倍然后反向。

            4、向量的內(nèi)積(數(shù)量積、點(diǎn)積): a.b=|a|*|b|*cosA 等于向量a的長(zhǎng)度乘上b的長(zhǎng)度再乘上ab之間夾角的余弦。
               它的幾何意義就是a的長(zhǎng)度與ba上的投影長(zhǎng)度的乘積,或者是b的長(zhǎng)度與ab上投影長(zhǎng)的乘積,它是一個(gè)標(biāo)量,而
            且可正可負(fù)。因此互相垂直的向量的內(nèi)積為0。



            5、向量的矢積(叉積): a x b = |a|*|b|*sinA*v = c, |a|是a的長(zhǎng)度,|b|是b的長(zhǎng)度,A是ab之間的銳夾角,v是與a,b所決定的平面垂直的幺矢,即axbab都垂直。a,b,c構(gòu)成右手系,即右手拇指伸直,其余四指按由ab的銳角蜷曲,此時(shí)拇指所指方向就是c的方向。因此axb!=bxa,bxa是手指朝ba的銳角蜷曲時(shí),拇指指向的方向,它和c相反,即-ca x b的行列式計(jì)算公式在左右手坐標(biāo)系下是不同的,如上圖所示。兩個(gè)向量的矢積是一個(gè)向量。

            6、正交向量的內(nèi)積:互相垂直的兩個(gè)向量是正交的,正交向量的內(nèi)積為零。a.b = |a|.|b|*cos(PI/2) = |a|.|b|*0 = 0。

            二、向量的性質(zhì)

            沒有下面的這些性質(zhì)做基礎(chǔ),我們后面向量技巧的推導(dǎo)將無法進(jìn)行。

            1) a + b = b + a
            2) (a + b) + c = a + (b + c)
            3) a + 0 = 0 + a = a
            4) a + (-a) = 0
            5) k*(l*a) = (k*l)*a = a*(k*l)
            6) k*(a + b) = k*a + k*b
            7) (k + l)*a = k*a + l*a
            8) 1*a = a

            9) a.b = b.a
            10)a.(b + c) = a.b + a.c
            11)k*(a.b) = (k*a).b = a.(k*b)
            12)0.a = 0
            13)a.a = |a|^2


            三、自由向量的代數(shù)(分量)表示

            1、向量在直角坐標(biāo)中的代數(shù)表示方法:

            a=(x,y)



            其中x,y分別是向量在x軸和y軸上的分量。任何一個(gè)在直角坐標(biāo)軸上的分量為(x,y)的向量都相等。比如上圖中的每個(gè)向量都表示為(-2,1)。

            或者寫成a=x*i+y*j,ij的線性組合,這里i是x軸方向的單位向量(1,0),j是y軸方向的單位向量(0,1),因此i正交于j。任意一個(gè)2-D向量都可以表成ij的線性組合。

            |i| = |j| = 1

            2、向量的代數(shù)(分量)表示的運(yùn)算:

            向量加法分量表示:a+b=(xa,ya)+(xb,yb)=(xa+xb,ya+yb)

            向量減法分量表示:a-b=(xa,ya)-(xb,yb)=(xa-xb,ya-yb)

            向量的內(nèi)積(數(shù)量積、點(diǎn)積)分量表示:

            a.b
            =(xa * i + ya * j).(xb * i + yb * j)
            = xa * i * xb * i + xa * i * yb * j + ya * j * xb * i + ya * j * yb * j
            =(xa * xb) * (i * i) + (xa * yb) * (i * j) + (xb * ya) * (i * j) + (ya * yb) * (j * j)
            = xa * xb + ya * yb


            3、向量長(zhǎng)度(模)的計(jì)算以及單位化(歸一化):

            設(shè)a=(x,y),則
            |a| = |(x,y)| = |x*i + y*j| = sqrt(x^2*i^2 + y^2*j^2) = sqrt(x^2 + y^2),這里sqrt是開平方符號(hào)。

            a的單位向量為a/|a|,即(x,y)/sqrt(x^2 + y^2)。

            四、簡(jiǎn)單的2-D追蹤

            現(xiàn)在,有了向量的基本知識(shí),我們就可以分析一個(gè)常見的問題-屏幕上一點(diǎn)到另一點(diǎn)的追蹤,其實(shí)這一問題也可理解為畫線問題,畫線的算法有很多:DDA畫線法、中點(diǎn)畫線法以及高效的Bresenham算法。但這些算法一般只是畫一些兩端固定的線段時(shí)所使用的方法,再做一些動(dòng)態(tài)的點(diǎn)與點(diǎn)之間的跟蹤時(shí)顯得不很靈活。使用向量的方法可以很好的解決此類問題。

            現(xiàn)在假設(shè)你正在編寫一個(gè)飛行射擊游戲,你的敵人需要一種很厲害的武器-跟蹤導(dǎo)彈,這種武器在行進(jìn)的同時(shí)不斷的修正自己與目標(biāo)之間的位置關(guān)系,使得指向的方向總是玩家,而不論玩家的位置在哪里,這對(duì)一個(gè)水平不高的玩家(我?)來說可能將是滅頂之災(zāi),玩家可能很詫異敵人會(huì)擁有這么先進(jìn)的秘密武器,但對(duì)于你來說只需要再程序循環(huán)中加入幾行代碼
            ,它們的原理是向量的單位化和基本向量運(yùn)算。

            首先我們要知道玩家的位置(x_player, y_player),然后,我們的導(dǎo)彈就可以通過計(jì)算得到一個(gè)有初始方向的速度,速度的方向根據(jù)玩家的位置不斷修正,它的實(shí)質(zhì)是一個(gè)向量減法的計(jì)算過程。速度的大小我們自己來設(shè)置,它可快可慢,視游戲難易度而定,它的實(shí)質(zhì)就是向量單位化和數(shù)乘向量的過程。具體算法是:導(dǎo)彈的更新速度(vx_missile, vy_missile) = 玩家的位置(x_player, y_player) - 導(dǎo)彈的位置(x_missile, y_missile),然后再對(duì)(vx_missile, vy_missile)做縮小處理,導(dǎo)彈移動(dòng),判斷是否追到玩家,重新更新速度,縮小...

            看一下這個(gè)簡(jiǎn)單算法的代碼:



            // 假設(shè)x_player,y_player是玩家位置分量
            // x_missile,y_missile是導(dǎo)彈位置分量
            // xv_missile,yv_missile是導(dǎo)彈的速度分量
            // 讓我們開始吧!

            float n_missile ; // 這是玩家位置與導(dǎo)彈位置之間向量的長(zhǎng)度
            float v_rate ; // 這是導(dǎo)彈的速率縮放比率

            // 計(jì)算一下玩家與導(dǎo)彈之間的位置向量
            xv_missile = x_player-x_missile ; // 向量減法,方向由導(dǎo)彈指向玩家,x分量
            yv_missile = y_player-y_missile ; // y分量

            // 計(jì)算一下它的長(zhǎng)度
            n_missile = sqrt( xv_missile*xv_missile + yv_missile*yv_missile ) ;

            // 歸一化導(dǎo)彈的速度向量:
            xv_missile /= n_missile ;
            yv_missile /= n_missile ;

            // 此時(shí)導(dǎo)彈的速率為1,注意這里用速率。
            // 導(dǎo)彈的速度分量滿足xv_missile^2+yv_missile^2=1
            // 好!現(xiàn)在導(dǎo)彈的速度方向已經(jīng)被修正,它指向玩家。
            // 由于現(xiàn)在的導(dǎo)彈速度太快,為了緩解一下緊張的氣氛,我要給導(dǎo)彈減速
            v_rate = 0.2f ; // 減速比率
            xv_missile *= v_rate ; // 這里的速率縮放比率,你可以任意調(diào)整大小
            yv_missile *= v_rate ; // 可以加速:v_rate大于1;減速v_rate大于0小于1,這里就這么做!

            // 導(dǎo)彈行進(jìn)!導(dǎo)彈勇敢的沖向玩家!
            x_missile += xv_missile ;
            y_missile += yv_missile ;

            // 然后判斷是否攻擊成功

            現(xiàn)在,你編寫的敵人可以用跟蹤導(dǎo)彈攻擊玩家了。你也可以稍加修改,變?yōu)橹本€攻擊武器。這樣比較普遍。
            基本的跟蹤效果用向量可以很好的模擬。

            此時(shí),我們只用到了所述向量知識(shí)的很少的一部分。其他的知識(shí)會(huì)慢慢用到游戲中。這次先介紹到這里。
            下次我將說說利用向量模擬2-D物體任意角度返彈的技巧:)但是!別忘了復(fù)習(xí)一下向量的基礎(chǔ)知識(shí),我們要用到它們。

             

            <2>2-D物體任意角度的反彈

            第一次我說了一下向量知識(shí)的基礎(chǔ)內(nèi)容和一點(diǎn)使用技巧,淺顯的展示了它在游戲編程中的作用。這次深入一些,充分利用向量的性質(zhì)模仿一個(gè)物理現(xiàn)象。

            首先,我要介紹一下將要使用的兩個(gè)基本但非常重要的技巧。

            一、求與某個(gè)向量a正交的向量b



            根據(jù)向量?jī)?nèi)積的性質(zhì)以及正交向量之間的關(guān)系,有:

            設(shè)a=(xa,ya),b=(xb,yb)

            a.b = 0
            => xa*xb + ya*yb = 0
            => xa*xb = -ya*yb
            => xa/-ya = yb/xb
            => xb = -ya , yb = xa 或 xb = ya , yb = -xa

            則向量(xa,ya)的正交向量為(xb,yb)=(-ya,xa)
            比如上圖中,向量(2,3)的逆時(shí)針旋轉(zhuǎn)90度的正交向量是(-3,2),順時(shí)針旋轉(zhuǎn)90度的正交向量為(3,-2)。

            這樣,任給一個(gè)非零向量(x,y),則它相對(duì)坐標(biāo)軸逆時(shí)針轉(zhuǎn)90度的正交向量為(-y,x),順時(shí)針轉(zhuǎn)90度的正交向量為(y,-x)。

            二、計(jì)算一個(gè)向量b與另一向量a共線的兩個(gè)相反的投影向量

            我們看一下上面的圖,很明顯,cosA(A=X)關(guān)于y軸對(duì)稱,是偶函數(shù),因此cosA = cos(-A),
            又因?yàn)閏osA是周期函數(shù),且周期是2*PI,則有cos(A+2*PI) = cosA = cos(-A) = cos(-A+2*PI),
            則根據(jù)cosA = cos(2*PI-A)以及a.b = |a|*|b|*cosA,有

            a.b = |a|*|b|*cosA = |a|*|b|*cos(2*PI-A)



            現(xiàn)在,根據(jù)上圖,就有a.b = |a|*|b|*cosA = |a|*|b|*cos(2*PI-A) = ax*bx + ay*by

            按照這個(gè)規(guī)則,當(dāng)上面的bc的模相等時(shí),有|a|*|b| = |a|*|c|,進(jìn)一步的,當(dāng)它們與a的夾角A = B時(shí),就有

            a.b = |a|*|b|*cosA = |a|*|c|*cosB = a.c ,相應(yīng)的有
            a.b = |a|*|b|*cosA = |a|*|b|*cos(2*PI-A) = |a|*|c|*cosB = |a|*|c|*cos(2*PI-B) = a.c 也就是
            ax*bx + ay*by = ax*cx + ay*cy

            我們還注意到在一個(gè)周期內(nèi),比如在[0,2*PI]中,cosA有正負(fù)兩種情況,分別是:在(0,PI/2)&(3*PI/2, 2*PI)為正,在(PI/2,3/2*PI)為負(fù)。好,知道了這件事情之后,再看a.b = |a|*|b|*cosA,|a|和|b|都為正,所以a.b的正負(fù)性就由cosA決定,換句話說,a.b與它們夾角A的余弦cos有相同的符號(hào)。所以,還看上面的圖,我們就有:

            1)當(dāng)A在(0, PI/2)&(3*PI/2, 2*PI)中,此時(shí)2*PI-A在(-PI/2,0)&(0, PI/2)中,a.b為正
            2)當(dāng)A在(PI/2, 3*PI/2)中,此時(shí)2*PI-A也在(PI/2, 3*PI/2)中,a.b為負(fù)


            現(xiàn)在我們?cè)賮砜匆幌峦O喾矗▕A角為PI)向量bb'與同一個(gè)向量a的兩個(gè)內(nèi)積之間有什么關(guān)系。
            首先B + B'= 2*PI - PI = PI,所以有b = -b', b' = -b,即

            (bx, by) = (-b'x, -b'y) = -(b'x, b'y)
            (b'x, b'y) = (-bx, -by) = -(bx, by)

            所以
            a.b =
            (ax, ay) . (bx, by) = (ax, ay) . -(b'x, b'y) = a.-b'= -(a.b')
            a.b'= (ax, ay) . (b'x, b'y) = (ax, ay) . -(bx, by) = a.-b = -(a.b)

            我們看到,一個(gè)向量b的同模相反向量b'與向量a的內(nèi)積a.b',等于ba的內(nèi)積的相反數(shù)-(a.b)

            好,有了上面的基礎(chǔ),我們就可以求一個(gè)向量b與另一向量a共線的兩個(gè)相反的投影向量cc'了。

            要求ba上的投影向量c,我們可以用一個(gè)數(shù)乘上一個(gè)單位向量,這個(gè)單位向量要和a方向一至,我們記為a1。而這個(gè)數(shù)就是ba上的投影長(zhǎng)。

            先來求單位向量a1,我們知道它就是向量a乘上它自身長(zhǎng)度的倒數(shù)(數(shù)乘向量),它的長(zhǎng)度我們
            可以求出,就是m = sqrt(ax^2 + ay^2),所以a1就是(ax/m, ay/m),記為(a1x, a1y)。

            再求投影長(zhǎng)/c/(注意//與||的區(qū)別,前者是投影長(zhǎng),可正可負(fù)也可為零,后者是實(shí)際的長(zhǎng)度,衡為非負(fù))。 根據(jù)內(nèi)積的幾何意義:一個(gè)向量b點(diǎn)乘另一個(gè)向量a1,等于ba1上投影長(zhǎng)與a1的長(zhǎng)的乘積。那我們要求ba上的投影長(zhǎng),就用它點(diǎn)乘a的單位向量a1就可以了,因?yàn)閱挝幌蛄康拈L(zhǎng)度為1,b的投影長(zhǎng)/c/乘上1還等于投影長(zhǎng)自身,即:

            /c/ = b.a1 = (bx, by) . (a1x, a1y) = bx * a1x + by * a1y

            好,我們得到了c的投影長(zhǎng),現(xiàn)在就可以求出c:

            c = /c/*a1 = ( (bx * a1x + by * a1y)*a1x, (bx * a1x + by * a1y)*a1y )

            總結(jié)一下,就是c = (b.a1)*a1

            我們看到,ba1的夾角在(0, PI/2)之間,因此它們的點(diǎn)積/c/是個(gè)正值。因此當(dāng)它乘a1之后,得到向量的方向就是a1的方向。

            現(xiàn)在來看b',它是b的同模相反向量,它和a1的夾角在(PI/2, 3*PI/2)之間,因此b'點(diǎn)乘a1之后得到/c'/是個(gè)負(fù)值,它再乘a1,得到向量的方向和a1相反。我們知道一個(gè)向量b的同模相反向量b'與向量a的內(nèi)積a.b',等于ba的內(nèi)積的相反數(shù)-(a.b)。因此,/c'/ = -/c/,也就是說,它們的絕對(duì)值相等,符號(hào)相反。因此它們同乘一個(gè)a1,得到的的兩個(gè)模相等向量cc'共線。

            讓我們把它完成:

            (b'.a1) = -(b.a1)
            => -(b'.a1) = (b.a1), 好,代入c = (b.a1)*a1,得到

            c = -(b'.a1)*a1
            => (b'.a1)*a1 = -c = c'

            c = ( b . a1 )
            * a1 = (-b'. a1) * a1
            c'= ( b'. a1 )
            * a1 = (-b . a1) * a1

            至此為止,我們得出結(jié)論:當(dāng)一個(gè)向量b與另一個(gè)向量a的夾角在(0, PI/2)&(3*PI/2, 2*PI)之間,它在a方向上的投影向量c就是
            c = ( b . a1 ) * a1,其中a1a的單位向量;它在a相反方向的投影向量c'c'= ( b'. a1 ) * a1,其中向量b'b的同模相反向量。

            相反的,也可以這樣說:當(dāng)一個(gè)向量b'與另一個(gè)向量a的夾角在(PI/2, 3*PI/2)之間,它在a相反方向上的
            投影向量c'
            c'= ( b'. a1 ) * a1,其中 a1a的單位向量;它在a方向上的投影向量cc = ( b . a1 ) * a1。其中向量bb'的同模相反向量。

            特別的,點(diǎn)乘兩個(gè)單位向量,得到它們夾角的余弦值:

            E.E = |E|*|E|*cosA = 1*1*cosA = cosA

            好了,可完了。 現(xiàn)在就可以看一下

            三、使用向量模擬任意角度反彈的原理

            根據(jù)初等物理,相互接觸的物體在受到外力具有接觸面相對(duì)方向相對(duì)運(yùn)動(dòng)趨勢(shì)的時(shí)候,接觸面會(huì)發(fā)生形變從而產(chǎn)生相互作用的彈力。
            彈力使物體形變或形變同時(shí)運(yùn)動(dòng)形式發(fā)生改變。在知道了這件事情之后,我們開始具體討論下面這種情況:



            矩形框和小球碰撞,碰撞時(shí)間極短,墻面無限光滑從而碰撞過程沒有摩擦,碰撞時(shí)間極短,沒有能量損失...總之是一個(gè)理想的物理環(huán)境。我們?cè)谶@種理想環(huán)境下討論,小球與墻面發(fā)生了完全彈性碰撞,且入射角和反射角相等:A=A',B=B',C=C',...。虛線是法線,它和墻面垂直。小球?qū)⒃诰匦慰蛑杏罒o休止的碰撞下去,且每次碰撞過程中入射角和反射角都相等。


            我們?cè)倬唧w點(diǎn),現(xiàn)在假設(shè)上面那個(gè)矩形墻壁的上下面平行于x軸,左右面平行于y軸。這樣太好了,我們?cè)诰帉懗绦虻臅r(shí)候只要判斷當(dāng)球碰到上下表面的時(shí)候?qū)方向速度值取返,碰到左右表面時(shí)將x方向速度值取返就行了,這種方法常常用在簡(jiǎn)單物理模型和規(guī)則邊界框的游戲編程上,這樣可以簡(jiǎn)化很多編程步驟,編寫簡(jiǎn)單游戲時(shí)可以這樣處理。可事實(shí)不總是像想向中的那么好。如果情況像下面這樣:



            雖然在碰撞過程中入射角仍然等于反射角,但是邊界的角度可沒那么“純”了,它們的角度是任意的,這樣就不能簡(jiǎn)單的將x方向或者y方向的速度取返了,我們要另找解決辦法。

            我們現(xiàn)在的任務(wù)是:已知物體的速度向量S和邊界向量b,求它的反射向量F。我們先來看一下在碰撞過程中都有哪些向量關(guān)系:



            設(shè)b是障礙向量,S是入射速度向量,F是反射速度向量,也就是我們要計(jì)算的向量。A是入射角度,A'是反射角度,A=A'。Nb的法向量,即N垂直于bn是與N共線的向量,n'N方向的單位向量。T是垂直于N的向量。根據(jù)向量加法,現(xiàn)在有關(guān)系:

            (1) S + n = T
            (2) n + T = F

            合并,得

            F = 2*T - S

            我們已經(jīng)找到了計(jì)算F的公式了。這里S是已知的,我們要計(jì)算一下T,看(1)式:

            T = S + n

            要計(jì)算TS是已知的,就要計(jì)算一下n。我們知道,nSN方向上投影得到的,S已知所以要得到n就要再計(jì)算一下N,而N又是和b垂直的。還記得剛才我們導(dǎo)出的使用向量的兩個(gè)技巧吧,這里我們都要用到:

            1、任給一個(gè)非零向量(x,y),則它相對(duì)坐標(biāo)軸逆時(shí)針轉(zhuǎn)90度的垂直向量為(-y,x),順時(shí)針轉(zhuǎn)90度垂直向量為(y,-x)。

            2、當(dāng)一個(gè)向量b與另一個(gè)向量a的夾角在(0, PI/2)&(3*PI/2, 2*PI)之間,它在a方向上的投影向量c就是c = ( b . a1 ) * a1,其中a1a的單位向量;它在a相反方向的投影向量c'c'= ( b'. a1 ) * a1,其中向量b'b的同模相反向量。


            我們知道了b,用技巧1可以計(jì)算出N。然后歸一化N計(jì)算出n',再用技巧2,這里Sn'之間的夾角在(PI/2, 3*PI/2)中,因此要想用c = ( b. a1 ) * a1,必須要使b = -S,a1=n'。這樣就計(jì)算出了n。然后根據(jù)上面的(1)式計(jì)算出T,好了,有了TF = 2*T - S ,你就擁有了一切!


            計(jì)算出的F就是物體碰撞后的速度向量,在2-D中它有兩個(gè)分量x和y,3-D中有x,y,z三個(gè)分量。這里也證明了使用向量的一個(gè)好處就是在一些類似這樣關(guān)系推導(dǎo)過程中不用去考慮坐標(biāo)問題,而直接的用簡(jiǎn)單的向量就可以進(jìn)行。

            這里注意我們的障礙向量b在實(shí)際的編程中是用障礙的兩個(gè)端點(diǎn)坐標(biāo)相減計(jì)算出的,計(jì)算的時(shí)候不需要考慮相減的順序問題。因?yàn)殡m然用不同的相減順序得到b的方向相反,且計(jì)算得到的單位法向量n'方向也相反(看上圖的虛線部分),但是當(dāng)用-S去點(diǎn)乘單位法向量n'之后得到的值也是相反的,它有一個(gè)自動(dòng)的調(diào)節(jié)功能:現(xiàn)在假設(shè)以b為界,S一側(cè)為正方向。則如果單位法向量n'是正方向,與-S點(diǎn)積值也是正,正的n'再乘正點(diǎn)積得正的n;如果單位法向量為負(fù)方向,與-S點(diǎn)積值也為負(fù)值,負(fù)的n'再乘負(fù)的點(diǎn)積得到的n為正方向。總之n的方向是不變的,算出的F當(dāng)然也是不變的。

            四、編碼實(shí)現(xiàn)它

            現(xiàn)在我想編碼實(shí)現(xiàn)它,但之前有一點(diǎn)我想說一下,可能讀者已經(jīng)想到了,在反彈之前我們要先判斷什么時(shí)候開始反彈,也就是什么時(shí)候碰撞,這是一個(gè)碰撞檢測(cè)問題,本來這是我們應(yīng)該先要解決的問題,但我想把它放到下一次在具體說,所以這里的編碼省略碰撞檢測(cè)的一步,直接計(jì)算反彈速度向量!目的是把上述理論迅速用到算法中去。




            // 在游戲循環(huán)中
            // 移動(dòng)的物體簡(jiǎn)化為質(zhì)點(diǎn),位置是x=0.0f,y=0.0f
            // 質(zhì)點(diǎn)速度向量的分量是Svx=4.0f,Svy=2.0f
            // 障礙向量是bx=14.0f-6.0f=8.0f,by=4.0f-12.0f=-8.0f
            // 則障礙向量的垂直向量是Nx=-8.0f,Ny=-8.0f

            // 這里可以加入碰撞檢測(cè)
            // 現(xiàn)在假設(shè)已經(jīng)碰撞完畢,開始反彈計(jì)算!

            // 計(jì)算N的長(zhǎng)度
            float lengthN = sqrt( Nx*Nx + Ny*Ny ) ;
            // 歸一化N為n'
            float n0x = Nx / lengthN ; // n0x就是n'的x分量
            float n0y = Ny / lengthN ; // n0y就是n'的y分量
            // 計(jì)算n,就是S在N方向上的投影向量
            // 根據(jù)b'= (-b.a1').a1',有n = (-S.n').n'
            float nx = -(Svx*n0x+Svy*n0y)*n0x ; // n的x分量
            float ny = -(Svx*n0x+Svy*n0y)*n0y ; // n的y分量
            // 計(jì)算T
            // T = S + n
            float Tx = Svx + nx ; // T的x分量
            float Ty = Svy + ny ; // T的y分量
            // 有了T,有了F = 2*T - S,好了,你現(xiàn)在擁有一切了
            // 計(jì)算F
            float Fx = 2*Tx - Svx ; // F的x分量
            float Fy = 2*Ty - Svy ; // F的y分量
            // 現(xiàn)在已經(jīng)計(jì)算出了反彈后的速度向量了
            // 更新速度向量
            Svx = Fx ;
            Svy = Fy ;
            // 質(zhì)點(diǎn)移動(dòng)
            x+=Svx ;
            y+=Svy ;
            // 現(xiàn)在你就可以看到質(zhì)點(diǎn)被無情的反彈回去了
            // 而且是按照物理法則在理想環(huán)境下模擬


            就是這么簡(jiǎn)單,一個(gè)物理現(xiàn)象就可以模擬出來,但是還不完善,只是針對(duì)直線障礙,且沒有碰撞檢測(cè),下次分析一下后者,還是用向量的知識(shí)。這次先到這,See u next time!


            <3>2-D邊界碰撞檢測(cè)
            -Twinsen編寫

            -本人水平有限,疏忽錯(cuò)誤在所難免,還請(qǐng)各位數(shù)學(xué)高手、編程高手不吝賜教
            -我的Email-address: popyy@netease.com

            一、使用向量進(jìn)行障礙檢測(cè)的原理

            上次說了使用向量模擬任意角度的反彈,這次談?wù)勊那疤?--障礙碰撞。

            在游戲中進(jìn)行障礙碰撞檢測(cè),基本思路是這樣的:給定一個(gè)障礙范圍,判斷物體在這次移動(dòng)后會(huì)不會(huì)進(jìn)入這個(gè)范圍,如果會(huì),就發(fā)生碰撞,否則不發(fā)生碰撞。在實(shí)際操作中,是用物體的邊界來判斷還是其他部位判斷完全取決于編程者。這時(shí)候,就可以從這個(gè)部位沿著速度的方向引出一條速度向量線,判斷一下這條線段(從檢測(cè)部位到速度向量終點(diǎn))和障礙邊界線有沒有交點(diǎn),如果有,這個(gè)交點(diǎn)就是碰撞點(diǎn)。



            上面物體A,在通過速度向量移動(dòng)之后將到達(dá)B位置。但是,這次移動(dòng)將不會(huì)順利進(jìn)行,因?yàn)槲覀儼l(fā)現(xiàn),碰撞發(fā)生了。碰撞點(diǎn)就在那個(gè)紅色區(qū)域中,也就是速度向量和邊界線的交點(diǎn)。 我們接下來的工作就是要計(jì)算這個(gè)交點(diǎn),這是一個(gè)解線性方程組的過程,那么我們將要用到一樣工具...


            二、一個(gè)解線性方程組的有力工具---克蘭姆(Cramer)法則

            首先要說明一下的是,這個(gè)法則是有局限性的,它必須在一個(gè)線性方程組的系數(shù)行列式非零的時(shí)候才能夠使用。別緊張,我會(huì)好好談?wù)勊鼈兊摹J紫茸屛襾頂⑹鲆幌逻@個(gè)法則(我會(huì)試著讓你感覺到這不是一堂數(shù)學(xué)課):

            如果線性方程組:

            A11*X1 + A12*X2 + ... + A1n*Xn = b1
            A21*X1 + A22*X2 + ... + A2n*Xn = b2
            ...................................
            An1*X1 + An2*X2 + ... + Ann*Xn = bn

            的系數(shù)矩陣 A =
            __               __
            | A11 A12 ... A1n |
            | A21 A22 ... A2n |
            | ...............       |
            | An1 An2 ... Ann |
            --               --

            的行列式 |A| != 0
            線性方程組有解,且解是唯一的,并且解可以表示為:

            X1 = d1/d , X2 = d2/d , ... , Xn = dn/d (這就是/A/=d為什么不能為零的原因)

            這里d就是行列式/A/的值,dn(n=1,2,3...)是用線性方程組的常數(shù)項(xiàng)b1,b2,...,bn替換系數(shù)矩陣中的第n列的值得到的矩陣的行列式的值,即:

            d1 = | b1 A12 ... A1n |
            | b2 A22 ... A2n |
            | ..............       |
            | bn An2 ... Ann |

             

            d2 =  | A11 b1 ... A1n |
             | A21 b2 ... A2n |
             | ..............       |
             | An1 bn ... Ann |


            ...
             


                   

            | A11 A12 ... b1 |
             

            dn = | A21 A22 ... b2 |
            | ..............       |
            | An1 An2 ... bn |

             

            別去點(diǎn)擊關(guān)閉窗口按鈕!我現(xiàn)在就舉個(gè)例子,由于我們現(xiàn)在暫時(shí)只討論2-D游戲(3-D以后會(huì)循序漸進(jìn)的談到),就來個(gè)2-D線性方程組:

            (1) 4.0*X1 + 2.0*X2 = 5.0
            (2) 3.0*X1 + 3.0*X2 = 6.0

            這里有兩個(gè)方程,兩個(gè)未知量,則根據(jù)上面的Cramer法則:

                  | 4.0 2.0 |
            d = | 3.0 3.0 | = 4.0*3.0 - 2.0*3.0 = 6.0 (2階行列式的解法,'\'對(duì)角線相乘減去'/'對(duì)角線相乘)

                   | 5.0 2.0 |
            d1 = | 6.0 3.0 | = 5.0*3.0 - 2.0*6.0 = 3.0

                   | 4.0 5.0 |
            d2 = | 3.0 6.0 | = 4.0*6.0 - 5.0*3.0 = 9.0



            X1 = d1/d = 3.0/6.0 = 0.5
            X2 = d2/d = 9.0/6.0 = 1.5  

            好了,現(xiàn)在就得到了方程組的唯一一組解。
            是不是已經(jīng)掌握了用Cramer法則解2-D線性方程組了?如果是的話,我們繼續(xù)。

            三、深入研究

            這里的2-D障礙碰撞檢測(cè)的實(shí)質(zhì)就是判斷兩條線段是否有交點(diǎn),注意不是直線,是線段,兩直線有交點(diǎn)不一定直線上的線段也有交點(diǎn)。現(xiàn)在我們從向量的角度,寫出兩條線段的方程。

            現(xiàn)在有v1v2兩條線段,則根據(jù)向量加法:

            v1e = v1b + s*v1
            v2e = v2b + t*v2

            v1bv2b分別是兩線段的一端。s,t是兩個(gè)參數(shù),它們的范圍是[0.0,1.0],當(dāng)s,t=0.0時(shí),v1e=v1b,v2e=v2b;當(dāng)s,t=1.0時(shí),v1ev2e分別是兩線段的另一端。s,t取遍[0.0,1.0]則v1ev2e取遍兩線段的每一點(diǎn)。

            那么我們要判斷v1v2有沒有交點(diǎn),就讓v1e=v2e,看解出的s,t是不是在范圍內(nèi)就可以了:

            v1e = v2e
            => v1b + s*v1 = v2b + t*v2
            => s*v1 - t*v2 = v2b - v1b
            寫成分量形式:

            s*x_v1 - t*x_v2 = x_v2b - x_v1b
            s*y_v1 - t*y_v2 = y_v2b - y_v1b


            現(xiàn)在是兩個(gè)方程式,兩個(gè)未知數(shù),則根據(jù)Cramer法則:
             

            d = | x_v1 -x_v2 |

             | y_v1 -y_v2 |

             = | 4.0 -2.0 |

            | 1.0 -3.0 |

            = -10.0

             

             d1 = | x_v2b-x_v1b -x_v2 |

            | y_v2b-y_v1b -y_v2 |

             = | 5.0 -2.0 |

            | 2.0 -3.0 |  

            = -11.0  


            s = d1/d = -11.0/-10.0 = 1.1 > 1.0

            現(xiàn)在s已經(jīng)計(jì)算出來,沒有在[0.0,1.0]內(nèi),所以兩線段沒有交點(diǎn),從圖上看很直觀。t沒有必要再計(jì)算了。所以是物體與障礙沒有發(fā)生碰撞。如果計(jì)算出的s,t都在[0.0,1.0]內(nèi),則把它們帶入原方程組,計(jì)算出v1e或者v2e,它的分量就是碰撞點(diǎn)的分量。

            四、理論上的東西已經(jīng)夠多的了,開始寫程序

            我現(xiàn)在要寫一個(gè)用于處理障礙碰撞檢測(cè)的函數(shù),為了測(cè)試它,我還準(zhǔn)備安排一些障礙:

            這是一個(gè)凸多邊形,我讓一個(gè)質(zhì)點(diǎn)在初始位置(10,8),然后給它一個(gè)隨機(jī)速度,這個(gè)隨機(jī)速度的兩個(gè)分速度在區(qū)間[1.0,4.0]內(nèi),同時(shí)檢測(cè)是否與邊界發(fā)生碰撞。當(dāng)碰撞發(fā)生時(shí),就讓它回到初始位置,重新給一個(gè)隨機(jī)速度。

            // 首先我要記下凸多邊形的邊界坐標(biāo)
            float poly[2][8] = {
            { 6.0f , 2.0f , 4.0f , 8.0f , 14.0f , 18.0f , 14.0f , 6.0f } , // 所有點(diǎn)的x分量,最后一個(gè)點(diǎn)和第一個(gè)點(diǎn)重合
            { 2.0f , 6.0f , 10.0f , 14.0f , 12.0f , 8.0f , 4.0f , 2.0f } // 所有點(diǎn)的y分量
            } ;
            // 定義一些變量
            float x,y ; // 這是質(zhì)點(diǎn)的位置變量
            float vx , vy ; // 質(zhì)點(diǎn)的速度向量分量

            // 好,開始編寫碰撞檢測(cè)函數(shù)
            bool CollisionTest() { // 當(dāng)發(fā)生碰撞時(shí)返回true,否則返回false

            float s , t ; // 線段方程的兩個(gè)參數(shù)
            // 各個(gè)參量
            float x_v1 , x_v2 , y_v1 , y_v2 ;
            float x_v2b , x_v1b , y_v2b , y_v1b ;

            for( int i = 0 ; i < 8-1 ; ++i ) { // 循環(huán)到倒數(shù)第二個(gè)點(diǎn)

            // 障礙線段
            x_v1 = poly[0][i+1]-poly[0][i] ;
            y_v1 = poly[1][i+1]-poly[1][i] ;
            // 物體速度向量
            x_v2 = vx ;
            y_v2 = vy ;
            // 障礙向量初始點(diǎn)
            x_v1b = poly[0][i] ;
            y_v1b = poly[1][i] ;
            // 物體位置
            x_v2b = x ;
            y_v2b = y ;
            // 計(jì)算d,d1和d2
            //    | x_v1 -x_v2 |  
            //d = | y_v1 -y_v2 |
            //     | x_v2b-x_v1b -x_v2 |
            //d1 = | y_v2b-y_v1b -y_v2 |
            //     | x_v1 x_v2b-x_v1b |
            //d2 = | y_v1 y_v2b-y_v1b |

            d = (x_v1*(-y_v2))-((-x_v2)*y_v1) ;
            d1 = ((x_v2b-x_v1b)*(-y_v2))-((-x_v2)*(y_v2b-y_v1b)) ;
            d2 = (x_v1*(y_v2b-y_v1b))-((x_v2b-x_v1b)*y_v1) ;

            // 判斷d是否為零
            if( abs(d) < 0.001f ) // 如果等于零做近似處理,abs()用于求絕對(duì)值
            d = 0.001f ;

            // 計(jì)算參量s,t
            s = d1/d ;
            t = d2/d ;
            // 判斷是否發(fā)生碰撞
            // 如果發(fā)生了就返回true
            if( 0.0f <= s && 1.0f >= s && 0.0f <= t && 1.0f >= t )
            return true ;

            } // for( int i = 0 ; i < 8-1 ; ++i )

            // 沒有發(fā)生碰撞,返回false
            return false ;

            } // end of function

            // 現(xiàn)在對(duì)函數(shù)做測(cè)試
            // 初始化質(zhì)點(diǎn)
            x = 10.0f , y = 8.0f ;
            vx = vy = (float)(rand()%4+1) ;

            // 進(jìn)入主循環(huán)中
            // 假設(shè)現(xiàn)在已經(jīng)在主循環(huán)中
            if( CollisionTest() ) { // 如果物體與質(zhì)點(diǎn)發(fā)生碰撞
            x = 10.0f , y = 8.0f ;
            vx = vy = (float)(rand()%4+1) ;
            }
            // 質(zhì)點(diǎn)移動(dòng)
            x+=vx ;
            y+=vy ;


            現(xiàn)在你就可以結(jié)合上次的討論模擬一個(gè)完整的理想物理情景:一個(gè)物體在不規(guī)則障礙中移動(dòng)、反彈,永不停息...除非...

            至此為止我們討論了2-D游戲的障礙碰撞檢測(cè)以及它的編程實(shí)現(xiàn),在此過程中涉及到了線性代數(shù)學(xué)的知識(shí),以后隨著深入還會(huì)不斷的加入更多的數(shù)學(xué)、物理知識(shí)。

             

            <4>2-D物體間的碰撞響應(yīng)
            這次我要分析兩個(gè)球體之間的碰撞響應(yīng),這樣我們就可以結(jié)合以前的知識(shí)來編寫一款最基本的2-D臺(tái)球游戲了,雖然粗糙了點(diǎn),但卻是個(gè)很好的開始,對(duì)嗎?

            一、初步分析

            中學(xué)時(shí)候上物理課能夠認(rèn)真聽講的人(我?哦,不包括我)應(yīng)該很熟悉的記得:當(dāng)兩個(gè)球體在一個(gè)理想環(huán)境下相撞之后,它們的總動(dòng)量保持不變,它們的總機(jī)械能也守恒。但這個(gè)理想環(huán)境是什么樣的呢?理想環(huán)境會(huì)不會(huì)影響游戲的真實(shí)性?對(duì)于前者我們做出在碰撞過程中理想環(huán)境的假設(shè):

            1)首先我們要排除兩個(gè)碰撞球相互作用之外的力,也就是假設(shè)沒有外力作用于碰撞系統(tǒng)。
            2)假設(shè)碰撞系統(tǒng)與外界沒有能量交換。
            3)兩個(gè)球體相互作用的時(shí)間極短,且相互作用的內(nèi)力很大。

            有了這樣的假設(shè),我們就可以使用動(dòng)量守恒和動(dòng)能守恒定律來處理它們之間的速度關(guān)系了,因?yàn)?)確保沒有外力參與,碰撞系統(tǒng)內(nèi)部動(dòng)量守恒,我們就可以使用動(dòng)量守恒定律。2)保證了我們的碰撞系統(tǒng)的總能量不會(huì)改變,我們就可以使用動(dòng)能守恒定律。3)兩球發(fā)生完全彈性碰撞,不會(huì)粘在一起,沒有動(dòng)量、能量損失。

            而對(duì)于剛才的第二個(gè)問題,我的回答是不會(huì),經(jīng)驗(yàn)告訴我們,理想環(huán)境的模擬看起來也是很真實(shí)的。除非你是在進(jìn)行科學(xué)研究,否則完全可以這樣理想的去模擬。

            現(xiàn)在,我們可以通過方程來觀察碰撞前后兩球的速度關(guān)系。當(dāng)兩球球心移動(dòng)方向共線(1-D處理)時(shí)的速度,或不共線(2-D處理)時(shí)共線方向的速度分量滿足:

            (1)m1 * v1 + m2 * v2 = m1 * v1' + m2 * v2' (動(dòng)量守恒定律)
            (2)1/2 * m1 * v1^2 + 1/2 * m2 * v2^2 = 1/2 * m1 * v1'^2 + 1/2 * m2 * v2'^2 (動(dòng)能守恒定律)

            這里m1和m2是兩球的質(zhì)量,是給定的,v1和v2是兩球的初速度也是我們已知的,v1'和v2'是兩球的末速度,是我們要求的。好,現(xiàn)在我們要推導(dǎo)出v1'和v2'的表達(dá)式:

            由(1),得到v1' = (m1 * v1 + m2 * v2 - m2 * v2') / m1,代入(2),得
            1/2 * m1 * v1^2 + 1/2 * m2 * v2^2 = 1/2 * m1 * (m1 * v1 + m2 * v2 - m2 * v2')^2 + 1/2 * m2 * v2'^2

            => v2' = (2 * m2 * v1 + v2 * (m1 - m2)) / (m1 + m2),則
            => v1' = (2 * m1 * v2 + v1 * (m1 - m2)) / (m1 + m2)

            我們現(xiàn)在得到的公式可以用于處理當(dāng)兩球球心移動(dòng)方向共線(1-D處理)時(shí)的速度關(guān)系,或者不共線(2-D處理)時(shí)共線方向的速度分量的關(guān)系。不管是前者還是后者,我們都需要把它們的速度分解到同一個(gè)軸上才能應(yīng)用上述公式進(jìn)行處理。

            二、深入分析

            首先我要說明一件事情:當(dāng)兩球碰撞時(shí),它們的速度可以分解為球心連線方向的分速度和碰撞點(diǎn)切線方向的分速度。而由于它們之間相互作用的力只是在切點(diǎn)上,也就是球心連線方向上,因此我們只用處理這個(gè)方向上的力。而在切線方向上,它們不存在相互作用的力,而且在理想環(huán)境下也沒有外力,因此這個(gè)方向上的力在碰撞前后都不變,因此不處理。好,知道了這件事情之后,我們就知道該如何把兩球的速度分解到同一個(gè)軸上進(jìn)行處理。

            現(xiàn)在看上面的分析圖,s和t是我們根據(jù)兩個(gè)相碰球m1和m2的位置建立的輔助軸,我們一會(huì)就將把速度投影到它們上面。v1v2分別是m1和m2的初速度,v1'v2'是它們碰撞后的末速度,也就是我們要求的。s'是兩球球心的位置向量,t'是它的逆時(shí)針正交向量。s1s'的單位向量,t1t'的單位向量。

            我們的思路是這樣的:首先我們假設(shè)兩球已經(jīng)相碰(在程序中可以通過計(jì)算兩球球心之間的距離來判斷)。接下來我們計(jì)算一下s't',注意s't'的方向正反無所謂(一會(huì)將解釋),現(xiàn)在設(shè)m1球心為(m1x, m1y),m2球心為(m2x, m2y),則s'為(m1x-m2x, m1y-m2y),t'為(m2y-m1y, m1x-m2x)(第一篇的知識(shí))。

            則設(shè)
            sM = sqrt((m1x-m2x)^2+(m1y-m2y)^2),
            tM = sqrt((m2y-m1y)^2+(m1x-m2x)^2),有

            s1
            = ((m1x-m2x)/sM, (m1y-m2y)/sM) = (s1x, s1y)
            t1 = ((m2y-m1y)/tM, (m1x-m2x)/tM) = (t1x, t1y)

            現(xiàn)在s和t軸的單位向量已經(jīng)求出了,我們根據(jù)向量點(diǎn)乘的幾何意義,計(jì)算v1v2s1t1方向上的投影值,然后將s軸上投影值代
            入公式來計(jì)算s方向碰撞后的速度。注意,根據(jù)剛才的說明,t方向的速度不計(jì)算,因?yàn)闆]有相互作用的力,因此,t方向的分速度不變。所以我們要做的就是:把v1投影到s和t方向上,再把v2投影到s和t方向上,用公式分別計(jì)算v1v2在s方向上的投影的末速度,然后把得到的末速度在和原來v1v2在t方向上的投影速度再合成,從而算出v1'v2'。好,我們接著這個(gè)思路做下去:

            先算v1(v1x, v1y)在s和t軸的投影值,分別設(shè)為v1s和v1t:

            v1s = v1.s1
            => v1s = v1x * s1x + v1y * s1y

            v1t = v1.t1
            => v1t = v1x * t1x + v1y * t1y

            再算v2(v2x, v2y)在s和t軸的投影值,分別設(shè)為v2s和v2t:

            v2s = v2.s1
            => v2s = v2x * s1x + v2y * s1y

            v2t = v2.t1
            => v2t = v2x * t1x + v2y * t1y

            接下來用公式

            v1' = (2 * m1 * v2 + v1 * (m1 - m2)) / (m1 + m2)
            v2' = (2 * m2 * v1 + v2 * (m1 - m2)) / (m1 + m2)

            計(jì)算v1s和v2s的末值v1s'和v2s',重申v1t和v2t不改變:

            假設(shè)m1 = m2 = 1

            v1s' = (2 * 1 * v2s + v1s * (1 - 1)) / (1 + 1)
            v2s' = (2 * 1 * v1s + v2s * (1 - 1)) / (1 + 1)

            => v1s' = v2s
            => v2s' = v1s

            好,下一步,將v1s'和v1t再合成得到v1',將v2s'和v2t再合成得到v2',我們用向量和來做:

            首先求出v1t和v2t在t軸的向量v1t'v2t'(將數(shù)值變?yōu)橄蛄浚?/font>

            v1t' = v1t * t1 = (v1t * t1x, v1t * t1y)
            v2t' = v2t * t1 = (v2t * t1x, v2t * t1y)

            再求出v1s'和v2s'在s軸的向量v1s'v2s'(將數(shù)值變?yōu)橄蛄浚?strong>

            v1s'= v1s' * s1 = (v1s' * s1x, v1s' * s1y)
            v2s'
            = v2s' * s1 = (v2s' * s2x, v2s' * s2y)

            最后,合成,得

            v1' = v1t' + v1s' = (v1t * t1x + v1s' * s1x, v1t * t1y + v1s' * s1y)
            v2' = v2t' + v2s' = (v2t * t1x + v2s' * s2x, v2t * t1y + v2s' * s2y)

            從而就求出了v1'v2'。下面解釋為什么說s't'的方向正反無所謂:不論我們?cè)谟?jì)算s'時(shí)使用m1的球心坐標(biāo)減去m2的球心坐標(biāo)還是相反的相減順序,由于兩球的初速度的向量必有一個(gè)和s1是夾角大于90度小于270度的,而另外一個(gè)與s1的夾角在0度和90度之間或者說在270度到360度之間,則根據(jù)向量點(diǎn)積的定義|a|*|b|*cosA,計(jì)算的到的兩個(gè)投影值一個(gè)為負(fù)另一個(gè)為正,也就是說,速度方向相反,這樣就可以用上面的公式區(qū)求得末速度了。同時(shí),求出的末速度也是方向相反的,從而在轉(zhuǎn)換為v1s'v2s'時(shí)也是正確的方向。同樣的,求t'既可以是用s'逆時(shí)針90度得到也可以是順時(shí)針90度得到。

            三、編寫代碼

            按照慣例,該編寫代碼了,其實(shí)編寫的代碼和上面的推導(dǎo)過程極為相似。但為了完整,我還是打算寫出來。

            // 用于球體碰撞響應(yīng)的函數(shù),其中v1a和v2a為兩球的初速度向量,
            // v1f和v2f是兩球的末速度向量。
            // m1和m2是兩球的位置向量
            // s'的分量為(sx, sy),t'的分量為(tx, ty)
            // s1是s的單位向量,分量為(s1x, s1y)
            // t1是t的單位向量,分量為(t1x, t1y)

            void Ball_Collision(v1a, v2a, &v1f, &v2f, m1, m2){

            // 求出s'
            double sx = m1.x - m2.x ;
            double sy = m1.y - m2.y ;

            // 求出s1
            double s1x = sx / sqrt(sx*sx + sy*sy) ;
            double s1y = sy / sqrt(sx*sx + sy*sy) ;

            // 求出t'
            double tx = -sy ;
            double ty = sx ;

            // 求出t1
            double t1x = tx / sqrt(tx*tx + ty*ty) ;
            double t1y = ty / sqrt(tx*tx + ty*ty) ;

            // 求v1a在s1上的投影v1s
            double v1s = v1a.x * s1x + v1a.y * s1y ;

            // 求v1a在t1上的投影v1t
            double v1t = v1a.x * t1x + v1a.y * t1y ;

            // 求v2a在s1上的投影v2s
            double v2s = v2a.x * s1x + v2a.y * s1y ;

            // 求v2a在t1上的投影v2t
            double v2t = v2a.x * t1x + v2a.y * t1y ;

            // 用公式求出v1sf和v2sf
            double v1sf = v2s ;
            double v2sf = v1s ;

            // 最后一步,注意這里我們簡(jiǎn)化一下,直接將v1sf,v1t和v2sf,v2t投影到x,y軸上,也就是v1'和v2'在x,y軸上的分量
            // 先將v1sf和v1t轉(zhuǎn)化為向量

            double nsx = v1sf * s1x ;
            double nsy = v1sf * s1y ;
            double ntx = v1t * t1x ;
            double nty = v1t * t1y ;

            // 投影到x軸和y軸
            // x軸單位向量為(1,0),y軸為(0,1)
            // v1f.x = 1.0 * (nsx * 1.0 + nsy * 0.0) ;
            // v1f.y = 1.0 * (nsx * 0.0 + nsy * 1.0) ;
            // v1f.x+= 1.0 * (ntx * 1.0 + nty * 0.0) ;
            // v1f.y+= 1.0 * (ntx * 0.0 + nty * 1.0) ;

            v1f.x = nsx + ntx ;
            v1f.y = nsy + nty ;

            // 然后將v2sf和v2t轉(zhuǎn)化為向量

            nsx = v2sf * s1x ;
            nsy = v2sf * s1y ;
            ntx = v2t * t1x ;
            nty = v2t * t1y ;

            // 投影到x軸和y軸
            // x軸單位向量為(1,0),y軸為(0,1)
            // v2f.x = 1.0 * (nsx * 1.0 + nsy * 0.0) ;
            // v2f.y = 1.0 * (nsx * 0.0 + nsy * 1.0) ;
            // v2f.x+= 1.0 * (ntx * 1.0 + nty * 0.0) ;
            // v2f.y+= 1.0 * (ntx * 0.0 + nty * 1.0) ;

            v2f.x = nsx + ntx ;
            v2f.y = nsy + nty ;

            }// end of function

            呼~~是不是感覺有點(diǎn)亂阿?不管怎么樣,我有這種感覺。但我們確實(shí)完成了它。希望你能夠理解這個(gè)計(jì)算的過程,你完全可以依照這個(gè)過程自己編寫更高效的代碼,讓它看上去更清楚:)至此位置,我們已經(jīng)掌握了編寫一個(gè)臺(tái)球游戲的基本知識(shí)了,Let's make it!

            事實(shí)上,一切才剛剛起步,我們還有很多沒有解決的問題,比如旋轉(zhuǎn)問題,擊球的角度問題等等,你還會(huì)深入的研究一下,對(duì)嗎?一旦你有了目標(biāo),堅(jiān)持下去,保持激情,總會(huì)有成功的一天:)這次就到這里,下次我們接著研究,Bye for now~~


            <5>物體的旋轉(zhuǎn)
            歡迎回來這里!此次我們要討論向量的旋轉(zhuǎn)問題,包括平面繞點(diǎn)旋轉(zhuǎn)和空間繞軸旋轉(zhuǎn)兩部分。對(duì)于游戲程序員來說,有了向量的旋轉(zhuǎn),就代表有了操縱游戲中物體旋轉(zhuǎn)的鑰匙,而不論它是一個(gè)平面精靈還是一組空間的網(wǎng)格體亦或是我們放在3-D世界某一點(diǎn)的相機(jī)。我們?nèi)孕杞柚蛄縼硗瓿晌覀兇舜蔚穆贸蹋@還不夠,我們還需要一個(gè)朋友,就是矩陣,一個(gè)我們用來對(duì)向量進(jìn)行線性變換的GooL GuY。就像我們剛剛提及向量時(shí)所做的一樣,我們來復(fù)習(xí)一下即將用到的數(shù)學(xué)知識(shí)。(這部分知識(shí)我只會(huì)一帶而過,因?yàn)槲覍阎攸c(diǎn)放在后面對(duì)旋轉(zhuǎn)問題的分析上)

            一、矩陣的基本運(yùn)算及其性質(zhì)

            對(duì)于3x3矩陣(也叫3x3方陣,行列數(shù)相等的矩陣也叫方陣)m和M,有

            1、矩陣加減法

            m +(-) M =

            [a b c]      [A B C]   [a+(-)A b+(-)B c+(-)C]
            [d e f] +(-) [D E F] = [d+(-)D e+(-)E f+(-)F]
            [g h i]      [G H I]   [g+(-)G h+(-)H i+(-)I]

            性質(zhì):
            1)結(jié)合律 m + (M + N) = (m + M)  + N
            2) 交換律 m + M = M + m

            2、數(shù)量乘矩陣

            k x M =

                [A B C]   [kxA kxB kxC]
            k x [D E F] = [kxD kxE kxF]
                [G H I]   [kxG kxH kxI]

            性質(zhì):

            k和l為常數(shù)
            1) (k + l) x M = k x M + l x M
            2) k x (m + M) = k x m + k x M
            3) k x (l x M) = (k x l) x M
            4) 1 x M = M
            5) k x (m x M) = (k x m) x M = m x (k x M)

            3、矩陣乘法

            m x M =

            [a b c]   [A B C}   [axA+bxD+cxG axB+bxE+cxH axC+bxF+cxI]
            [d e f] x [D E F] = [dxA+exD+fxG dxB+exE+fxH dxC+exF+fxI]
            [g h i]   [G H I]   [gxA+hxD+ixG gxB+hxE+ixH gxC+hxF+ixI]

            可以看出,矩陣相乘可以進(jìn)行的條件是第一個(gè)矩陣的列數(shù)等于第二個(gè)矩陣的行數(shù)。
            由矩陣乘法的定義看出,矩陣乘法不滿足交換率,即在一般情況下,m x M != M x m。

            性質(zhì):
            1) 結(jié)合律 (m x M) x N = m x (M x N)
            2) 乘法加法分配律 m x (M + N) = m x M + m x N ; (m + M) x N = m x N + M x N

            4、矩陣的轉(zhuǎn)置
                 
             

            m' = [a b c]'    [a d g]
            [
            d e f]  = [b e h
            [
            g h i]     [c  f i ]


             

            性質(zhì):
            1)(m x M)' = M' x m'
            2)(m')' = m
            3)(m + M)' = m' + M'
            4)(k x M)' = k x M'   

            5、單位矩陣
              
                  [1 0 0]
            E = [0 1 0] 稱為3級(jí)單位陣
                  [0 0 1]

            性質(zhì):對(duì)于任意3級(jí)矩陣M,有E x M = M ; M x E = M

            6、矩陣的逆

            如果3x3級(jí)方陣m,有m x M = M x m = E,這里E是3級(jí)單位陣,則可以說m是可逆的,它的逆矩陣為M,也記為m^-1。相反的,也可以說M是可逆的,逆矩陣為m,也記為M^-1。

            性質(zhì):
            1) (m^-1)^-1 = m
            2) (k x m)^-1 = 1/k x m^-1
            3)(m')^-1 = (m^-1)'
            4) (m x M)^-1 = M^-1 x n^-1

            矩陣求逆有幾種算法,這里不深入研究,當(dāng)我們用到的時(shí)候在討論。
            在我們建立了矩陣的概念之后,就可以用它來做坐標(biāo)的線性變換。好,現(xiàn)在我們開始來使用它。

             

            二、基礎(chǔ)的2-D繞原點(diǎn)旋轉(zhuǎn)

            首先是簡(jiǎn)單的2-D向量的旋轉(zhuǎn),以它為基礎(chǔ),我們會(huì)深入到復(fù)雜的3-D旋轉(zhuǎn),最后使我們可以在3-D中無所不能的任意旋轉(zhuǎn)。

            在2-D的迪卡爾坐標(biāo)系中,一個(gè)位置向量的旋轉(zhuǎn)公式可以由三角函數(shù)的幾何意義推出。比如上圖所示是位置向量R逆時(shí)針旋轉(zhuǎn)角度B前后的情況。在左圖中,我們有關(guān)系:

            x0 = |R| * cosA
            y0 = |R| * sinA
            =>
            cosA = x0 / |R|
            sinA = y0 / |R|

            在右圖中,我們有關(guān)系:

            x1 = |R| * cos(A+B)
            y1 = |R| * sin(A+B)

            其中(x1, y1)就是(x0, y0)旋轉(zhuǎn)角B后得到的點(diǎn),也就是位置向量R最后指向的點(diǎn)。我們展開cos(A+B)和sin(A+B),得到

            x1 = |R| * (cosAcosB - sinAsinB)
            y1 = |R| * (sinAcosB + cosAsinB)

            現(xiàn)在

            cosA = x0 / |R|
            sinA = y0 / |R|

            代入上面的式子,得到

            x1 = |R| * (x0 * cosB / |R| - y0 * sinB / |R|)
            y1 = |R| * (y0 * cosB / |R| + x0 * sinB / |R|)
            =>
            x1 = x0 * cosB - y0 * sinB
            y1 = x0 * sinB + y0 * cosB

            這樣我們就得到了2-D迪卡爾坐標(biāo)下向量圍繞圓點(diǎn)的逆時(shí)針旋轉(zhuǎn)公式。順時(shí)針
            旋轉(zhuǎn)就把角度變?yōu)樨?fù):

            x1 = x0 * cos(-B) - y0 * sin(-B)
            y1 = x0 * sin(-B) + y0 * cos(-B)
            =>
            x1 = x0 * cosB + y0 * sinB
            y1 = -x0 * sinB + y0 * cosB

            現(xiàn)在我要把這個(gè)旋轉(zhuǎn)公式寫成矩陣的形式,有一個(gè)概念我簡(jiǎn)單提一下,平面或空間里的每個(gè)線性變換(這里就是旋轉(zhuǎn)變換)都對(duì)應(yīng)一個(gè)矩陣,叫做變換矩陣。對(duì)一個(gè)點(diǎn)實(shí)施線性變換就是通過乘上該線性變換的矩陣完成的。好了,打住,不然就跑題了。

            所以2-D旋轉(zhuǎn)變換矩陣就是:

            [cosA  sinA]      [cosA -sinA]
            [-sinA cosA] 或者 [sinA cosA]

            我們對(duì)點(diǎn)進(jìn)行旋轉(zhuǎn)變換可以通過矩陣完成,比如我要點(diǎn)(x, y)繞原點(diǎn)逆時(shí)針旋轉(zhuǎn):

                      [cosA  sinA]  
            [x, y] x  [-sinA cosA] = [x*cosA-y*sinA  x*sinA+y*cosA]

            為了編程方便,我們把它寫成兩個(gè)方陣

            [x, y]   [cosA  sinA]   [x*cosA-y*sinA  x*sinA+y*cosA]
            [0, 0] x [-sinA cosA] = [0              0            ]

            也可以寫成

            [cosA -sinA]   [x 0]   [x*cosA-y*sinA  0]
            [sinA  cosA] x [y 0] = [x*sinA+y*cosA  0]


            三、2-D的繞任一點(diǎn)旋轉(zhuǎn)

            下面我們深入一些,思考另一種情況:求一個(gè)點(diǎn)圍繞任一個(gè)非原點(diǎn)的中心點(diǎn)旋轉(zhuǎn)。

            我們剛剛導(dǎo)出的公式是圍繞原點(diǎn)旋轉(zhuǎn)的公式,所以我們要想繼續(xù)使用它,就要把想要圍繞的那個(gè)非原點(diǎn)的中心點(diǎn)移動(dòng)到原點(diǎn)上來。按照這個(gè)思路,我們先將該中心點(diǎn)通過一個(gè)位移向量移動(dòng)到原點(diǎn),而圍繞點(diǎn)要保持與中心點(diǎn)相對(duì)位置不變,也相應(yīng)的按照這個(gè)位移向量位移,此時(shí)由于中心點(diǎn)已經(jīng)移動(dòng)到了圓點(diǎn),就可以讓同樣位移后的圍繞點(diǎn)使用上面的公式來計(jì)算旋轉(zhuǎn)后的位置了,計(jì)算完后,再讓計(jì)算出的點(diǎn)按剛才的位移向量 逆 位移,就得到圍繞點(diǎn)繞中心點(diǎn)旋轉(zhuǎn)一定角度后的新位置了。看下面的圖


            現(xiàn)在求左下方的藍(lán)色點(diǎn)圍繞紅色點(diǎn)旋轉(zhuǎn)一定角度后的新位置。由于紅色點(diǎn)不在原點(diǎn),所以可以通過紅色向量把它移動(dòng)到原點(diǎn),此時(shí)藍(lán)色的點(diǎn)也按照這個(gè)向量移動(dòng),可見,紅色和藍(lán)色點(diǎn)的相對(duì)位置沒有變。現(xiàn)在紅色點(diǎn)在原點(diǎn),藍(lán)色點(diǎn)可以用上面旋轉(zhuǎn)變換矩陣進(jìn)行旋轉(zhuǎn),旋轉(zhuǎn)后的點(diǎn)在通過紅色向量的的逆向量回到它實(shí)際圍繞下方紅色點(diǎn)旋轉(zhuǎn)后的位置。

            在這個(gè)過程中,我們對(duì)圍繞點(diǎn)進(jìn)行了三次線性變換:位移變換-旋轉(zhuǎn)變換-位移變換,我們把它寫成矩陣形式:

            設(shè)紅色向量為(rtx, rty)

            [x y 1]   [1   0   0]    [cosA  sinA 0]    [1    0     0]    [x' y' -]
            [0 1 0] x [0   1   0] x [-sinA cosA 0] x [0    1     0] = [-  -  -]
            [0 0 1]   [rtx rty 1]    [0       0     1]    [-rtx -rty 1]    [-  -  -]

            最后得到的矩陣的x'和y'就是我們旋轉(zhuǎn)后的點(diǎn)坐標(biāo)。

            注意到矩陣乘法滿足結(jié)合律:(m x M) x N = m x (M x N),我們可以先將所有的變換矩陣乘在一起,即

                  [1   0   0]    [cosA  sinA 0]   [1    0    0] 
            M = [0   1   0] x [-sinA cosA 0] x [0    1    0]  
                  [rtx rty 1]    [0       0     1]   [-rtx -rty 1]  

            然后再讓

            [x y 1]
            [0 1 0] x M
            [0 0 1]

            像這樣歸并變換矩陣是矩陣運(yùn)算一個(gè)常用的方法,因?yàn)楫?dāng)把諸多變換矩陣歸并為一個(gè)矩陣之后,對(duì)某點(diǎn)或向量的重復(fù)變換只需要乘一個(gè)矩陣就可以完成,減少了計(jì)算的開銷。

            本小節(jié)討論的這種“其他變換-繞點(diǎn)旋轉(zhuǎn)變換-其他變換”的思想很重要,因?yàn)橛袝r(shí)候復(fù)雜一些的旋轉(zhuǎn)變換不可能一步完成,必須使用這種旁敲側(cè)擊、化繁為簡(jiǎn)的方法,尤其是在3-D空間中,可能需要在真正做規(guī)定度數(shù)的旋轉(zhuǎn)前還要做一些其他必要旋轉(zhuǎn)變換,也就是要做很多次的旋轉(zhuǎn),但總體的思想還是為了把復(fù)雜的問題分成若干簡(jiǎn)單的問題去解決,而每一個(gè)簡(jiǎn)單問題都需要一個(gè)變換矩陣來完成,所以希望讀者深入思考一下這種方法。

            好,2-D的旋轉(zhuǎn)探討完畢。接下來,我們進(jìn)入3-D空間,討論更為復(fù)雜一些的旋轉(zhuǎn)。Here We Go!

             
            四、基礎(chǔ)的3-D繞坐標(biāo)軸方向旋轉(zhuǎn)

            就像2-D繞原點(diǎn)旋轉(zhuǎn)一樣,3-D的繞坐標(biāo)軸旋轉(zhuǎn)是3-D旋轉(zhuǎn)的基礎(chǔ),因?yàn)槠渌麖?fù)雜的3-D旋轉(zhuǎn)最后都會(huì)化簡(jiǎn)為繞坐標(biāo)軸旋轉(zhuǎn)。其實(shí),剛才我們推導(dǎo)出的在xoy坐標(biāo)面繞o旋轉(zhuǎn)的公式可以很容易的推廣到3-D空間中,因?yàn)樵?-D直角坐標(biāo)系中,三個(gè)坐標(biāo)軸兩兩正交,所以z軸垂直于xoy面,這樣,在xoy面繞o點(diǎn)旋轉(zhuǎn)實(shí)際上在3-D空間中就是圍繞z軸旋轉(zhuǎn),如下圖左所示:

            這張圖描述了左手系中某點(diǎn)在xoy、yoz、xoz面上圍繞原點(diǎn)旋轉(zhuǎn)的情況,同時(shí)也是分別圍繞z、x、y坐標(biāo)軸旋轉(zhuǎn)。可見在3-D空間中繞坐標(biāo)軸旋轉(zhuǎn)相當(dāng)于在相應(yīng)的2-D平面中圍繞原點(diǎn)旋轉(zhuǎn)。我們用矩陣來說明:

            設(shè)p(x, y, z)是3-D空間中的一點(diǎn),也可以說是一個(gè)位置向量,當(dāng)以上圖中的坐標(biāo)為準(zhǔn),p點(diǎn)所圍繞的中心軸指向你的屏幕之外時(shí),有

            p
            繞z軸逆時(shí)針和順時(shí)針旋轉(zhuǎn)角度A分別寫成:

            [x y z 1]    [cosA -sinA 0 0]    [x y z 1]   [cosA sinA  0 0]
            [0 1 0 0] x [sinA cosA  0 0] 和 [0 1 0 0] x [-sinA cosA 0 0] 
            [0 0 1 0]   [0    0     1 0]    [0 0 1 0]   [0     0    1 0]
            [0 0 0 1]   [0    0     0 1]    [0 0 0 1]   [0     0    0 1]

            p繞x軸逆時(shí)針和順時(shí)針旋轉(zhuǎn)角度A分別寫成:

            [x y z 1]   [1 0     0    0]    [x y z 1]   [1 0     0    0]
            [0 1 0 0] x [0 cos  -sinA 0] 和 [0 1 0 0] x [0 cosA  sinA 0] 
            [0 0 1 0]   [0 sin  cosA  0]    [0 0 1 0]   [0 -sinA cosA 0]
            [0 0 0 1]   [0 0    0     1]    [0 0 0 1]   [0 0     0    1]

            p繞y軸逆時(shí)針和順時(shí)針旋轉(zhuǎn)角度A分別寫成:

            [x y z 1]    [cosA  0 sinA 0]     [x y z 1]    [cosA 0  -sinA 0]
            [0 1 0 0] x [0     1    0   0]  和 [0 1 0 0] x [0     1  0    0] 
            [0 0 1 0]   [-sinA 0 cosA 0]     [0 0 1 0]    [sinA  0  cosA 0]
            [0 0 0 1]   [0      0   0    1]     [0 0 0 1]    [0     0  0    1]

            以后我們會(huì)把它們寫成這樣的標(biāo)準(zhǔn)4x4方陣形式,Why?為了便于做平移變換,還記得上小節(jié)做平移時(shí)我們把2x2方陣寫為3x3方陣嗎?

            讓我們繼續(xù)研究。我們?cè)侔呀Y(jié)論推廣一點(diǎn),讓它適用于所有和坐標(biāo)軸平行的軸,具體一點(diǎn),讓它適用于所有和y軸平行的軸。
            這個(gè)我們很快可以想到,可以按照2-D的方法“平移變換-旋轉(zhuǎn)變換-平移變換”來做到,看下圖

            要實(shí)現(xiàn)point繞axis旋轉(zhuǎn),我們把a(bǔ)xis按照一個(gè)位移向量移動(dòng)到和y軸重合的位置,也就是變換為axis',為了保持point和axis的相對(duì)位置不變,point也通過相同的位移向量做相應(yīng)的位移。好,現(xiàn)在移動(dòng)后的point就可以用上面的旋轉(zhuǎn)矩陣圍繞axis'也就是y軸旋轉(zhuǎn)了,旋轉(zhuǎn)后用相反的位移向量位移到實(shí)際圍繞axis相應(yīng)度數(shù)的位置。我們還是用矩陣來說明:

            假設(shè)axis為x = s, z = t,要point(x, y, z)圍繞它逆時(shí)針旋轉(zhuǎn)度數(shù)A,按照“平移變換-旋轉(zhuǎn)變換-位移變換”,我們有

            [x y z 1]   [1  0 0  0]   [cosA  0 sinA 0]   [1 0 0 0]   [x' y z' -]
            [0 1 0 0]   [0  1 0  0]   [0     1 0    0]   [0 1 0 0]   [-  - -  -]
            [0 0 1 0] x [0  0 1  0] x [-sinA 0 cosA 0] x [0 0 1 0] = [-  - -  -]
            [0 0 0 1]   [-s 0 -t 1]   [0     0 0    1]   [s 0 t 1]   [-  - -  -]

            則得到的(x', y, z')就是point圍繞axis旋轉(zhuǎn)角A后的位置。

            同理,平行于x軸且圍繞軸y=s,z=t逆時(shí)針旋轉(zhuǎn)角A的變換為

            [x y z 1]   [1  0 0  0]    [1    0    0     0]   [1 0 0 0]   [x  y' z' -]
            [0 1 0 0]   [0  1 0  0]    [0 cosA -sinA 0]   [0 1 0 0]   [-  -  -  -]
            [0 0 1 0] x [0  0 1  0] x [0 sinA cosA  0] x [0 0 1 0] = [-  -  -  -]
            [0 0 0 1]   [0 -s -t 1]    [0    0    0     1]   [0 s t 1]   [-  -  -  -]

            平行于z軸且圍繞軸x=s,y=t逆時(shí)針旋轉(zhuǎn)角A的變換為

            [x y z 1]    [1  0  0  0]   [cosA -sinA 0 0]   [1 0 0 0]    [x' y' z  -]
            [0 1 0 0]   [0  1  0  0]   [sinA cosA  0 0]   [0  1 0 0]   [-  -  -  -]
            [0 0 1 0] x [0  0  1  0] x [0    0     1   0] x [0 0 1 0] = [-  -  -  -]
            [0 0 0 1]    [-s -t 0  1]   [0    0     0   1]   [s  t 0 1]    [-  -  -  -]

            逆時(shí)針旋轉(zhuǎn)就把上面推出的相應(yīng)逆時(shí)針旋轉(zhuǎn)變換矩陣帶入即可。至此我們已經(jīng)討論了3-D空間基本旋轉(zhuǎn)的全部,接下來的一小節(jié)是我們3-D旋轉(zhuǎn)部分的重頭戲,也是3-D中功能最強(qiáng)大的旋轉(zhuǎn)變換。


            五、3-D繞任意軸的旋轉(zhuǎn)


            Wow!終于來到了最后一部分,這一節(jié)我們將綜合運(yùn)用上面涉及到的所有旋轉(zhuǎn)知識(shí),完成空間一點(diǎn)或著說位置向量圍繞空間任意方向旋轉(zhuǎn)軸的旋轉(zhuǎn)變換(我在下面介紹的一種方法是一個(gè)稍微繁瑣一點(diǎn)的方法,大體上看是利用幾個(gè)基本旋轉(zhuǎn)的綜合。我將在下一篇中介紹一個(gè)高檔一些的方法)。

            何謂任意方向的旋轉(zhuǎn)軸呢?其實(shí)就是空間一條直線。在空間解析幾何中,決定空間直線位置的兩個(gè)值是直線上一點(diǎn)以及直線的方向向量。在旋轉(zhuǎn)中,我們把這個(gè)直線稱為一個(gè)旋轉(zhuǎn)軸,因此,直線的這個(gè)方向向量我們叫它軸向量,它類似于3-D動(dòng)畫中四元數(shù)的軸向量。我們?cè)趯?shí)際旋轉(zhuǎn)之前的變換矩陣需要通過把這個(gè)軸向量移動(dòng)到原點(diǎn)來獲得。

            我們先討論旋轉(zhuǎn)軸通過原點(diǎn)的情況。目前為止對(duì)于3-D空間中的旋轉(zhuǎn),我們可以做的只是繞坐標(biāo)軸方向的旋轉(zhuǎn)。因此,當(dāng)我們考慮非坐標(biāo)軸方向旋轉(zhuǎn)的時(shí)候,很自然的想到,可以將這個(gè)旋轉(zhuǎn)軸通過變換與某一個(gè)坐標(biāo)軸重合,同時(shí),為了保持旋轉(zhuǎn)點(diǎn)和這個(gè)旋轉(zhuǎn)軸相對(duì)位置不變,旋轉(zhuǎn)點(diǎn)也做相應(yīng)的變換,然后,讓旋轉(zhuǎn)點(diǎn)圍繞相應(yīng)旋轉(zhuǎn)軸重合的坐標(biāo)軸旋轉(zhuǎn),最后將旋轉(zhuǎn)后的點(diǎn)以及旋轉(zhuǎn)軸逆變換回原來的位置,此時(shí)就完成了一點(diǎn)圍繞這個(gè)非坐標(biāo)軸方向旋轉(zhuǎn)軸的旋轉(zhuǎn)。我們?cè)賮砜磮D分析。

            圖中有一個(gè)紅色的分量為(x0, y0, z0)的軸向量,此外有一個(gè)藍(lán)色位置向量圍繞它旋轉(zhuǎn),由于這個(gè)軸向量沒有與任何一個(gè)坐標(biāo)軸平行,我們沒有辦法使用上面推導(dǎo)出的旋轉(zhuǎn)變換矩陣,因此必須將該軸變換到一個(gè)坐標(biāo)軸上,這里我們選擇了z軸。在變換紅色軸的同時(shí),為了保持藍(lán)色位置向量同該軸的相對(duì)位置不變,也做相應(yīng)的變換,然后就出現(xiàn)中圖描述的情況。接著我們就用可以用變換矩陣來圍繞z軸旋轉(zhuǎn)藍(lán)色向量相應(yīng)的度數(shù)。旋轉(zhuǎn)完畢后,再用剛才變換的逆變換把兩個(gè)向量相對(duì)位置不變地還原到初始位置,此時(shí)就完成了一個(gè)點(diǎn)圍繞任意過原點(diǎn)的軸的旋轉(zhuǎn),對(duì)于不過原點(diǎn)的軸我們?nèi)匀挥?#8220;位移變換-旋轉(zhuǎn)變換-位移變換”的方法,一會(huì)討論。

            在理解了基本思路之后,我們來研究一下變換吧!我們就按上圖將紅色軸變到z軸上,開始吧!

            首先我們假設(shè)紅軸向量是一個(gè)單位向量,因?yàn)檫@樣在一會(huì)求sin和cos時(shí)可以簡(jiǎn)化計(jì)算,在實(shí)際編程時(shí)可以先將軸向量標(biāo)準(zhǔn)化。然后我準(zhǔn)備分兩步把紅色軸變換到z軸上去:

            1)將紅色軸變換到y(tǒng)oz平面上
            2) 將yoz平面上的紅色軸變到z軸上

            至于這兩個(gè)變換的方法...我實(shí)在沒有別的辦法了,只能夠旋轉(zhuǎn)了,你覺得呢?先把它旋轉(zhuǎn)到y(tǒng)oz平面上。

            我們?cè)O(shè)軸向量旋轉(zhuǎn)到y(tǒng)oz面的變換為(繞z軸旋轉(zhuǎn)):

            [cosA  sinA   0   0]
            [-sinA cosA   0   0]
            [0       0      1   0]
            [0       0      0   1] 

            接著我們要求出cosA和sinA,由上圖,沿著z軸方向看去,我們看到旋轉(zhuǎn)軸向量到y(tǒng)oz面在xoy面就是將軸的投影向量旋轉(zhuǎn)角度A到y(tǒng)軸上,現(xiàn)在我不知道角度A,但是我們可以利用它直接求出cosA和sinA,因?yàn)槲覀冎狸P(guān)系:

            cosA = y0 / 軸向量在xoy面的投影長(zhǎng)
            sinA = x0 / 軸向量在xoy面的投影長(zhǎng)

            我們?cè)O(shè)軸向量的投影長(zhǎng)為lr = sqrt(x0^2 + y0^2),呵呵,現(xiàn)在,我們第一步的變換矩陣就出來了:

            [y0/lr  x0/lr 0 0]
            [-x0/lr y0/lr 0 0]
            [0      0     1  0]
            [0      0     0  1]

            同時(shí)我們得到逆變換矩陣:

            [y0/lr -x0/lr 0 0]
            [x0/lr y0/lr  0 0]
            [0      0     1 0]
            [0      0     0 1]

            然后我們進(jìn)行第二步:將yoz平面上的紅色軸變到z軸上。我們的變換矩陣是(繞x軸旋轉(zhuǎn)):

            [1 0     0    0]
            [0 cosB  sinB 0]
            [0 -sinB cosB 0]
            [0 0     0    1]

            由圖,這是經(jīng)第一次旋轉(zhuǎn)后的軸向量在yoz面中的情形,此次我們要求出上面變換中的cosB和sinB,我們?nèi)圆恢澜嵌菳,但我們還是可以利用它求cosB和sinB。由于第一次旋轉(zhuǎn)是圍繞z軸,所以軸向量的z分量沒有變,還是z0。此外,軸向量現(xiàn)在的y分量和原來不同了,我們?cè)倏匆幌碌谝淮巫儞Q那張圖,可以發(fā)現(xiàn)軸向量在旋轉(zhuǎn)到y(tǒng)oz面后,y分量變成了剛才軸向量在xoy面上的投影長(zhǎng)lr了。Yes!我想是時(shí)候?qū)懗鯿osB和sinB了:

            cosB = z0 / 軸向量的長(zhǎng)
            sinB = lr / 軸向量的長(zhǎng)

            還記得我們剛才假設(shè)軸向量是一個(gè)單位向量嗎?所以

            cosB = z0
            sinB = lr

            至此我們的第二個(gè)變換就出來了:

            [1 0   0   0]
            [0 z0  lr  0]
            [0 -lr z0  0]
            [0 0   0   1]

            相應(yīng)逆變換矩陣:

            [1 0   0   0]
            [0 z0  -lr 0]
            [0 lr  z0  0]
            [0 0   0   1]

            現(xiàn)在總結(jié)一下,我們對(duì)于空間任意點(diǎn)圍繞某個(gè)任意方向且過原點(diǎn)的軸旋轉(zhuǎn)的變換矩陣就是:

                  [y0/lr  x0/lr 0 0]   [1 0   0  0]   [cosA  sinA 0 0]   [1 0  0   0]   [y0/lr  -x0/lr 0 0]
                  [-x0/lr y0/lr 0 0]   [0 z0  lr 0]   [-sinA cosA 0 0]   [0 z0 -lr 0]   [x0/lr  y0/lr  0 0]
            M = [0      0     1 0] x [0 -lr z0 0] x [0     0    1 0] x [0 lr z0  0] x [0      0      1 0]
                  [0      0     0 1]   [0 0   0  1]   [0     0    0 1]   [0 0  0   1]   [0      0      0 1]

            上面的變換是“旋轉(zhuǎn)變換-旋轉(zhuǎn)變換-旋轉(zhuǎn)變換-旋轉(zhuǎn)變換-旋轉(zhuǎn)變換”的變換組。當(dāng)我們需要讓空間中的某個(gè)位置向量圍繞一個(gè)軸旋轉(zhuǎn)角度A的時(shí)候,就可以用這個(gè)向量相應(yīng)的矩陣乘上這個(gè)M,比如

            [x y 0 0]         [x' y' z' -]
            [0 1 0 0]         [-  -  -  -]
            [0 0 1 0] x M = [-  -  -  -] 
            [0 0 0 1]         [-  -  -  -]

            當(dāng)然,M中矩陣相應(yīng)的元素是根據(jù)軸向量得到的。

            以上的變換矩陣是通過把軸向量變到z軸上得到的,而且是先旋轉(zhuǎn)到y(tǒng)oz面上,然后再旋轉(zhuǎn)到z軸上。我們也可以不這樣做,而是先把軸向量旋轉(zhuǎn)到xoz面上,然后再旋轉(zhuǎn)到z軸上。此外,我們還可以把軸向量變到x或y軸上,這一點(diǎn)我們可以自己決定。雖然變換不同,但推導(dǎo)的道理是相同的,都是這種“其他變換-實(shí)際旋轉(zhuǎn)變換-其他變換”的滲透形式。

            剛才分析的是旋轉(zhuǎn)軸過原點(diǎn)的情況,對(duì)于一般的旋轉(zhuǎn)軸,雖然我們也都是把它的軸向量放到原點(diǎn)來考慮,但我們不能只是讓旋轉(zhuǎn)點(diǎn)圍繞過原點(diǎn)的軸向量旋轉(zhuǎn)完就算完事,我們?nèi)孕枰捎?#8220;平移變換-旋轉(zhuǎn)變換-平移變換”方法。即先將旋轉(zhuǎn)軸平移到過原點(diǎn)方向,旋轉(zhuǎn)點(diǎn)也做相應(yīng)平移,接著按上面推出的變換陣旋轉(zhuǎn),最后將旋轉(zhuǎn)軸和點(diǎn)逆平移回去。這里,我們只需在M的左右兩邊各加上一個(gè)平移變換即可。這個(gè)平移變換的元素是根據(jù)軸向量與原點(diǎn)之間的距離向量得到的,比如旋轉(zhuǎn)軸與原點(diǎn)的距離向量是(lx, ly, lz),則我們的變換就變成
                  [1   0    0  0]       [1  0  0  0]
                  [0   1    0  0]       [0  1  0  0]
            m = [0   0    1  0] x M x [0  0  1  0]
                  [-lx -ly -lz 1]       [lx ly lz 1]

            變換矩陣m就是全部7個(gè)變換矩陣的歸并,適用于各種旋轉(zhuǎn)情況。

            我們現(xiàn)在已經(jīng)討論完了一般的2-D、3-D旋轉(zhuǎn)了。可以看出其基本的思想還是能夠化繁為簡(jiǎn)的變換、歸并。而實(shí)際的旋轉(zhuǎn)也仍是用我們最最基本的2-D繞原點(diǎn)旋轉(zhuǎn)公式。其實(shí)還有很多的旋轉(zhuǎn)效果可以用我們上面的變換、公式稍加修改獲得。比如螺旋形旋轉(zhuǎn)、旋轉(zhuǎn)加前進(jìn)、隨機(jī)旋轉(zhuǎn)等等。下一篇將介紹一個(gè)用的最多的高檔一些的方法,下次見。

             

            <6>3-D空間中的基變換與坐標(biāo)變換

            一、空間坐標(biāo)系的基和基矩陣
            在3-D空間中,我們用空間坐標(biāo)系來規(guī)范物體的位置,空間坐標(biāo)系由3個(gè)相互垂直的坐標(biāo)軸組成,我們就把它們作為我們觀察3-D空間的基礎(chǔ),空間中物體的位置可以通過它們來衡量。當(dāng)我們把這3個(gè)坐標(biāo)軸上單位長(zhǎng)度的向量記為3個(gè)相互正交的單位向量i,j,k,空間中每一個(gè)點(diǎn)的位置都可以被這3個(gè)向量線性表出,如P<1,-2,3>這個(gè)點(diǎn)可以表為i-2j+3k。

            我們把這3個(gè)正交的單位向量稱為空間坐標(biāo)系的,它們單位長(zhǎng)度為1且正交,所以可以成為標(biāo)準(zhǔn)正交基。三個(gè)向量叫做基向量。現(xiàn)在我們用矩陣形式寫出基向量和基。

            i =  | 1 0 0 | 
            j =  | 0 1 0 | 
            k =  | 0 0 1 |
                
                  | i |    | 1 0 0 |    
            B = | j | =  | 0 1 0 |
                  | k |    | 0 0 1 |

            這樣的矩陣我們叫它基矩陣。有了基矩陣,我們就可以把空間坐標(biāo)系中的一個(gè)向量寫成坐標(biāo)乘上基矩陣的形式,比如上面的向量P可以寫成:

            P = C x B => | 1 -2 3 | = | 1 -2 3 | x | 1 0 0 |                          

            | 0 1 0 |
                    
            | 0 0 1 |


             

            這樣的話,空間坐標(biāo)系下的同一個(gè)向量在不同的基下的坐標(biāo)是不同的。


            二、局部坐標(biāo)系和局部坐標(biāo)

            和空間坐標(biāo)系(也可以叫做全局坐標(biāo)系或者世界坐標(biāo)系)并存的稱為局部坐標(biāo)系(也叫坐標(biāo)架——coordinate frame),它有自己的基,這些基向量把空間坐標(biāo)系作為參考系。比如
               
                  | x'|   | -1  0   0  |
            B' = | y'| = | 0   1   0  |
                  | z'|   | 0   0   -1 |

                   | x''|   | 2^½ /2    0   2^½ /2    |

            B'' = | y''| = | 0        -1   0          |

                   | z''|   | -(2^½) /2   0   2^½ /2  |

            就是兩個(gè)局部坐標(biāo)系的基,如圖:

             

            現(xiàn)在我們可以把上面那個(gè)空間坐標(biāo)中的向量P|1 -2 3|(以后都用矩陣表示)表示在不同的基下,我把它寫成一個(gè)大長(zhǎng)串的式子:



                                  | x' |                        | x''|
             
            P = | Px' Py' Pz' | x | y' | = | Px'' Py'' Pz'' | x | y''|

                                  | z' |                        | z''|

             

            這里| Px' Py' Pz'|是P在B'下的坐標(biāo),| Px'' Py'' Pz''|是P在B''下的坐標(biāo),我把它寫的具體點(diǎn)吧:

             

            | 1 -2 3 | = | -1 -2 -3 | x | -1 0  0 |

            | 0  1  0 |

            | 0  0 -1 |

            = | 2*2^½   -2   2^½ | x | 2^½ /2       0     2^½ /2|

            |     0           -1       0      |

            | -(2^½) /2    0    2^½ /2|

               

             

             

            這就是說,在空間坐標(biāo)系下面的向量| 1 -2 3 |在基B'下的坐標(biāo)為|-1 -2 -3|,在B''下的坐標(biāo)為| 2*2^½   -2   2^½ |。當(dāng)然空間坐標(biāo)系也有自己的基B|i j k|^T(因?yàn)槭橇邢蛄浚詫懗尚邢蛄康霓D(zhuǎn)置),但我們現(xiàn)在是拿它當(dāng)作一個(gè)參考系。

            在研究了局部坐標(biāo)系之后,我現(xiàn)在要分析兩個(gè)應(yīng)用它們的例子,先來看



            三、空間坐標(biāo)系中一個(gè)點(diǎn)圍繞任一軸的旋轉(zhuǎn)

            上一篇討論3-D空間旋轉(zhuǎn)的時(shí)候說到有一個(gè)高檔的方法做3-D空間任意軸旋轉(zhuǎn),現(xiàn)在我們的知識(shí)儲(chǔ)備已經(jīng)足夠理解這個(gè)方法了(Quake引擎使用的就是這個(gè)方法)。


            如上所示,空間坐標(biāo)系中的一個(gè)局部坐標(biāo)系xyz中有一個(gè)向量a(2,5,3)和一個(gè)點(diǎn)p(8,4,2)現(xiàn)在我要讓p點(diǎn)圍繞a向量旋轉(zhuǎn)60度,得到p’點(diǎn),該如何做呢?從目前掌握的旋轉(zhuǎn)知識(shí)來看,我們有兩個(gè)理論基礎(chǔ):

            1)在一個(gè)坐標(biāo)系中的一個(gè)點(diǎn),如果要它圍繞該坐標(biāo)系中一個(gè)坐標(biāo)軸旋轉(zhuǎn),就給它的坐標(biāo)值乘相應(yīng)的旋轉(zhuǎn)矩陣,如

            [cosA -sinA 0 ]
            [sinA cosA  0 ]
            [0    0     1 ]

            等等。

            2)我們已經(jīng)學(xué)習(xí)了局部坐標(biāo)系的理論了,知道空間中一個(gè)點(diǎn)在不同的坐標(biāo)系中的坐標(biāo)不同。利用這一點(diǎn),我們可以很方便的讓一個(gè)點(diǎn)或者向量在不同的坐標(biāo)系之間轉(zhuǎn)換。

            我們聯(lián)系這兩個(gè)理論根據(jù),得出我們的思路:

            1構(gòu)造另一個(gè)局部坐標(biāo)系abc,使得a成為該坐標(biāo)系的一個(gè)坐標(biāo)軸。

            2 把p的坐標(biāo)變換到abc中,得到p’,用旋轉(zhuǎn)公式讓p’圍繞已經(jīng)成為坐標(biāo)軸的a旋轉(zhuǎn),得到p’’。

            3把p’’再變換回坐標(biāo)系xyz,得到p’’’,則p’’’就是p圍繞a旋轉(zhuǎn)后的點(diǎn)。

            下面我們逐步說明。

            首先我們構(gòu)造abc,我們有無數(shù)種方法構(gòu)造,因?yàn)橹灰WCb、c之間以及他們和a之間都正交就可以了,但我們只要一個(gè)。根據(jù)上圖,我們首先產(chǎn)生一個(gè)和a正交的b。這可以通過向量的叉乘來完成:我們?nèi)×硪粋€(gè)向量v(顯然,這個(gè)向量是不能和a共線的任何非零向量),讓它和a決定一個(gè)平面x,然后讓v叉乘a得到一個(gè)垂直于x的向量b,因?yàn)閎垂直于x,而a在平面x上,因此b一定垂直于a,然后用a叉乘b得到c,最后單位化a、b、c,這樣就得到了局部坐標(biāo)系abc。

            然后我們把p點(diǎn)變換到abc坐標(biāo)系中,得到p’,即p’就是p在abc中的坐標(biāo):

            |a  b  c| * p’=  |x  y  z| * p

            p’ = |a  b  c|^-1 * |x  y  z| * p

                  |ax bx cx|        |1 0 0|   |px|

            p’ = |ay by cy| ^-1 *  |0 1 0| * |py|

                  |az bz cz|        |0 0 1|   |pz|

            注意這里|a b c|^-1即矩陣|a b c|的逆矩陣,因?yàn)閍、b、c是三個(gè)正交向量,并且是單位向量,因此|a b c|是一個(gè)正交矩陣,正交矩陣的轉(zhuǎn)置和逆相等,這是它的一個(gè)特性,因此上面的公式就可以寫成:

                  |ax ay az|     |1 0 0|   |px|

            p’ = |bx by bz|  *  |0 1 0| * |py|

                  |cx cy cz|     |0 0 1|   |pz|

            這個(gè)時(shí)候p’就是p在abc坐標(biāo)系下的坐標(biāo)了。此時(shí)a已經(jīng)是一個(gè)坐標(biāo)軸了,我們可以用旋轉(zhuǎn)矩陣來做。

            p’’ = RotMatrix * p’

            [1 0          0]   |p’x|
            p’’ = [0 cos60 -sin60] * |p’y|
                    [0 sin60  cos60]   |p’z|

             

            最后,我們把p’’再次變換回xyz坐標(biāo)系,得到最終的p’’’

            |a  b  c| * p’’ = |x  y  z| * p’’’

            p’’’ = |x  y  z|^-1 * |a  b  c| * p’’

            p’’’ = |a  b  c| * p’’

             

            最后

            p’’’ = |a  b  c| * RotMatrix * |a   b   c|^T * p = M * p

            這樣就得到了xyz坐標(biāo)系中點(diǎn)p圍繞a旋轉(zhuǎn)60度后的點(diǎn)。

            最后,我用Quake3引擎的相應(yīng)函數(shù)(來自idSoftware ——quake3-1[1].32b-source——mathlib.c)來完成對(duì)這個(gè)算法的說明:

            /*

            ===============

            RotatePointAroundVector

            dst是一個(gè)float[3],也就是p’’’

            dir相當(dāng)于a,point就是p,degrees是旋轉(zhuǎn)度數(shù)

            ===============

            */

            void RotatePointAroundVector( vec3_t dst, const vec3_t dir, const vec3_t point,

                                      float degrees ) {

                float  m[3][3];

                float  im[3][3];

                float  zrot[3][3];

                float  tmpmat[3][3];

                float  rot[3][3];

                int i;

                vec3_t vr, vup, vf;

                float  rad;

                vf[0] = dir[0];

                vf[1] = dir[1];

                vf[2] = dir[2];

               // 首先通過dir得到一個(gè)和它垂直的vr

               // PerpendicularVector()函數(shù)用于構(gòu)造和dir垂直的向量

               // 也就是我們上面的第1步

                PerpendicularVector( vr, dir );

               // 通過cross multiply得到vup

               // 現(xiàn)在已經(jīng)構(gòu)造出坐標(biāo)軸向量vr, vup, vf

                CrossProduct( vr, vf, vup );

               // 把這三個(gè)單位向量放入矩陣中

                m[0][0] = vr[0];

                m[1][0] = vr[1];

                m[2][0] = vr[2];

                m[0][1] = vup[0];

                m[1][1] = vup[1];

                m[2][1] = vup[2];

                m[0][2] = vf[0];

                m[1][2] = vf[1];

                m[2][2] = vf[2];

               // 產(chǎn)生轉(zhuǎn)置矩陣im

                memcpy( im, m, sizeof( im ) );

              

                im[0][1] = m[1][0];

                im[0][2] = m[2][0];

                im[1][0] = m[0][1];

                im[1][2] = m[2][1];

                im[2][0] = m[0][2];

                im[2][1] = m[1][2];

               // 構(gòu)造旋轉(zhuǎn)矩陣zrot

                memset( zrot, 0, sizeof( zrot ) );

                zrot[0][0] = zrot[1][1] = zrot[2][2] = 1.0F;

              

                rad = DEG2RAD( degrees );

                zrot[0][0] = cos( rad );

                zrot[0][1] = sin( rad );

                zrot[1][0] = -sin( rad );

                zrot[1][1] = cos( rad );

               // 開始構(gòu)造變換矩陣M

               // tmpmat = m * zrot

                MatrixMultiply( m, zrot, tmpmat );

               // rot = m * zrot * im

                MatrixMultiply( tmpmat, im, rot );

               // 則 rot = m * zrot * im 和我們上面推出的

               // M = |a  b  c| * RotMatrix * |a   b   c|^T  一致

               // 變換point這個(gè)點(diǎn)

               // p’’’ = M * p

                for ( i = 0; i < 3; i++ ) {

                   dst[i] = rot[i][0] * point[0] + rot[i][1] * point[1] + rot[i][2] * point[2];

                }

            }

            四、世界空間到相機(jī)空間的變換

             

             

            空間坐標(biāo)系XYZ,相機(jī)坐標(biāo)系UVN。這時(shí)候相機(jī)空間的基(以下簡(jiǎn)稱相機(jī))在空間坐標(biāo)系中圍繞各個(gè)坐標(biāo)軸旋轉(zhuǎn)了一定角度<a,b,c>,然后移動(dòng)了<x,y,z>。對(duì)于模型我們可以看作相對(duì)于相機(jī)的逆運(yùn)動(dòng),即模型旋轉(zhuǎn)了一定角度<-a,-b,-c>,然后移動(dòng)了<-x,-y,-z>,可以把相機(jī)和物體的運(yùn)動(dòng)看成兩個(gè)互逆的變換。這樣,可以通過對(duì)相機(jī)的變換矩陣求逆來得到模型的變換矩陣。下面來具體看一下,如何得到相機(jī)變換矩陣,并且求得它的逆矩陣。

            首先聲明一下,對(duì)于一個(gè)模型的變換,我們可以給模型矩陣左乘變換矩陣:

            M x P = P'

            | A B C D |

            | E F G H |

            | I J K L |

            | M N O P |

            x

            | x |

            | y |

            | z |

            | 1 |

            =

            | Ax + By + Cz + D |

            | Ex + Fy + Gz + H |         
            | Ix + Jy + Kz + L |

            | Mx + Ny + Oz + P |

            也可以右乘變換矩陣:

            PT x MT = P'T

            | x y z 1|   x

            | A E I  M |

            | B F J  N |   
            | C G K O |

            | D H L  P |

            =  |Ax+By+Cz+D Ex+Fy+Gz+H Ix+Jy+Kz+L Mx+Ny+Oz+P|    

                           

            可以看出兩種變換方式是一個(gè)轉(zhuǎn)置關(guān)系,結(jié)果只是形式上的不同,但這里我們使用后者,即右乘變換矩陣,因?yàn)楸容^普遍。

            很顯然,相機(jī)的變換可以分成兩個(gè)階段:旋轉(zhuǎn)和平移。我們先來看旋轉(zhuǎn)。

            在空間坐標(biāo)系中,相機(jī)旋轉(zhuǎn)之前世界坐標(biāo)系xyz和相機(jī)坐標(biāo)系u0v0n0的各個(gè)軸向量的方向相同,有關(guān)系:
             

            P = |Pu0 Pv0 Pn0| x | u0 |

            | v0 |

            | n0 |

            =  |Px Py Pz| x   | x |

            | y |

            | z |


                                           

            這里P是空間坐標(biāo)系中的一個(gè)向量。|u0 v0 n0|^T是相機(jī)基矩陣,|Pu0 Pv0 Pn0|是P在相機(jī)基矩陣下的坐標(biāo)。|x y z|^T是
            世界基矩陣,|Px Py Pz|是P在它下面的坐標(biāo)。有Pu0 = Px, Pv0 =Py, Pn0 = Pz。

            相機(jī)和向量P都旋轉(zhuǎn)之后,有關(guān)系:
             

            P' = |Pu0 Pv0 Pn0| x | u |

            | v |

            | n |

              = |Px' Py' Pz'| x | x |

            | y |

            | z |


                                
                                                   

            P'是P同相機(jī)一起旋轉(zhuǎn)后的向量。|u v n|^T是相機(jī)旋轉(zhuǎn)后的基矩陣,|Pu0 Pv0 Pn0|是P'在它下面的坐標(biāo),因?yàn)镻是和相機(jī)一起旋轉(zhuǎn)的,所以坐標(biāo)不變。|x y z|^T仍為世界基矩陣,|Px' Py' Pz'|是P'在它下面的坐標(biāo)。

            現(xiàn)在看

                                              

            因?yàn)?span lang=EN-US>|x y z|^T為一個(gè)單位陣,且Pu0 = Px, Pv0 =Py, Pn0 = Pz。 所以得到

            |Pu0 Pv0 Pn0| x | u |

            | v |

            | n |

            = |Px' Py' Pz'| x | x |

            | y |

            | z |

             

             

            |Px Py Pz| x | u |

            | v |

            | n |

            = |Px' Py' Pz'|   

             

             

             

             

            |Px Py Pz|和相機(jī)一起旋轉(zhuǎn)后變成|Px' Py' Pz'|,即P x R = P',而旋轉(zhuǎn)變換矩陣R就是:

            | u |

            | v |

            | n |

            寫成標(biāo)準(zhǔn)4x4矩陣:

            | ux uy uz 0|

            | vx vy vz 0|

            | nx ny nz 0|

            | 0  0  0  1|

            平移矩陣T很簡(jiǎn)單:

            | 1 0 0 0 |

            | 0 1 0 0 |

            | 0 0 1 0 |

            | x y z 1 |

            則相機(jī)矩陣就是:

                         | ux uy uz 0 |     | 1 0 0 0 |
                                           
                         | vx vy vz 0 |     | 0 1 0 0 |
            C = R x T =                  x             
                         | nx ny nz 0 |     | 0 0 1 0 |
              
                         | 0  0  0  1 |     | x y z 1 |

             

            它的逆矩陣,即相機(jī)的逆變換矩陣為

             

            C-1 = T-1 x R-1 = | 1  0  0  0 |

            | 0  1  0  0 |

            | 0  0  1  0 |

            | -x -y -z 1 |

            x   | ux vx nx 0 |

            | uy vy ny 0 |

            | uz nz nz 0 |

            | 0   0  0  1 |

            = | ux   vx   nx  0 |

            | uy   vy   ny  0 |

            | uz   vz   nz  0 |

            |-T.u -T.v -T.n 1 |

            posted on 2008-01-15 17:00 楊粼波 閱讀(1358) 評(píng)論(0)  編輯 收藏 引用


            只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


            精品久久777| 国产成人精品免费久久久久| 成人资源影音先锋久久资源网| 69久久精品无码一区二区| 久久精品男人影院| 亚洲精品美女久久久久99小说| 色8久久人人97超碰香蕉987| 91久久国产视频| 久久亚洲精品成人AV| 久久综合久久综合九色| 亚洲中文久久精品无码| 色综合久久中文色婷婷| 久久久久亚洲精品天堂久久久久久| 久久亚洲春色中文字幕久久久| 91久久精品国产免费直播| 伊人热热久久原色播放www| 99麻豆久久久国产精品免费| 无码人妻少妇久久中文字幕 | 亚洲人成电影网站久久| 99精品久久精品| 久久99热这里只有精品66| 无码乱码观看精品久久| 久久91综合国产91久久精品| 无码精品久久久久久人妻中字| 午夜精品久久久久久毛片| 久久久久亚洲精品无码网址 | 久久久久久国产精品无码下载| 欧美一级久久久久久久大| 狠狠色丁香婷综合久久| 99久久夜色精品国产网站| 久久亚洲精品国产精品婷婷| 久久午夜综合久久| 国内精品伊人久久久久影院对白 | 国产成人久久精品一区二区三区| 久久午夜无码鲁丝片午夜精品| Xx性欧美肥妇精品久久久久久| 久久久久成人精品无码 | 久久国产精品一区二区| 97精品伊人久久大香线蕉app| av无码久久久久不卡免费网站| 亚洲va中文字幕无码久久|