1. 程式人生 > >3D 圖形程式設計的數學基礎 1 向量及其運算

3D 圖形程式設計的數學基礎 1 向量及其運算

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow

也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!

               

write by 九天雁翎(JTianLing) -- blog.csdn.net/vagrxie

 

討論新聞組及檔案

  Technorati 標籤:
向量
, 3D, 座標系, 規範化, 點積, 叉積  

 

 

說明

 

因為大學時在高等數學課程中學習過線性代數相關的內容,所以學習3D程式設計的時候這一段事實上是跳過去了,學習到某些內容的時候覺得很鬱悶,(4,5年沒有用了,難免忘掉)最後常常依靠高階API完成,但是事實上這些高階API的演算法具體實現啥的基本看不懂,於是還是決定回來好好的將基礎部分弄明白,當然,首先是數學部分。為了更好的達到直觀的效果,還有在複雜矩陣運算的時候驗證運算結果,將引入freemat或者scilab(5.1.1)或者GNU Octave(3.2.3)的使用,將此三個軟體作為matlab的替代品來使用。不能用龐大的matlab也是種解脫,預設使用freemat,不行的時候考慮其他替代。具體牽涉到計算的時儘量實現DirectX與Irrlicht兩個版本,也會參考部分原始碼。(主要用於看看公式用C/C++的實現)基本上,我希望能以概念的講解為主,最好是直觀的講解。

 

 

 

向量

 

只用大小就能表示的量叫數量,比如溫度,質量等。既需要用大小表示,同時還要指明方向的量叫向量,比如位移,速度等。幾何學中,我們用有向線段來表示向量。有兩個變數可以確定一個向量,即向量的長度和向量的方向。量與位置無關,有相同長度和方向的兩個向量是相等的。在irrlicht中有專門的類vector2d,vector3d分別來表示2維的,3維的向量。在DirectX中用於表示向量的是結構D3DXVECTOR2,D3DXVECTOR3,D3DXVECTOR4。

 

 

 

左右手座標系

 

一圖勝前言,不懂怎麼用手扭曲的去比劃的看看圖,就明白啥是左手,啥是右手座標系了。在OpenGL中使用的是右手座標系,DirectX,Irrlicht中使用的是左手座標系。(圖片來自於網路)

 

image

 

向量的模

 

向量的大小(或長度)稱為向量的模,向量a的模記為||a||。下面以3維的向量(3D中用的最多)為例:abc2_html_8d5f713

 

在irrlicht中獲取向量模的函式是vector3d的成員函式

 
//! Get length of the vector.T getLength() const { return core::squareroot( X*X + Y*Y + Z*Z ); }//! Get squared length of the vector./** This is useful because it is much faster than getLength()./return Squared length of the vector. */T getLengthSQ() const { return X*X + Y*Y + Z*Z; }

可以看出公式的實現,其中getLengthSQ用於某些時候使用不開根號,直接使用平方值的方法來優化程式碼。

DirectX中的實現差不多一樣,只是使用的是C風格的介面沒有使用C++的類而已。

