歐拉角和四元數的介紹及應用

一、歐拉角

歐拉角是用於描述物體在三維空間中旋轉的方法之一。它由三個連續旋轉組成,分別是繞x軸旋轉角度φ、繞y軸旋轉角度θ和繞z軸旋轉角度ψ,其旋轉順序可以是xyz、xzy、yxz、yzx、zxy、zyx等。

歐拉角的優點是直觀易懂,可以通過簡單的三個角度來描述旋轉情況。但缺點也是很明顯的,就是在某些情況下會出現萬向鎖問題。另外,由於歐拉角旋轉的過程是連續的,一旦多次旋轉累積起來,會導致誤差的積累。

//歐拉角轉為旋轉矩陣
function euler2mat(phi, theta, psi) {
  const cosPhi = Math.cos(phi);
  const sinPhi = Math.sin(phi);
  const cosTheta = Math.cos(theta);
  const sinTheta = Math.sin(theta);
  const cosPsi = Math.cos(psi);
  const sinPsi = Math.sin(psi);
  const mat = [
    [cosTheta * cosPsi, -cosPhi * sinPsi + sinPhi * sinTheta * cosPsi, sinPhi * sinPsi + cosPhi * sinTheta * cosPsi],
    [cosTheta * sinPsi, cosPhi * cosPsi + sinPhi * sinTheta * sinPsi, -sinPhi * cosPsi + cosPhi * sinTheta * sinPsi],
    [-sinTheta, sinPhi * cosTheta, cosPhi * cosTheta]
  ];
  return mat;
}

二、四元數

四元數是一種擴展了複數的數學結構,它由一個實部和三個虛部構成。可以用於表示旋轉操作和空間旋轉中的旋轉向量,同時解決了歐拉角的兩個問題,即避免萬向鎖和誤差積累。

四元數的乘法可以看作是兩個旋轉的合成,因此可以很方便地進行多次旋轉的疊加。而且只需要4個數就可以表示旋轉狀態,比歐拉角更加緊湊和高效。

//四元數旋轉矩陣轉歐拉角
function mat2euler(mat) {
  const theta1 = -Math.asin(mat[2][0]);
  const cosTheta1 = Math.cos(theta1);
  const phi1 = Math.atan2(mat[2][1] / cosTheta1, mat[2][2] / cosTheta1);
  const psi1 = Math.atan2(mat[1][0] / cosTheta1, mat[0][0] / cosTheta1);
  return [phi1, theta1, psi1];
}

//四元數類
class Quaternion {
  constructor(w, x, y, z) {
    this.w = w || 1.0;
    this.x = x || 0.0;
    this.y = y || 0.0;
    this.z = z || 0.0;
  }
  normalize() {
    const norm = Math.sqrt(this.w ** 2 + this.x ** 2 + this.y ** 2 + this.z ** 2);
    if (norm === 0) return;
    this.w /= norm;
    this.x /= norm;
    this.y /= norm;
    this.z /= norm;
  }
  multiply(q) {
    const w1 = this.w,
      x1 = this.x,
      y1 = this.y,
      z1 = this.z;
    const w2 = q.w,
      x2 = q.x,
      y2 = q.y,
      z2 = q.z;
    this.w = w1 * w2 - x1 * x2 - y1 * y2 - z1 * z2;
    this.x = w1 * x2 + x1 * w2 + y1 * z2 - z1 * y2;
    this.y = w1 * y2 - x1 * z2 + y1 * w2 + z1 * x2;
    this.z = w1 * z2 + x1 * y2 - y1 * x2 + z1 * w2;
    this.normalize();
  }
}

三、應用實例

在遊戲開發中,我們通常會使用三維渲染引擎來展現一個三維場景。在這個場景中,有很多需要旋轉的物體,比如角色、相機和物體等。下面我們以相機為例,展示歐拉角和四元數在相機旋轉中的應用。

1、歐拉角控制相機旋轉

在WebGL中,我們可以使用歐拉角來控制相機的旋轉。假設我們有一個相機對象,可以通過鼠標控制其繞x和y軸的旋轉角度,代碼如下:

