[Análisis detallado del motor de juego Overload] Fórmula de Quaternion para codificar

Fórmula del cuaternión

Este artículo solo registra las fórmulas relacionadas con los cuaterniones y no habla de sus principios matemáticos porque yo tampoco los entiendo. Introduzca estas fórmulas matemáticas únicamente en Sobrecarga al código.

1. Definición de cuaternión

William Hamilton inventó los cuaterniones en 1843 como un método que permitía multiplicar, dividir, rotar y estirar vectores. Un cuaternión es un número complejo que consta de cuatro números, un escalar y un vector. [w,v], w es un escalar, v es un vector 3D, después de la expansión es——[w, (x,y,z)]: q = w + x ∗ i + y ∗ j + z ∗
k \mathbf{ q} = w + x*i+y*j+z*kq=w+Xi+yj+zk
en:
yo 2 = j 2 = k 2 = − 1 i^2=j^2=k^2=-1i2=j2=k2=1ij = − ji = k ij=-ji=kyo=ji=kki = − ik = j ki=-ik=ja=yo=jjk = − kj = yo jk=-kj=ijk=kj=yo ,

Para cualquier cuaternión dado, se puede representar una rotación en el espacio 3D. En el espacio 3D, cualquier rotación se puede expresar como un ángulo de rotación alrededor de un eje. Como sugiere el nombre, el ángulo del eje es un eje A (ax, ay, az) más un ángulo de rotación. Convierte cuaterniones en ángulos de eje. Entonces, cualquier cuaternión dado puede representar una rotación en el espacio 3D.
Eche un vistazo a la definición de cuaterniones en Overload: simplemente encapsula 4 números y define muchas funciones.

struct FQuaternion
{
    
    
public:
	float x; // 矢量部分
	float y;
	float z;
	float w; // 标量部分
}
2. Cálculos comunes de cuaterniones.

1. 相加
q ^ + r ^ = ( qw , qv ) + ( rw , rv ) = ( qw + rw , qv + rv ) \hat{\mathbf{q}} + \hat{\mathbf{r}} =(\mathbf{q}_w, q_v)+(\mathbf{r}_w, r_v)=(\mathbf{q}_w+\mathbf{r}_w, q_v+r_v)q^+r^=( qw,qv)+( rw,rv)=( qw+rw,qv+rv)
código de sobrecarga correspondiente:

OvMaths::FQuaternion OvMaths::FQuaternion::operator+(const FQuaternion& p_otherQuat) const
{
    
    
    return FQuaternion(x + p_otherQuat.x, y + p_otherQuat.x,
z + p_otherQuat.z, w + p_otherQuat.w);
  }

2. 相乘
q ^ × r ^ = ( iqx + jqy + kqz + qw ) ( irx + jry + krz + rw ) = qwrw − qxrx − qyry − qzrz + i ( qyrz − qzry + rwqx + qwrx ) + j ( qzrx − qxrz + rwqy + qwry ) + k ( qxry − qyrx + rwqz + qwrz ) \begin{aligned} \hat{\mathbf{q}}\times \hat{\mathbf{r}} &= (iq_x+jq_y +kq_z+q_w)(ir_x+jr_y+kr_z+r_w)\\&=q_wr_w-q_xr_x-q_yr_y-q_zr_z ​​\\ &+i(q_yr_z-q_zr_y+r_wq_x+q_wr_x)\\ &+j(q_zr_x-q_xr_z+r_wq_y +q_wr_y)\\ &+k(q_xr_y-q_yr_x+r_wq_z+q_wr_z)\end{alineado}q^×r^=( yo qx+j qy+k qz+qw) ( y rx+jr _y+k rz+rw)=qwrwqxrxqyryqzrz+yo ( qyrzqzry+rwqx+qwrx)+j ( qzrxqxrz+rwqy+qwry)+k ( qxryqyrx+rwqz+qwrz)
Código de sobrecarga correspondiente:

OvMaths::FQuaternion OvMaths::FQuaternion::operator*(const FQuaternion& p_otherQuat) const
{
    
    
	return FQuaternion
	(
		x * p_otherQuat.w + y * p_otherQuat.z - z * p_otherQuat.y + w * p_otherQuat.x,
		-x * p_otherQuat.z + y * p_otherQuat.w + z * p_otherQuat.x + w * p_otherQuat.y,
		x * p_otherQuat.y - y * p_otherQuat.x + z * p_otherQuat.w + w * p_otherQuat.z,
		-x * p_otherQuat.x - y * p_otherQuat.y - z * p_otherQuat.z + w * p_otherQuat.w
	);
}


