四元數與3D旋轉實例! Cocos Creator 3D Quternion !

2021-01-12 白玉無冰

用幾個實用的例子帶你理解四元數!文末獲取完整項目!

前言

本文不會講太多四元數公式的推導過程,重點講講幾個接口的使用和個人理解。

閱讀本文可能需要一些前置的知識(但不限於這些知識點):

矩陣(平移/旋轉/縮放/模型矩陣/視圖矩陣/投影矩陣)

表示3D旋轉一般採用三種方法:

為什麼使用四元數表示旋轉呢?

平滑插值。(矩陣基本沒有,歐拉角可以做插值,但可能遭遇萬向鎖的問題)

當然四元數也有一些缺點:

四元數可能不合法。(一般通過四元數標準化解決這個問題,確保四元數為單位四元數)對給定的方位的表達方式有兩種方法,它們相互為負。(矩陣唯一,歐拉角有無數種)實例構造四元數

四元數的定義這邊就不詳細說了,大概知道就是用四個數字去表達旋轉。

那麼怎麼去構造這個四元數呢?我們從API入手去講解和理解。

旋轉軸和旋轉角

有了旋轉軸和旋轉角,就可以表示旋轉了,那麼四元數也可以通過這個構造出來。

/**
* @zh 根據旋轉軸和旋轉弧度計算四元數
*/
public static fromAxisAngle<Out extends IQuatLike, VecLike extends IVec3Like> (out: Out, axis: VecLike, rad: number) {
    rad = rad * 0.5; // 為什麼要除以2?因為公式推導出來的!
    const s = Math.sin(rad);
    out.x = s * axis.x;
    out.y = s * axis.y;
    out.z = s * axis.z;
    out.w = Math.cos(rad);
    return out;
}

本地坐標軸

根據該物體本地坐標軸也能確定旋轉。

/**
* @zh 根據本地坐標軸朝向計算四元數,默認三向量都已歸一化且相互垂直
*/
public static fromAxes<Out extends IQuatLike, VecLike extends IVec3Like> (out: Out, xAxis: VecLike, yAxis: VecLike, zAxis: VecLike) {
    Mat3.set(m3_1,
        xAxis.x, xAxis.y, xAxis.z,
        yAxis.x, yAxis.y, yAxis.z,
        zAxis.x, zAxis.y, zAxis.z,
    );
    return Quat.normalize(out, Quat.fromMat3(out, m3_1));
}

視口和上方向

根據視口的前方向和上方向,先計算本地坐標軸的右向量,再算出本地坐標的上向量,最後再構造成四元數。

/**
* @zh 根據視口的前方向和上方向計算四元數
* @param view 視口面向的前方向,必須歸一化
* @param up 視口的上方向,必須歸一化,默認為 (0, 1, 0)
*/
public static fromViewUp<Out extends IQuatLike, VecLike extends IVec3Like> (out: Out, view: VecLike, up?: Vec3) {
    Mat3.fromViewUp(m3_1, view, up);
    return Quat.normalize(out, Quat.fromMat3(out, m3_1));
}

兩向量間的最短路徑旋轉

也可以用一個四元數表示兩向量旋轉的最短路徑。

/**
* @zh 設置四元數為兩向量間的最短路徑旋轉,默認兩向量都已歸一化
*/
public static rotationTo<Out extends IQuatLike, VecLike extends IVec3Like> (out: Out, a: VecLike, b: VecLike) {
  // 省略代碼實現
}

矩陣/歐拉角

也可以通過其他表示方法轉換為四元數。

/**
* @zh 根據三維矩陣信息計算四元數,默認輸入矩陣不含有縮放信息
*/
public static fromMat3<Out extends IQuatLike> (out: Out, m: Mat3) {
    // 省略代碼實現
}

/**
* @zh 根據歐拉角信息計算四元數,旋轉順序為 YZX
*/
public static fromEuler<Out extends IQuatLike> (out: Out, x: number, y: number, z: number) {
    // 省略代碼實現
}

獲取四元數相關信息