class Camera {
  constructor() {
    this.position = [0, 0, 5];
    this.target = [0, 0, 0];
    this.up = [0, 1, 0];
    this.dist = Math.sqrt(this.position[0] ** 2 + this.position[1] ** 2 + this.position[2] ** 2);
    this.phi = Math.atan2(this.position[1], this.position[0]);
    this.theta = Math.acos(this.position[2] / this.dist);
  }
  rotate(deltaPhi, deltaTheta) {
    this.phi += deltaPhi;
    this.theta += deltaTheta;
    if (this.theta  Math.PI - 0.01) this.theta = Math.PI - 0.01;

    const x = this.dist * Math.sin(this.theta) * Math.cos(this.phi);
    const y = this.dist * Math.sin(this.theta) * Math.sin(this.phi);
    const z = this.dist * Math.cos(this.theta);

    this.position = [x, y, z];
  }
  getViewMatrix() {
    const mat = euler2mat(this.theta, this.phi, 0);
    const zAxis = [mat[0][2], mat[1][2], mat[2][2]];
    const xAxis = [mat[0][0], mat[1][0], mat[2][0]];
    const yAxis = [mat[0][1], mat[1][1], mat[2][1]];

    const tx = -xAxis[0] * this.position[0] - xAxis[1] * this.position[1] - xAxis[2] * this.position[2];
    const ty = -yAxis[0] * this.position[0] - yAxis[1] * this.position[1] - yAxis[2] * this.position[2];
    const tz = -zAxis[0] * this.position[0] - zAxis[1] * this.position[1] - zAxis[2] * this.position[2];

    return [
      xAxis[0], yAxis[0], zAxis[0], 0,
      xAxis[1], yAxis[1], zAxis[1], 0,
      xAxis[2], yAxis[2], zAxis[2], 0,
      tx, ty, tz, 1
    ];
  }
}

2、四元數控制相機旋轉

除了使用歐拉角之外,我們還可以使用四元數來控制相機的旋轉。這裡假設我們有一個相機對象和一個旋轉四元數q,可以通過鼠標操作旋轉四元數,然後計算出相機的旋轉矩陣,代碼如下:

class Camera {
constructor() {
this.position = [0, 0, 5];
this.target = [0, 0, 0];
this.up = [0, 1, 0];
this.dist = Math.sqrt(this.position[0] ** 2 + this.position[1] ** 2 + this.position[2] ** 2);
this.phi = Math.atan2(this.position[1], this.position[0]);
this.theta = Math.acos(this.position[2] / this.dist);
this.quaternion = new Quaternion();
}
rotate(deltaPhi, deltaTheta) {
this.phi += deltaPhi;
this.theta += deltaTheta;
if (this.theta Math.PI - 0.01) this.theta = Math.PI - 0.01;

const x = this.dist * Math.sin(this.theta) * Math.cos(this.phi);
const y = this.dist * Math.sin(this.theta) * Math.sin(this.phi);
const z = this.dist * Math.cos(this.theta);

this.position = [x, y, z];
const mat = euler2mat(this.theta, this.phi, 0);
this.quaternion = new Quaternion().fromMat(mat);
}
getViewMatrix() {
const mat = this.quaternion.toMat();
const zAxis = [mat[0][2], mat[1][2], mat[2][2]];
const xAxis = [mat[0][0], mat[1][0], mat[2][0]];
const yAxis = [mat[0][1], mat[1][1], mat[2][1]];

const tx = -xAxis[0] * this.position[0] - xAxis[1] * this.position[1] - xAxis[2] * this.position[2];
const ty = -yAxis[0] * this.position[0] - yAxis[1] * this.position[1] - yAxis[2] * this.position[2];
const tz = -zAxis[0] * this.position[0] - zAxis[1] * this.position[1] - zAxis[2] * this.position[2];

return [
xAxis[0], yAxis[0], zAxis[0], 0,
xAxis[1], yAxis[1], zAxis[1], 0,
xAxis[2], yAxis[2], zAxis[2], 0,
tx, ty, tz, 1
];
}
}