3. Invierta la parte del vector conjugado e ingrese directamente el código:

OvMaths::FQuaternion OvMaths::FQuaternion::Conjugate(const FQuaternion & p_target)
{
    
    
	return {
    
     -p_target.x, -p_target.y, -p_target.z, p_target.w };
}

4. Determina
q ^ = q ^ / w 2 + x 2 + y 2 + z 2 \hat{\mathbf{q}} = \hat{\mathbf{q}} / \sqrt{w^2+x^ 2 +y^2+z^2}q^=q^/w2+X2+y2+z2

5. 点积
q ^ ⋅ r ^ = qx ∗ rx + qy ∗ ry + qz ∗ rz + qw ∗ rw \hat{\mathbf{q}} \cdot \hat{\mathbf{r}} = q_x*r_x + q_y*r_y + q_z*r_z + q_w*r_wq^r^=qxrx+qyry+qzrz+qwrw
Código:

float OvMaths::FQuaternion::DotProduct(const FQuaternion & p_left, const FQuaternion & p_right)
{
    
    
	return 
		p_left.x * p_right.x +
		p_left.y * p_right.y +
		p_left.z * p_right.z +
		p_left.w * p_right.w;
}

6. Interpolación
La interpolación de dos cuaterniones q y r se puede utilizar para animar el proceso de rotación.
El cálculo de interpolación debe calcular primero la mitad del ángulo entre q y r θ \thetaθ , qfrecuenciacos ( θ ) cos(\theta)cos ( θ ) y luego use la función acos para encontrarθ \thetaθ
θ = acos ( q ^ ⋅ r ^ ) \theta = acos(\hat{\mathbf{q}} \cdot \that{\mathbf{r}})i=porque ( _q^r^ )
da la cantidad de interpolación t, 0<t<1.0, la cantidad de interpolación se puede obtener:
Q m = q ^ ∗ sin ( ( 1 − t ) ∗ θ ) + r ^ sin ( t ∗ θ ) sin ( θ ) Q_m= \frac{\hat{\mathbf{q}}*sin((1-t)*\theta) + \hat{\mathbf{r}}sin(t*\theta)}{sin(\theta) }qm=s en ( yo )q^s en (( 1t )yo )+r^ sen(tyo ).

Código de sobrecarga correspondiente:

OvMaths::FQuaternion OvMaths::FQuaternion::Slerp(const FQuaternion& p_start, const FQuaternion& p_end, float p_alpha)
{
    
    
	FQuaternion from = p_start; // 第一个四元数
	FQuaternion to = p_end; // 第二个四元数

	p_alpha = std::clamp(p_alpha, 0.f, 1.f); // 插值量t
	float cosAngle = FQuaternion::DotProduct(from, to); // 计算四元数的点积

	if (cosAngle < 0.f) // 小于0说明两个四元数角度大于180度,to为什么要翻转呢?
	{
    
    
		cosAngle = -cosAngle;
		to = FQuaternion(-to.x, -to.y, -to.z, -to.w);
	}

	if (cosAngle < 0.95f) // 两个四元数角度较大时
	{
    
    
		float angle = std::acos(cosAngle); // 计算角度theta
		float sinAngle = std::sin(angle); // 计算sin(theta)
		float invSinAngle = 1.f / sinAngle;
		float t1 = std::sin((1 - p_alpha) * angle) * invSinAngle;
		float t2 = std::sin(p_alpha * angle) * invSinAngle;
		return FQuaternion(from.x * t1 + to.x * t2, from.y * t1 + to.y * t2, from.z * t1 + to.z * t2, from.w * t1 + to.w * t2);
	}
	else
	{
    
    
	    // 两个四元数角度较小时
		return FQuaternion::Lerp(from, to, p_alpha);
	}
}

Cuando el ángulo entre los dos cuaterniones es pequeño, sin ( θ ) sin(\theta)s en ( θ ) tiene un problema de división por 0, por lo que se realiza un tratamiento especial.

3. Ángulo del eje al cuaternión

Entrada: eje de rotación A (ax, ay, az), ángulo de rotación ángulo
Salida:
qx = ax * sin(angle/2)
qy = ay * sin(angle/2)
qz = az * sin(angle/2)
qw = porque(ángulo/2)