上面講了如何去構造,相應的也可以通過四元數獲取相關信息,這裡不細講了含義了,直接看看API吧。

/**
* @zh 獲取四元數的旋轉軸和旋轉弧度
* @param outAxis 旋轉軸輸出
* @param q 源四元數
* @return 旋轉弧度
*/
public static getAxisAngle<Out extends IQuatLike, VecLike extends IVec3Like> (outAxis: VecLike, q: Out) {
    //...
}

/**
* @zh 返回定義此四元數的坐標系 X 軸向量
*/
public static toAxisX (out: IVec3Like, q: IQuatLike) {
    //...
}

/**
* @zh 返回定義此四元數的坐標系 Y 軸向量
*/
public static toAxisY (out: IVec3Like, q: IQuatLike) {
    //...
}

/**
* @zh 返回定義此四元數的坐標系 Z 軸向量
*/
public static toAxisZ (out: IVec3Like, q: IQuatLike) {
    //...
}

/**
* @zh 根據四元數計算歐拉角,返回角度 x, y 在 [-180, 180] 區間內, z 默認在 [-90, 90] 區間內,旋轉順序為 YZX
* @param outerZ z 取值範圍區間改為 [-180, -90] U [90, 180]
*/
public static toEuler (out: IVec3Like, q: IQuatLike, outerZ?: boolean) {
   //...
}

實際例子

沒有實戰,單純講API就是耍流氓!直接進入實戰部分!

角色朝向和平滑插值

已知當前點和下一個點,如何求出角色的朝向四元數?

const cur_p = list[index - 1]; // 當前點
const next_p = list[index]; // 最終點
const quat_end = new Quat(); // 最終旋轉四元數
const dir = next_p.clone().subtract(cur_p); // 前向量
// 模型正好朝z軸方向
Quat.fromViewUp(quat_end, dir.normalize(), v3(0, 1, 0)); // 根據視口的前方向和上方向計算四元數  
// 最終旋轉四元數 / 視口面向的前方向 / 視口的上方向

已知起始四元數和終點四元數,如何平滑旋轉?

const tw = tween(this.node_bezier_role); // 使用tween動畫
const quat_start = new Quat();
this.node_bezier_role.getRotation(quat_start); // 獲取起始四元數
const quat_end = new Quat(); // 最終旋轉四元數 假設已經算出
const quat_now = new Quat(); // 用一個中間變量
tw.to(0.2, {}, {
    onUpdate: (target, ratio: number) => {
        // ratio : 0~1
        // 這裡使用球面插值,旋轉時不會出現變形
        quat_now.set(quat_start).slerp(quat_end, ratio);
        this.node_bezier_role.setRotation(quat_now);
    },
})
tw.start();

將旋轉和移動結合起來就能達到下面這個效果。

觸摸旋轉

關鍵是求出旋轉軸,這邊處理的旋轉軸在 xoy 這個平面上。

//  private onTouchMove(touch: Touch) {
const delta = touch.getDelta();

// 自傳
// 這個物體模型『錨點』在正中心效果比較好
// 垂直的軸,右手  
//  
//  旋轉軸
//  ↑
//  ---> 觸摸方向
const axis = v3(-delta.y, delta.x, 0); //旋轉軸,根據相似三角形求出
const rad = delta.length() * 1e-2; //旋轉角度
const quat_cur = this.node_touch_rotation_role.getRotation(); //當前的四元數
Quat.rotateAround(this.__temp_quat, quat_cur, axis.normalize(), rad); //當面的四元數繞旋轉軸旋轉
// 旋轉後的結果 / 當前的四元數 / 旋轉軸 / 旋轉四元數
this.node_touch_rotation_role.setRotation(this.__temp_quat);

展示結果如下:

繞軸旋轉

已知旋轉點、旋轉軸、旋轉角度,求旋轉後的位置和朝向。

朝向計算和觸摸旋轉類似,這裡不詳說了。

這邊講講如何計算旋轉後的坐標。

