<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          四元數(shù)與3D旋轉(zhuǎn)實例! Cocos Creator 3D Quternion !

          共 5584字,需瀏覽 12分鐘

           ·

          2020-10-25 12:33

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

          前言

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

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

          • 向量 (內(nèi)積外積/基本運算/幾何意義)
          • 坐標系(左手系/右手系/世界坐標/本地坐標)
          • 矩陣(平移/旋轉(zhuǎn)/縮放/模型矩陣/視圖矩陣/投影矩陣)
          • 視點和視線(視點/觀察目標/上方向)

          表示3D旋轉(zhuǎn)一般采用三種方法:

          • 矩陣
          • 歐拉角
          • 四元數(shù)

          為什么使用四元數(shù)表示旋轉(zhuǎn)呢?

          • 平滑插值。(矩陣基本沒有,歐拉角可以做插值,但可能遭遇萬向鎖的問題)
          • 快速連接和角位移求逆。
          • 能和矩陣快速轉(zhuǎn)換。
          • 僅用四個數(shù)。(矩陣9個,歐拉角3個)
          • 難以理解,學(xué)會了看起來很牛逼。

          當然四元數(shù)也有一些缺點:

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

          實例

          構(gòu)造四元數(shù)

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

          那么怎么去構(gòu)造這個四元數(shù)呢?我們從API入手去講解和理解。

          旋轉(zhuǎn)軸和旋轉(zhuǎn)角

          有了旋轉(zhuǎn)軸和旋轉(zhuǎn)角,就可以表示旋轉(zhuǎn)了,那么四元數(shù)也可以通過這個構(gòu)造出來。

          /**
          *?@zh?根據(jù)旋轉(zhuǎn)軸和旋轉(zhuǎn)弧度計算四元數(shù)
          */

          public?static?fromAxisAngleextends?IQuatLike,?VecLike?extends?IVec3Like>?(out:?Out,?axis:?VecLike,?rad:?number)?{
          ????rad?=?rad?*?0.5;?//?為什么要除以2?因為公式推導(dǎo)出來的!
          ????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;
          }

          本地坐標軸

          根據(jù)該物體本地坐標軸也能確定旋轉(zhuǎn)。

          /**
          *?@zh?根據(jù)本地坐標軸朝向計算四元數(shù),默認三向量都已歸一化且相互垂直
          */

          public?static?fromAxesextends?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));
          }

          視口和上方向

          根據(jù)視口的前方向和上方向,先計算本地坐標軸的右向量,再算出本地坐標的上向量,最后再構(gòu)造成四元數(shù)。

          /**
          *?@zh?根據(jù)視口的前方向和上方向計算四元數(shù)
          *?@param?view?視口面向的前方向,必須歸一化
          *?@param?up?視口的上方向,必須歸一化,默認為?(0,?1,?0)
          */

          public?static?fromViewUpextends?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));
          }

          兩向量間的最短路徑旋轉(zhuǎn)

          也可以用一個四元數(shù)表示兩向量旋轉(zhuǎn)的最短路徑。

          /**
          *?@zh?設(shè)置四元數(shù)為兩向量間的最短路徑旋轉(zhuǎn),默認兩向量都已歸一化
          */

          public?static?rotationToextends?IQuatLike,?VecLike?extends?IVec3Like>?(out:?Out,?a:?VecLike,?b:?VecLike)?{
          ??//?省略代碼實現(xiàn)
          }

          矩陣/歐拉角

          也可以通過其他表示方法轉(zhuǎn)換為四元數(shù)。

          /**
          *?@zh?根據(jù)三維矩陣信息計算四元數(shù),默認輸入矩陣不含有縮放信息
          */

          public?static?fromMat3extends?IQuatLike>?(out:?Out,?m:?Mat3)?{
          ????//?省略代碼實現(xiàn)
          }

          /**
          *?@zh?根據(jù)歐拉角信息計算四元數(shù),旋轉(zhuǎn)順序為?YZX
          */

          public?static?fromEulerextends?IQuatLike>?(out:?Out,?x:?number,?y:?number,?z:?number)?{
          ????//?省略代碼實現(xiàn)
          }

          獲取四元數(shù)相關(guān)信息

          上面講了如何去構(gòu)造,相應(yīng)的也可以通過四元數(shù)獲取相關(guān)信息,這里不細講了含義了,直接看看API吧。

          /**
          *?@zh?獲取四元數(shù)的旋轉(zhuǎn)軸和旋轉(zhuǎn)弧度
          *?@param?outAxis?旋轉(zhuǎn)軸輸出
          *?@param?q?源四元數(shù)
          *?@return?旋轉(zhuǎn)弧度
          */

          public?static?getAxisAngleextends?IQuatLike,?VecLike?extends?IVec3Like>?(outAxis:?VecLike,?q:?Out)?{
          ????//...
          }

          /**
          *?@zh?返回定義此四元數(shù)的坐標系?X?軸向量
          */

          public?static?toAxisX?(out:?IVec3Like,?q:?IQuatLike)?{
          ????//...
          }

          /**
          *?@zh?返回定義此四元數(shù)的坐標系?Y?軸向量
          */

          public?static?toAxisY?(out:?IVec3Like,?q:?IQuatLike)?{
          ????//...
          }

          /**
          *?@zh?返回定義此四元數(shù)的坐標系?Z?軸向量
          */

          public?static?toAxisZ?(out:?IVec3Like,?q:?IQuatLike)?{
          ????//...
          }

          /**
          *?@zh?根據(jù)四元數(shù)計算歐拉角,返回角度?x,?y?在?[-180,?180]?區(qū)間內(nèi),?z?默認在?[-90,?90]?區(qū)間內(nèi),旋轉(zhuǎn)順序為?YZX
          *?@param?outerZ?z?取值范圍區(qū)間改為?[-180,?-90]?U?[90,?180]
          */

          public?static?toEuler?(out:?IVec3Like,?q:?IQuatLike,?outerZ?:?boolean)?{
          ???//...
          }

          實際例子

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

          角色朝向和平滑插值

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

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

          已知起始四元數(shù)和終點四元數(shù),如何平滑旋轉(zhuǎn)?

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

          將旋轉(zhuǎn)和移動結(jié)合起來就能達到下面這個效果。

          觸摸旋轉(zhuǎn)

          關(guān)鍵是求出旋轉(zhuǎn)軸,這邊處理的旋轉(zhuǎn)軸在 xoy 這個平面上。

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

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

          展示結(jié)果如下:

          繞軸旋轉(zhuǎn)

          已知旋轉(zhuǎn)點、旋轉(zhuǎn)軸、旋轉(zhuǎn)角度,求旋轉(zhuǎn)后的位置和朝向。

          朝向計算和觸摸旋轉(zhuǎn)類似,這里不詳說了。

          這邊講講如何計算旋轉(zhuǎn)后的坐標。

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

          //?計算朝向
          //?這么旋轉(zhuǎn)會按原始的朝向一起旋轉(zhuǎn)
          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);

          最終效果如下。

          小結(jié)

          可以把四元數(shù)當作一個工具,想想旋轉(zhuǎn)可以是用軸角度,本地坐標系,或者視角方向構(gòu)造出來的,再使用相應(yīng)的接口去實現(xiàn)我們的各種需求。

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

          參考

          • 《WebGL編程指南》
          • 《3D數(shù)學(xué)基礎(chǔ):圖形與游戲開發(fā)》
          • https://docs.cocos.com/creator3d/api/zh/classes/core_math.quat.html
          • https://en.wikipedia.org/wiki/Quaternion
          • https://eater.net/quaternions
          • https://github.com/Krasjet/quaternion
          • https://forum.cocos.org/t/creator-3d-unity-transfrom-rotatearound-api/85157/5
          • https://forum.cocos.org/t/topic/92924/11
          • https://forum.cocos.org/t/creator-3d/91299


          轉(zhuǎn)載請保留文末二維碼和完整代碼獲取方式!

          完整代碼(詳見readme):?

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

          點擊閱讀原文”查看精選導(dǎo)航

          ‘贊賞’“點贊“ ”在看” 鼓勵一下



          瀏覽 105
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产又黄又嫩又滑又白 | 四虎影库亚洲无码 | 日韩A片在线 | 欧美成人版H性爽997777 | 免费播放黄色片网站 |