D3DXINLINE FLOAT D3DXVec3Length    ( CONST D3DXVECTOR3 *pV ){#ifdef D3DX_DEBUG    if(!pV)        return 0.0f;#endif#ifdef __cplusplus    return sqrtf(pV->x * pV->x + pV->y * pV->y + pV->z * pV->z);#else    return (FLOAT) sqrt(pV->x * pV->x + pV->y * pV->y + pV->z * pV->z);#endif}D3DXINLINE FLOAT D3DXVec3LengthSq    ( CONST D3DXVECTOR3 *pV ){#ifdef D3DX_DEBUG    if(!pV)        return 0.0f;#endif    return pV->x * pV->x + pV->y * pV->y + pV->z * pV->z;}

FreeMat:

   
       
--> a = [1, 1, 1]a = 1 1 1--> b = norm(a)b =    1.7321--> 
     

 

三維空間中兩點的距離

公式:abc4_html_m257962b9

Irrlicht的實現:

//! Get distance from another point./** Here, the vector is interpreted as point in 3 dimensional space. */T getDistanceFrom(const vector3d<T>& other) const{    return vector3d<T>(X - other.X, Y - other.Y, Z - other.Z).getLength();}//! Returns squared distance from another point./** Here, the vector is interpreted as point in 3 dimensional space. */T getDistanceFromSQ(const vector3d<T>& other) const{    return vector3d<T>(X - other.X, Y - other.Y, Z - other.Z).getLengthSQ();}

也有平方的SQ函式版本。

 

向量的規範化

向量的規範化也稱(歸一化)就是使向量的模變為1,即變為單位向量。可以通過將向量都除以該向量的模來實現向量的規範化。規範化後的向量相當於與向量同方向的單位向量,可以用它表示向量的方向。由於方向的概念在3D程式設計中非常重要,所以此概念也很重要,單位向量有很多重要的性質,在表示物體表面的法線向量時用的更是頻繁。

基本的公式: clip_image002

在irrlicht中的呼叫函式及實現:

//! Normalizes the vector./** In case of the 0 vector the result is still 0, otherwisethe length of the vector will be 1./return Reference to this vector after normalization. */vector3d<T>& normalize(){    f64 length = (f32)(X*X + Y*Y + Z*Z);    if (core::equals(length, 0.0)) // this check isn't an optimization but prevents getting NAN in the sqrt.        return *this;    length = core::reciprocal_squareroot ( (f64) (X*X + Y*Y + Z*Z) );    X = (T)(X * length);    Y = (T)(Y * length);    Z = (T)(Z * length);    return *this;}

上述程式碼中首先計算length以防其為0,然後直接計算1/||u||,(這樣做的目的從程式碼實現上來看是因為SSE,Nviadia都有可以直接計算此值的能力) 然後再分別與各座標值進行乘法運算。

DirectX中的呼叫函式:(無實現可看)

D3DXVECTOR3* WINAPI D3DXVec3Normalize    ( D3DXVECTOR3 *pOut, CONST D3DXVECTOR3 *pV );
 

向量的加減法,數乘

太簡單,不多描述,無非就是對應的加,減,乘罷了,幾何意義講一下,加法可以看做是兩個向量綜合後的方向,減法可以看做兩個向量的差異方向(甚至可以用於追蹤演算法),數乘用於對向量進行縮放。

為了完整,這裡從百度百科拷貝一段資料過來:(以下都是2維的,放到3維也差不多)

a=(x,y),b=(x',y')。

1、向量的加法


向量的加法滿足平行四邊形法則和三角形法則。

AB+BC=AC

a+b=(x+x',y+y')。

a+0=0+a=a。

向量加法的運算律:

交換律:a+b=b+a;

結合律:(a+b)+c=a+(b+c)。

2、向量的減法


如果 ab是互為相反的向量,那麼 a=- bb=- aa+ b= 0. 0的反向量為 0

AB-AC=CB. 即“共同起點,指向被減”

a=(x,y) b=(x',y') 則 a-b=(x-x',y-y').

3、數乘向量


實數λ和向量 a的乘積是一個向量,記作λ a,且∣λa∣=∣λ∣ ·∣a∣。

當λ>0時,λaa同方向;

當λ<0時,λaa反方向;

當λ=0時,λa=0,方向任意。

a=0時,對於任意實數λ,都有λa=0

注:按定義知,如果λa=0,那麼λ=0或a=0

實數λ叫做向量a的係數,乘數向量λa的幾何意義就是將表示向量a的有向線段伸長或壓縮。

當∣λ∣>1時,表示向量a的有向線段在原方向(λ>0)或反方向(λ<0)上伸長為原來的∣λ∣倍;

當∣λ∣<1時,表示向量a的有向線段在原方向(λ>0)或反方向(λ<0)上縮短為原來的∣λ∣倍。

數與向量的乘法滿足下面的運算律

結合律:(λa)·b=λ(a·b)=(a·λb)。

向量對於數的分配律(第一分配律):(λ+μ)aaa.

數對於向量的分配律(第二分配律):λ(a+b)=λab.

數乘向量的消去律:① 如果實數λ≠0且λa=λb,那麼a=b。② 如果a0且λa=μa,那麼λ=μ。

 

點積(dot product)又稱數量積或內積

v0 . v1 = v0.x*v1.x+v0.y*v1.y+v0.z*v1.z;  
所以向量的點積結果是一個數,而非向量。  
點積等於向量v0的長度乘以v1的長度,再乘以它們之間夾角的餘弦,即|v0|*|v1|*cos(θ).  
通過點積,可以計算兩個向量之間的夾角。  
cos(θ)=v0.v1/|v0||v1|;  
θ=Math.acos(v0.v1/|v0||v1|);  
如果兩個向量都是單位向量,上面的公式可以簡化為  
θ=Math.acos(v0.v1);  
V0.v1=0 =》兩個向量互相垂直  
V0.v1>0 =》兩個向量的夾角小於90度  
V0.v1<0 =》兩個向量的夾角大於90度

Irrlicht中的實現:(很簡單的公式,很直白的實現)

//! Get the dot product with another vector.T dotProduct(const vector3d<T>& other) const{    return X*other.X + Y*other.Y + Z*other.Z;}

DirectX中的實現:(很簡單的公式,也是很直白的實現)

D3DXINLINE FLOAT D3DXVec3Dot    ( CONST D3DXVECTOR3 *pV1, CONST D3DXVECTOR3 *pV2 ){#ifdef D3DX_DEBUG    if(!pV1 || !pV2)        return 0.0f;#endif    return pV1->x * pV2->x + pV1->y * pV2->y + pV1->z * pV2->z;}
 

叉積(cross product):也稱向量積

叉積的結果是一個向量,該向量垂直於相乘的兩個向量。

公式:yyyy_html_1b72d65e

注意:叉積不滿足交換律,反過來相乘得到的向量與原向量方向相反。  
左手座標系可以通過左手法則來確定叉積返回的向量的方向,從第一個向量向第二個向量彎曲左手,這是拇指所指的方向就是求得的向量的方向。右手座標系同樣的,可以通過右手法則來確定叉積返回的向量的方向,從第一個向量向第二個向量彎曲右手,這是拇指所指的方向就是求得的向量的方向。因此,事實上叉積獲得的向量總是垂直於原來兩個向量所在的平面。  
如果兩個向量方向相同或相反,叉積結果將是一個零向量。(即a//b)  
叉乘的一個重要應用就是求三角形的法向量。

Irrlicht的實現:

//! Calculates the cross product with another vector./** /param p Vector to multiply with./return Crossproduct of this vector with p. */vector3d<T> crossProduct(const vector3d<T>& p) const{    return vector3d<T>(Y * p.Z - Z * p.Y, Z * p.X - X * p.Z, X * p.Y - Y * p.X);}

 

DirectX的實現:

D3DXINLINE D3DXVECTOR3* D3DXVec3Cross    ( D3DXVECTOR3 *pOut, CONST D3DXVECTOR3 *pV1, CONST D3DXVECTOR3 *pV2 ){    D3DXVECTOR3 v;#ifdef D3DX_DEBUG    if(!pOut || !pV1 || !pV2)        return NULL;#endif    v.x = pV1->y * pV2->z - pV1->z * pV2->y;    v.y = pV1->z * pV2->x - pV1->x * pV2->z;    v.z = pV1->x * pV2->y - pV1->y * pV2->x;    *pOut = v;    return pOut;}
基本上也就是按公式來了。

作為最後一個概念,這裡用程式碼實踐一下。

求a=(2,2,1)和b=(4,5,3)的叉積。

freemat:

   
       
--> a = [2,2,1]a = 2 2 1--> b = [4,5,3]b = 4 5 3--> c = cross(a,b)c =  1 -2  2--> 
     

Irrlicht:

#include <stdio.h>#include <irrlicht.h>using namespace irr::core;int _tmain(int argc, _TCHAR* argv[]){    vector3df a(2.0f, 2.0f, 1.0f);    vector3df b(4.0f, 5.0f, 3.0f);    vector3df c = a.crossProduct(b);    printf("c = (%f, %f, %f)", c.X, c.Y, c.Z);    return 0;}

 

輸出:

c = (1.000000, -2.000000, 2.000000)

DirectX:

#include <stdio.h>#include <d3dx9.h>int _tmain(int argc, _TCHAR* argv[]){    D3DXVECTOR3 a(2.0f, 2.0f, 1.0f);    D3DXVECTOR3 b(4.0f, 5.0f, 3.0f);    D3DXVECTOR3 c;    D3DXVec3Cross(&c, &a, &b);    printf("c = (%f, %f, %f)", c.x, c.y, c.z);    return 0;}

輸出:

c = (1.000000, -2.000000, 2.000000)

這裡給出個較為完整的例子是希望大家瞭解一下Irrlicht這種C++風格的介面及DirectX的C風格介面使用上的不同,這裡就不對兩種風格的介面提出更多評論了,以防引起口水戰。

下一篇預計講矩陣的計算

參考資料:

1.《DirectX 9.0 3D遊戲開發程式設計基礎》 ,(美)Frank D.Luna著,段菲譯,清華大學出版社

2.《大學數學》湖南大學數學與計量經濟學院組編,高等教育出版社

3.百度百科及wikipedia

 

原創文章作者保留版權 轉載請註明原作者 並給出連結

write by 九天雁翎(JTianLing) -- blog.csdn.net/vagrxie

           

給我老師的人工智慧教程打call!http://blog.csdn.net/jiangjunshow

這裡寫圖片描述