//  private onTouchMove(touch: Touch) {
const delta = touch.getDelta();
// 繞軸轉
// 這裡選取軸朝上
const axis2 = Vec3.UP;//旋轉軸
const rad2 = 1e-2 * delta.x; //旋轉角度
// 計算坐標
const point = this.node_axi.worldPosition; //旋轉點
const point_now = this.node_touch_axi_role.worldPosition; // 當前點的位置
// 算出坐標點的旋轉四元數
Quat.fromAxisAngle(this.__temp_quat, axis2, rad2);
// 計算旋轉點和現有點的向量
Vec3.subtract(this.__temp_v3, point_now, point);
// 計算旋轉後的向量
Vec3.transformQuat(this.__temp_v3, this.__temp_v3, this.__temp_quat)
// 計算旋轉後的點
Vec3.add(this.__temp_v3, point, this.__temp_v3);
this.node_touch_axi_role.setWorldPosition(this.__temp_v3);

// 計算朝向
// 這麼旋轉會按原始的朝向一起旋轉
const quat_now = this.node_touch_axi_role.worldRotation;
Quat.rotateAround(this.__temp_quat, quat_now, axis2, rad2);
Quat.normalize(this.__temp_quat, this.__temp_quat);
this.node_touch_axi_role.setWorldRotation(this.__temp_quat);

最終效果如下。

小結

可以把四元數當作一個工具,想想旋轉可以是用軸角度,本地坐標系,或者視角方向構造出來的,再使用相應的接口去實現我們的各種需求。

以上為白玉無冰使用 Cocos Creator 3D v1.2 實現 "四元數與旋轉" 的技術分享。歡迎分享給身邊的朋友!

參考https://docs.cocos.com/creator3d/api/zh/classes/core_math.quat.htmlhttps://en.wikipedia.org/wiki/Quaternionhttps://eater.net/quaternionshttps://github.com/Krasjet/quaternionhttps://forum.cocos.org/t/creator-3d-unity-transfrom-rotatearound-api/85157/5https://forum.cocos.org/t/topic/92924/11https://forum.cocos.org/t/creator-3d/91299


轉載請保留文末二維碼和完整代碼獲取方式!

完整代碼(詳見readme): 

https://github.com/baiyuwubing/cocos-creator-3d-examples/tree/master/1-2-x

點擊「閱讀原文」查看精選導航

『讚賞』「點讚「 」在看」 鼓勵一下▼