Entre ellos:
el eje de rotación es un vector unitario, entonces: ax ax + ay ay + az*az = 1
El cuaternión calculado también está unitario, entonces:
cos (angle / 2) 2 + ax ∗ ax ∗ sin (angle / 2 ) 2 + ay ∗ ay ∗ sin ( ángulo / 2 ) 2 + az ∗ az ∗ sin ( ángulo / 2 ) 2 = 1 cos(ángulo/2)^2 + ax*ax * sin(ángulo/2)^2 + ay*ay * sin(ángulo/2)^2+ az*az * sin(ángulo/2)^2 = 1cos ( ángulo e / 2 ) _2+una xuna xs en ( ángulo e / 2 ) _2+y yy ys en ( ángulo e / 2 ) _2+la zla zs en ( ángulo e / 2 ) _2=1

4. Ángulo del eje del cuaternión

x = qx / raíz cuadrada ( 1 − qw ∗ qw ) x = qx / raíz cuadrada(1-qw*qw)X=q x / s q r t ( 1qwqw )
y = qy / sqrt ( 1 − qw ∗ qw ) y = qy / sqrt(1-qw*qw)y=q y / s q r t ( 1qwqw )
z = qz / sqrt ( 1 − qw ∗ qw ) z = qz / sqrt(1-qw*qw)z=q z / s q r t ( 1qwqw )
ángulo = 2 ∗ acos ( qw ) ángulo = 2 * acos(qw)ángulo _ _ _=2a porque ( qw )

  • El cálculo del eje de singularidad
    es un valor con singularidad. Cuando el ángulo = 0, hay un problema de división por 0.
              q = cos(ángulo/2) + i ( x * sin(ángulo/2)) + j (y * sin(ángulo/2)) + k ( z * sin(ángulo/2)) cuando el ángulo es 0 grados
    Cuando , el cuaternión es q = 1 + i 0 + j 0 + k 0, entonces al calcular el eje,
    ángulo = 2 * acos(qw) = 0
    x = qx / sqrt(1-qw qw) = dividir por cero = infinito
    y = qy / sqrt(1-qw
    qw) = dividir por cero = infinito
    z = qz / sqrt(1-qw*qw) = dividir por cero = infinito
    pero girar 0 grados equivale a no girar, por lo que el eje dado puede ser cualquier valor.

El código correspondiente en Sobrecarga:

OvMaths::FVector3 OvMaths::FQuaternion::GetRotationAxis(const FQuaternion & p_target)
{
    
    
	const float S = sqrt(std::max(1.f - (p_target.w * p_target.w), 0.f));

	if (S >= 0.0001f)
	{
    
    
		return FVector3(p_target.x / S, p_target.y / S, p_target.z / S);
	}

	return FVector3(1.f, 0.f, 0.f);
}

5. Convertir cuaternión en matriz

Los cuaterniones se utilizan principalmente para representar rotaciones en gráficos. Para el cuaternión qw + i qx + j qy + k qz, las matrices de rotación correspondientes de 3x3 y 4x4 son las siguientes:
R 3 , 3 = [ 1 − 2 ( y 2 + z 2 ) 2 ( xy − zw ) 2 ( xz + yw 2 ( xy + zw ) 1 - 2 ( x 2 + z 2 ) 2 ( yz - xw ) 2 ( xz - yw ) 2 ( yz + xw ) 1 - 2 ( x 2 + y 2 ) ] R_ 3,3}=\begin{bmatrix} 1-2(y^2+z^2) & 2(xy-zw) & 2(xz+yw)\\ 2(xy+zw) & 1-2(x ^2+z^2) & 2(yz-xw)\\ 2(xz-yw) & 2(yz+xw) & 1-2(x^2+y^2) \end{bmatrix}R3 , 3= 12 ( y2+z2 )2 ( x y+zw ) _2 ( x zla w ).2 ( x yzw ) _12 ( x2+z2 )2 ( yz+xw ) _2 ( x z+la w )2 ( yzxw ) _12 ( x2+y2 )

R 4 , 4 = [ 1 − 2 ( y 2 + z 2 ) 2 ( xy − zw ) 2 ( xz + yw ) 0 2 ( xy + zw ) 1 − 2 ( x 2 + z 2 ) 2 ( yz − xw ) 0 2 ( xz − yw ) 2 ( yz + xw ) 1 − 2 ( x 2 + y 2 ) 0 0 0 0 1 ] R_{4,4}=\begin{bmatrix} 1-2(y^2+ z^2) & 2(xy-zw) & 2(xz+yw) & 0\\ 2(xy+zw) & 1-2(x^2+z^2) & 2(yz-xw) & 0 \\ 2(xz-yw) & 2(yz+xw) & 1-2(x^2+y^2) & 0\\ 0 & 0 & 0 & 1 \end{bmatrix}R4 , 4= 12 ( y2+z2 )2 ( x y+zw ) _2 ( x zla w )02 ( x yzw ) _12 ( x2+z2 )2 ( yz+xw ) _02 ( x z+la w )2 ( yzxw ) _12 ( x2+y2 )00001

