日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

(转)从零实现3D图像引擎:(6)向量函数库

發布時間:2023/12/10 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 (转)从零实现3D图像引擎:(6)向量函数库 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1. 數學分析

1) 基本定義:

向量由多個分量組成,2D/3D向量表示一條有向線段。下面的ux,uy就是兩個分量。

向量u = <ux, uy>,如果從點P1(x1, y1)指向點P2(x2, y2),則:

U = p2 - p1 = (x2-x1, y2-y1) = <Ux, Uy>

向量被定義后,總是相對于原點的,所以可以用一個點來表示從原點指向該點的向量。

2) 向量的范數(norm)

范數就是向量長度,是從原點到終點的距離。用|u|表示,所以:

|U| = sqrt(Ux2 + Uy2)

|U| = sqrt(Ux2 + Uy2 + Uz2)

3) 單位向量與歸一化

有時候,我們只關心向量的方向而不關心其長度,所以可以對向量做歸一化,使其方向不變,而長度縮放為1,以方便計算。用n'表示。

歸一化公式:

n' = n / |n|

4) 標量與向量乘法

對于標量k,標量與向量相乘的公式為:
k * u = k * <ux, uy> = <k * ux, k * uy>

標量與向量乘法的幾何意義:縮放一個向量。也可以乘以-1來反轉向量。

5) 向量之間相加,將各分量相加即可。

u + v = <ux, uy> + <vx, vy> = <ux + vx, uy + vy>

向量相加的幾何意義:平移v的起點至u的終點,則結果為u的起點到平移后的v的終點的線段。如下圖:

6) 向量相減,分量相減

u - v = <ux, uy> - <vx, vy> = <ux - vx, uy - vy>

幾何意義:減數向量的終點指向被減數向量的終點的線段,如下圖:

7) 點積

由于兩個向量的分量直接相乘沒有什么實際的幾何意義,所以一般沒用。而點積就十分有用。定義如下:

u.v = ux*vx + uy*vy

點積運算是將兩個向量的分量分別相乘,然后再相加,所得的結果是一個標量。

點積的幾何意義體現在這個點積公式上:

u.v = |u| * |v| * cos(theta)

即:點積等于兩個向量的長度積,再乘以它們之間的夾角的余弦。于是便可以推得夾角的計算方法:

theta = arccos(u.v / (|u| * |v|))

這個公式是很多3D圖形學算法的基礎,并且如果u和v都是單位向量的話,則|u| = |v| = 1,那么:

theta = arccos(u.v)

下面有4個點積非常重要的定理:

1. 如果u與v垂直,則u.v = 0

2. 如果夾角為銳角,則u.v > 0

3. 如果夾角為鈍角,則u.v < 0

4. 如果u與v相等,則u.v = |u| = |v|

那么根據點積的這些性質,我們可以發現由點積帶來的一大用途——計算向量在給定方向上的投影向量。

先看下圖:

其實思路很簡單,既然是求u在v分量上的投影向量,那么方向已經可以知道了,所以所求投影向量的單位向量就等于v的單位向量,所以已經可以求得了該投影向量的單位向量:p(單位) = v / |v|

現在就差長度了,通過上圖,可以知道|p| = |u| * cos(theta),綜合一下,就可以求得:

p = (v / |v|) * (|u| * cos(theta))

還記得點積公式嗎? u.v = |u| * |v| * cos(theta)

所以可以簡化上面咱們的推導,得:

p = (u.v * v) / (|v| * |v|)

另外,點積滿足以下乘法定律,很好證明,這里省略:

u.v = v.u

u.(v+w) = (u.v + u.w)

k*(u.v) = (k*u).v = u.(k*v)

8) 叉積

首先給出叉積的定義:

u × v = |u| * |v| * sin(theta) * n

其中n是垂直于u和v的單位法向量。

如何求n呢?我們需要建立一個矩陣:

|? i??? j??? k? |

| ux uy uz |

| vx vy vz? |

其中i,j,k分別是與X、Y、Z軸平行的單位向量。

n是三個標量乘以X、Y、Z軸單位向量的線性組合:

n = (uy*vz - vy*uz)*i - (ux*vz - vx*uz)*j + (ux*vy - vx*uy)*k

所以n = <uy*vz - vy*uz, -ux*vz + vx*uz, ux*vy - vx*uy>

這樣求得的n不一定是單位向量,所以需要進行歸一化再使用。

其實后面求n不叉積的定義更重要,因為如果要求角度,點積就可以直接計算出來了,所以一般用叉積都是來求法線向量的。

叉積的乘法定律:

u×v = -(v×u)

u×(v+w) = u×v + u×w

(u+v)×w = u×w + v×w

k*(u×v) = (k*u)×v = u×(k*v)

9) 位移向量

先看圖:

p1是從原點到點P1的向量,Vd是從點P1到點P2的向量,v'是Vd的單位向量,p是從原點到P2的向量。

還記得向量加法么,我們引入一個參數t來表示所相加的比例,則:

p = p1 + t*v' 其中t的取值范圍是[0, |vd|]

或者

p = p1 + t*vd 其中t的取值范圍是[0, 1]

這個概念非常重要,因為在游戲中跟蹤直線、線段、曲線時,非常有用。

2. 代碼實現

void _CPPYIN_Math::VectorAdd(VECTOR2D_PTR va, VECTOR2D_PTR vb, VECTOR2D_PTR vsum) {vsum->x = va->x + vb->x;vsum->y = va->y + vb->y; }void _CPPYIN_Math::VectorAdd(VECTOR3D_PTR va, VECTOR3D_PTR vb, VECTOR3D_PTR vsum) {vsum->x = va->x + vb->x;vsum->y = va->y + vb->y;vsum->z = va->z + vb->z; }void _CPPYIN_Math::VectorAdd(VECTOR4D_PTR va, VECTOR4D_PTR vb, VECTOR4D_PTR vsum) {vsum->x = va->x + vb->x;vsum->y = va->y + vb->y;vsum->z = va->z + vb->z;vsum->w = 1; }void _CPPYIN_Math::VectorSub(VECTOR2D_PTR va, VECTOR2D_PTR vb, VECTOR2D_PTR vsum) {vsum->x = va->x - vb->x;vsum->y = va->y - vb->y; }void _CPPYIN_Math::VectorSub(VECTOR3D_PTR va, VECTOR3D_PTR vb, VECTOR3D_PTR vsum) {vsum->x = va->x - vb->x;vsum->y = va->y - vb->y;vsum->z = va->z - vb->z; }void _CPPYIN_Math::VectorSub(VECTOR4D_PTR va, VECTOR4D_PTR vb, VECTOR4D_PTR vsum) {vsum->x = va->x - vb->x;vsum->y = va->y - vb->y;vsum->z = va->z - vb->z;vsum->w = 1; }void _CPPYIN_Math::VectorScale(double k, VECTOR2D_PTR va, VECTOR2D_PTR vscaled) {vscaled->x = k * va->x;vscaled->y = k * va->y; }void _CPPYIN_Math::VectorScale(double k, VECTOR3D_PTR va, VECTOR3D_PTR vscaled) {vscaled->x = k * va->x;vscaled->y = k * va->y;vscaled->z = k * va->z; }void _CPPYIN_Math::VectorScale(double k, VECTOR4D_PTR va, VECTOR4D_PTR vscaled) {vscaled->x = k * va->x;vscaled->y = k * va->y;vscaled->z = k * va->z;vscaled->w = 1; }double _CPPYIN_Math::VectorDot(VECTOR2D_PTR va, VECTOR2D_PTR vb) {return (va->x * vb->x) + (va->y * vb->y); }double _CPPYIN_Math::VectorDot(VECTOR3D_PTR va, VECTOR3D_PTR vb) {return (va->x * vb->x) + (va->y * vb->y) + (va->z * va->z); }double _CPPYIN_Math::VectorDot(VECTOR4D_PTR va, VECTOR4D_PTR vb) {return (va->x * vb->x) + (va->y * vb->y) + (va->z * va->z); }void _CPPYIN_Math::VectorCross(VECTOR3D_PTR va, VECTOR3D_PTR vb, VECTOR3D_PTR vn) {vn->x = ((va->y * vb->z) - (va->z * vb->y));vn->y = -((va->x * vb->z) - (va->z * vb->x));vn->z = ((va->x * vb->y) - (va->y * vb->x)); }void _CPPYIN_Math::VectorCross(VECTOR4D_PTR va, VECTOR4D_PTR vb, VECTOR4D_PTR vn) {vn->x = ((va->y * vb->z) - (va->z * vb->y));vn->y = -((va->x * vb->z) - (va->z * vb->x));vn->z = ((va->x * vb->y) - (va->y * vb->x)); vn->w = 1; }double _CPPYIN_Math::VectorLength(VECTOR2D_PTR va) {return sqrt(va->x * va->x + va->y * va->y); }double _CPPYIN_Math::VectorLength(VECTOR3D_PTR va) {return sqrt(va->x * va->x + va->y * va->y + va->z * va->z); }double _CPPYIN_Math::VectorLength(VECTOR4D_PTR va) {return sqrt(va->x * va->x + va->y * va->y + va->z * va->z); }void _CPPYIN_Math::VectorNormalize(VECTOR2D_PTR va, VECTOR2D_PTR vn) {vn->x = 0;vn->y = 0;double length = VectorLength(va);if (length < EPSILON){return;}else{double lengthdao = 1 / length;vn->x = va->x * lengthdao;vn->y = va->y * lengthdao;} }void _CPPYIN_Math::VectorNormalize(VECTOR3D_PTR va, VECTOR3D_PTR vn) {vn->x = 0;vn->y = 0;vn->z = 0;double length = VectorLength(va);if (length < EPSILON){return;}else{double lengthdao = 1 / length;vn->x = va->x * lengthdao;vn->y = va->y * lengthdao;vn->z = va->z * lengthdao;} }void _CPPYIN_Math::VectorNormalize(VECTOR4D_PTR va, VECTOR4D_PTR vn) {vn->x = 0;vn->y = 0;vn->z = 0;vn->w = 0;double length = VectorLength(va);if (length < EPSILON){return;}else{double lengthdao = 1 / length;vn->x = va->x * lengthdao;vn->y = va->y * lengthdao;vn->z = va->z * lengthdao;vn->w = 1;} }double _CPPYIN_Math::VectorCos(VECTOR2D_PTR va, VECTOR2D_PTR vb) {return VectorDot(va, vb) / (VectorLength(va) * VectorLength(vb)); }double _CPPYIN_Math::VectorCos(VECTOR3D_PTR va, VECTOR3D_PTR vb) {return VectorDot(va, vb) / (VectorLength(va) * VectorLength(vb)); }double _CPPYIN_Math::VectorCos(VECTOR4D_PTR va, VECTOR4D_PTR vb) {return VectorDot(va, vb) / (VectorLength(va) * VectorLength(vb)); }

不用多說了,都是按照上面數學推導出來的公式直接實現。

3. 代碼下載

完整項目源代碼下載:>>點擊進入下載頁<<

之前的一直忘了改資源分默認是1,從這次開始我都改成0了。

4. 補充內容

1) 對于求向量范數的問題,其實這個實現方式效率不高,現在使用的勾股開方的形式實現,而其實可以使用泰勒級數來計算近似值,雖然有一點點誤差,但是運算速度大大提高。

2) 你可以發現我在做向量歸一化的時候,是先求了lengthdao = 1 / length,然后再去和三個分量做乘法,而不是讓他們分別去除以length。其實也是效率原因,計算機做除法的速度遠遠慢于做乘法,所以我們只做一次除法,而做三次乘法,這樣簡單的優化帶來的效果卻是非常明顯的。

轉自:http://blog.csdn.net/cppyin/archive/2011/02/07/6174087.aspx

轉載于:https://www.cnblogs.com/CoolJie/archive/2011/03/03/1970223.html

總結

以上是生活随笔為你收集整理的(转)从零实现3D图像引擎:(6)向量函数库的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。