class Quaternion {
constructor(w, x, y, z) {
this.w = w || 1.0;
this.x = x || 0.0;
this.y = y || 0.0;
this.z = z || 0.0;
}
normalize() {
const norm = Math.sqrt(this.w ** 2 + this.x ** 2 + this.y ** 2 + this.z ** 2);
if (norm === 0) return;
this.w /= norm;
this.x /= norm;
this.y /= norm;
this.z /= norm;
}
multiply(q) {
const w1 = this.w,
x1 = this.x,
y1 = this.y,
z1 = this.z;
const w2 = q.w,
x2 = q.x,
y2 = q.y,
z2 = q.z;
this.w = w1 * w2 - x1 * x2 - y1 * y2 - z1 * z2;
this.x = w1 * x2 + x1 * w2 + y1 * z2 - z1 * y2;
this.y = w1 * y2 - x1 * z2 + y1 * w2 + z1 * x2;
this.z = w1 * z2 + x1 * y2 - y1 * x2 + z1 * w2;
this.normalize();
}
fromMat(mat) {
const trace = mat[0][0] + mat[1][1] + mat[2][2];
if (trace >= 0) {
const s = Math.sqrt(trace + 1) * 2;
const w = 0.25 * s;
const x = (mat[2][1] - mat[1][2]) / s;
const y = (mat[0][2] - mat[2][0]) / s;
const z = (mat[1][0] - mat[0][1]) / s;
this.w = w;
this.x = x;
this.y = y;
this.z = z;
} else if (mat[0][0] > mat[1][1] && mat[0][0] > mat[2][2]) {
const s = Math.sqrt(1 + mat[0][0] - mat[1][1] - mat[2][2]) * 2;
const w = (mat[2][1] - mat[1][2]) / s;
const x = 0.25 * s;
const y = (mat[0][1] + mat[1][0]) / s;
const z = (mat[0][2] + mat[2][0]) / s;
this.w = w;
this.x = x;
this.y = y;
this.z = z;
} else if

原創文章,作者:AJNLU,如若轉載,請註明出處:https://www.506064.com/zh-hant/n/333194.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
AJNLU的頭像AJNLU
上一篇 2025-01-27 13:34
下一篇 2025-01-27 13:34

相關推薦

  • 深入探究單位四元數

    單位四元數是一種旋轉表示方法,它被廣泛應用於計算機圖形學、機器人學、動畫製作等領域。在本文中,我們將從多個方面深入探究單位四元數,包括基本概念、構建方法、運算規則、旋轉應用等。 一…

    編程 2025-04-12
  • 四元數轉歐拉角詳解

    一、四元數轉歐拉角表 下面是一個關於四元數轉歐拉角的表格。這個表格展示了每個方向的旋轉,四元數表示和歐拉角表示。 方向 四元數表示 歐拉角表示 X軸 [cos(θ/2), sin(…

    編程 2024-12-12
  • 四元數與歐拉角關係

    本文目錄一覽: 1、求兩個單位向量之間的歐拉角 2、如何用Matlab實現四元數到歐拉角的轉換?(急需轉換的代碼) 3、四元素轉歐拉角為什麼不可逆 4、unity 四元數為什麼轉歐…

    編程 2024-12-12
  • 歐拉角轉換旋轉矩陣

    一、歐拉角轉換旋轉矩陣MATLAB 歐拉角是描述空間中物體運動姿態的一種常用方式,而旋轉矩陣是表示物體在三維空間內旋轉的一種方式。歐拉角和旋轉矩陣之間可以互相轉換,MATLAB提供…

    編程 2024-11-19
  • 歐拉定理證明詳解

    一、歐拉定理是什麼 歐拉定理(Euler’s theorem)是一條數論中的基本定理,它表明如果a和n是正整數,且它們互質,則a的歐拉函數(Euler function…

    編程 2024-11-02
  • 歐拉角和四元數的區別

    一、歐拉角與四元數的基礎介紹 歐拉角是指三個旋轉角度,分別為繞X軸旋轉的角度、繞Y軸旋轉的角度和繞Z軸旋轉的角度,表示物體在空間中的位置和方向。而四元數是一種複數形式的數字,由實部…

    編程 2024-10-04

發表回復

登錄後才能評論