相關焦點

  • Cocos Creator 3D v1.0.1 正式發布
    格式支持 khr-draco-mesh-compression[FEATURE] 內建材質支持頂點色[IMPROVE] 使用二進位格式存儲並優化骨骼動畫序列化後的尺寸[IMPROVE] 大幅降低 Profiler 在渲染過程中的性能損耗[IMPROVE] 復用材質實例
  • Cocos Creator 3D!
    記得在寫小雞拍拍的時候遇到一個問題,想要捕捉排球的點擊事件,按照 2d 的寫法,給3d 節點添加 node 事件,結果點了沒反應。代碼大概是以下的樣子。this.camera_3d.screenPointToRay(touch._point.x, touch._point.y, this._ray);接下來就是檢測這條線穿過了哪些物體啦。creator 3d 提供了三種檢測方案,可以一起看看是如何使用的。基於物理碰撞器的射線檢測:我們先給需要檢測的物體添加碰撞器。
  • 剛體運動變換的描述——四元數
    一、(Eigen)四元數類型常用的四元數格式有Quaternionf(float)和Quaterniond(double),模板類中的Scalar決定數據類型。 q7(1.0, 0.0, 0.0, 0.0);    q7.normalized();    // *注意,必須得是單位四元數才能表示旋轉矩陣    Eigen::Matrix3d R = q7.toRotationMatrix(); //由羅德裡格公式進行轉換    //Eigen::Matrix3d R = q7.matrix();  //直接轉換
  • 三維CAD實例教程:使用中望3D設計葉輪
    三維CAD實例教程:使用中望3D設計葉輪 2016年08月09日 13:44作者:薛麗潔編輯:薛麗潔文章出處:泡泡網原創 葉輪的造型獨特,而在中望3D中,僅需要通過旋轉、圓周陣列、掃掠、拉伸、倒圓角等功能即可輕鬆完成葉輪的建模設計。在本篇文章中,小編將以圖1中的葉輪為實例,向大家介紹如何使用中望3D進行葉輪設計。
  • Cocos Creator!
    將這個標準正態分布產生的數值進行一定的轉化,可以讓飛濺運動的物體的初速度 95% 的概率在我們的速度範圍內。完整項目:https://github.com/baiyuwubing/cocos-creator-examples/tree/master/splash我就知道你「在看」▼
  • Cocos2d-x v3.3-RC0 發布說明! - OSCHINA - 中文開源技術交流社區
    Cocos2d-x v3.3-RC0下載地址:http://cn.cocos2d-x.org/download關於v3.3各個版本的特性和改動的話,可以參考這篇文檔。因為加載這兩個lua文件會返回一個對象,lua代碼會使用返回的對象進行操作,所以需要替換對應的加載路徑,具體修改如下:require "luaj" -> require "cocos.cocos2d.luaj"require "luaoc" -> require "cocos.cocos2d.luaoc"兼容舊API的lua文件默認沒有被加載上來
  • 索尼3d電視機價格怎麼樣?索尼3d電視價格【詳解】
    下面來介紹些索尼3d電視價格的相關內容。3d電視成為了各大 家電 廠商的熱衷的發展方向,成就了更多的產品類型。在這方面,索尼就做得很不錯,在電視領域實現了自己的發展目標。下面就讓我們來瞧一瞧吧。   索尼3d電視價格  就索尼3d電視電視來講,畢竟品牌和生產的配置擺在那裡,電視的價格不會在太低的層次。
  • 「3d掃描儀結合3d列印技術」學校3d創新教育的一把利器
    3d創新教育是以培養學生創新精神、創新能力、動手實踐能力為價值取向的新型教育。在3d創新教育中,我們要如何讓「3d掃描儀、3d列印技術」這兩把利器發揮重要作用呢?從提供3d掃描獲取數據到3d設計、數據再創造再到3d列印創意實現的完整解決方案是廣大師生的呼聲,更是教育改革時代背景下的一條創新之路。
  • 創維3d雲電視怎麼樣?創維3d雲電視評測及報價介紹【圖文】
    那麼接下來下面就具體為大家評測一款 創維 3d 雲電視 ,以及它的市場售價,以供大家進行了解。  二、創維3d雲電視市場報價  這款創維3d雲電視的市場價格為1852元,總體上來說,性價比還是比較高的。以上價格來源於網絡,僅供大家進行參考。
  • 雷射3d印表機多少錢 雷射3d印表機價格及工作原理【詳解】
    雷射3D印表機是一種新型的 高科 技產品,能夠列印一切物品,3d技術在之前的新聞報導中我們就已經見識了它的神奇程度,可以應用於不同領域,幫助人們解決很多麻煩,甚至也可以解決在生物製造技術上的難題。雷射3d印表機就是3d印表機的升級版,那麼雷射3d印表機的工作原理是否和之前的相同呢?
  • 三維旋轉:歐拉角、四元數、旋轉矩陣、軸角之間的轉換
    2 四元數(Quaternion)與旋轉矩陣2.1 四元數---->旋轉矩陣眾所周知的是,歐拉旋轉是有萬向節死鎖(Gimbal Lock)的問題的。幸好我們有四元數(Quaternion)這種數學工具可以避免這個情況。一般來說,我們都會用單位四元數
  • 三維CAD實例:用中望3D設計花瓣吊燈(中篇)
    圖23 焊縫創建效果    到此,我們就完成了花瓣吊燈燈座、支撐杆、焊縫的設計了,剩餘的部分,我們將在下篇三維CAD實例教程文章中向大家介紹馬上下載高端三維CAD/CAM軟體中望3D 2016版,享受高效靈活的設計體驗:http://www.zw3d.com.cn/product-58-1.html
  • Cocos Creator
    不清楚也沒關係,記得最後的公式就可以了,接下來進入 cocos creator 操作環節。既然是物理系統中的碰撞檢測,我們在編輯器裡添加的是物理系統中的碰撞器,而不是引擎的碰撞器,不要選錯了哦。不動的剛體類型設為 static添加完所有的物理碰撞器後如下所示。用到物理引擎自然要把物理引擎打開。
  • 3d投影儀怎麼樣 3d投影儀推薦【圖文】
    投影儀隨著人們的需求加大還設計出了3d版的,視覺效果更加顯著,接著就為大家推薦幾款不錯的3d投影儀。   3d投影儀推薦  明基MX514P  明基MX514P 投影機 採用DLP顯示技術,並支持3D顯示功能,
  • 全彩LED顯示屏適合於哪種3D方案?主動式3D還是偏光3D立體
    電影院紅外快門式3d眼鏡二.它是DLP晶片廠商TI提供的一種低成本的3D立體方案,只要是DLP 3D Ready的投影機都可以使用,它的原理是,當投影機啟動3D時,3d同步信號(一個白光脈衝)就會被嵌入到圖像中,和圖像一起投射到銀幕上,再反射到3d眼鏡上,由DLP3d眼鏡分離出白光脈衝來實現左眼鏡圖像分離。由於每幀圖像都增加了一個白光脈衝會使得圖像的對比度下降。
  • 「3d走勢圖帶連線專業版」百家號財經領域排名-最新大v排行榜作者...
    站長之家百家號傳媒平臺對3d走勢圖帶連線專業版的運營數據估算如下: 預估總閱讀數:2萬-4萬,綜合排名25.6走勢圖帶連線專業版百家號推廣估值及分析 目前3d走勢圖帶連線專業版百家號預估推廣價格為18-58元之間不等,該價格綜合排名17.3萬 ,高於整體84.4%的作者預估價,財經分類排名3311,高於財經分類89.9%的作者預估價。
  • 3d常見知識科普二:3d眼鏡的類型
    說的3d眼鏡,細心的朋友可能發現,有的眼鏡很薄,有的眼鏡很厚重,需要充電。這是為什麼呢?與什麼有關呢?其實3d眼鏡的類型,和播放設備有關。首先,3d眼鏡主要分為三兩種:1,快門式快門式需要充電,工作原理是左右眼同一時間只有其中一隻眼可以看到,另一隻眼被擋住,通過快速的交替產生視覺延遲,產生3d的效果。
  • 3D印表機遲遲普及不了大眾的原因
    我們都知道,3d印表機的出現,不停在改變著我們的生活,而且3d印表機可以滿足我們對於動漫電影的各項幻想,讓我們隨心所欲列印出我們所需的物品。這是第一個原因,其次,作為個人創客手辦列印的,一般用得最多就數FDM與光固化了,先來說說FDM的臺式列印,儘管這類列印的耗材以及做到了可生物降解以及氣味上的優化,但是實際在列印過程還是會遇到各種問題,比如加熱穩定,堵口等相關,不過這些都很好解決,就是列印後處理是比較麻煩,然後就是列印精度的問題,比起以上的問題,作為fdm列印的模型,在細節上的處理一直是個問題,而且好的精度高的價格卻需要更貴
  • 基於HTML5的WebGL經典3D虛擬機房漫遊動畫
    數據模型中,詳情請參考HT for Web 序列化手冊:var g3d = window.g3d = new ht.graph3d.Graph3dView(), dataModel = g3d.dm(), view = g3d.getView(), path = null; g3d.setMovableFunc(function(data) { return false; }); g3d.setVisibleFunc
  • 基於HTML5 的 WebGL 3D 版俄羅斯方塊
    ,只需使用 setRotation 函數對 block 進行旋轉即可:block.setRotation(Math.PI*rotationNum/2);方塊有了,現在就該讓它動起來了。= new ht.graph3d.Graph3dView(dataModel);g3d.addToDOM();var node = new ht.Node();node.s({  'shape3d': 'damBoard',  'shape3d.reverse.flip': true,  '3d.movable': false,  '3d.editable': false,  '3d.selectable