4. Girar alrededor de un punto.

Los cuaterniones representan rotación alrededor del origen del sistema de coordenadas. La rotación alrededor de un punto, como su nombre lo indica, es una rotación alrededor de un punto específico. Es esencialmente un movimiento compuesto de traslación + rotación.
Supongamos que tenemos una matriz [R] que define una rotación alrededor del origen y ahora queremos hacer la misma rotación alrededor de cualquier punto P.
Podemos ver que su orientación es la misma que la rotación alrededor del origen, pero se ha trasladado a una posición diferente.

Su equivalente cinemático es:

  1. Traducir el objeto al origen (menos P, traducir usando los vectores -Px, -Py, -Pz)
  2. Girar alrededor del origen
  3. Traducir el objeto nuevamente (agregue P, use +Px, +Py, +Pz para traducir)

[Resultado de la transformación] = [+Px,+Py,+Pz] * [Girar alrededor del origen] * [-Px,-Py,-Pz]

En forma matricial:
[ R ] = [ T ] − 1 ∗ [ R ] ∗ [ T ] [R] = [T]^{-1} * [R] * [T][ R ]=[ T ]1[ R ][ T ]

Entre ellos:
[R] = matriz de rotación, que puede ser un cuaternión
[ T ] − 1 = [ 1 0 0 P x 0 1 0 P y 0 0 1 P z 0 0 0 1 ] [T]^{-1} = \begin{bmatrix} 1 & 0 & 0 & P_x\\ 0 & 1 & 0 & P_y\\ 0 & 0 & 1 & P_z\\ 0 & 0 & 0 & 1 \end{bmatrix}[ T ]1= 100001000010PAGxPAGyPAGz1

[ T ] = [ 1 0 0 − P x 0 1 0 − P y 0 0 1 − P z 0 0 0 1 ] [ T ]=\begin {bmatrix} 1 & 0 & 0 & -P_x\\ 0 & &0& -P_y\\0&0&1&-P_z\\0&0&0&1\end{bmatrix}[ T ]= 100001000010PAGxPAGyPAGz1

Determine lo siguiente:
[ r 00 r 01 r 02 P x − r 00 ∗ P x − r 01 ∗ P y − r 02 ∗ P zr 10 r 11 r 12 P y − r 10 ∗ P x − r 11 ∗ P y − r 12 ∗ P zr 20 r 21 r 22 P z − r 20 ∗ P x − r 21 ∗ P y − r 22 ∗ P z 0 0 0 1 ] \begin{bmatrix} r_{00} & r_{; 01 } & r____ { 10}*P_x - r_{11}*P_y - r_{12}*P_z\\ r_{20} & r_{21} & r_{22} & P_z - r_{20}*P_x - r_{21}* P_y - r_{22}*P_z\\ 0&0&0&1\end{bmatrix} r00r10r200r01r11r210r02r12r220PAGxr00PAGxr01PAGyr02PAGzPAGyr10PAGxr11PAGyr12PAGzPAGzr20PAGxr21PAGyr22PAGz1
Se puede comparar con alrededor del origen, el componente de rotación de la matriz es el mismo, solo se agrega el componente de traducción.

La fórmula del punto de bobinado en Sobrecarga es la siguiente:

// 点p_point绕原点旋转的坐标
OvMaths::FVector3 OvMaths::FQuaternion::RotatePoint(const FVector3& p_point, const FQuaternion& p_quaternion)
{
    
    
	FVector3 Q(p_quaternion.x, p_quaternion.y, p_quaternion.z);
	FVector3 T = FVector3::Cross(Q, p_point) * 2.0f;

	return p_point + (T * p_quaternion.w) + FVector3::Cross(Q, T);
}

// 点p_point绕p_pivot旋转后的坐标(这个地方我理解可能有误,转动之后不应该再加回来吗?)
OvMaths::FVector3 OvMaths::FQuaternion::RotatePoint(const FVector3 & p_point, const FQuaternion & p_quaternion, const FVector3 & p_pivot)
{
    
    
	FVector3 toRotate = p_point - p_pivot;
	return RotatePoint(toRotate, p_quaternion);
}

Utiliza otra fórmula. No he encontrado la fuente de esta fórmula. Debería ser equivalente a la fórmula anterior. No he realizado ninguna derivación ni verificación. No entiendo muy bien esta función. Espero que puedan dejarme un mensaje y darme algún consejo. ¡Gracias!

Supongo que te gusta

Origin blog.csdn.net/loveoobaby/article/details/133691686
Recomendado
Clasificación