カメラのクォータニオンしかない場合、カメラのピッチを効率的に制限するにはどうすればよいですか? オイラー角に変換してからクォータニオンに戻す必要がありますか、それとも他の方法はありますか?
4 に答える
カメラにロールがない場合(一人称シューティングゲームなどの多くのゲームで一般的です)、解決策は簡単です。ロールがある場合は、追加の手順が必要です。ロールがない場合の対処方法から始め、ロールがある場合の対処方法を一般化します。
qcをカメラの回転とします。qcと同じヨーで、ただしピッチがゼロの回転をqyとします。ロールがない場合、カメラの回転はヨー回転とそれに続くピッチ回転です。
qc = qp * qy
ピッチ回転qpは、qyからqcへの回転として復元できます。
qp = qc * qy^-1
したがって、秘訣はqyを作成することです。これにより、qyを上記の方程式に代入して、qpを解くことができます。vcを、カメラのレンズを指す単位ベクトル、つまり「前方ベクトル」とします。vyを同じベクトルとしますが、水平面に投影され、正規化されます。最後に、カメラの回転qcがアイデンティティの回転である場合、v0を順方向ベクトルとします。v0をvyに回転させる回転は、ヨー回転です。角度は次のように指定できます。
yaw = asin(Norm(cross(v0, vy)))
対応するヨー回転は次のとおりです。
qy = { cos(yaw/2), up * sin(yaw/2) }
ここで、「上」は上方向の単位ベクトルであり、ヨー回転の軸とも呼ばれます。これを上記のqp=qy ^ -1 * qcに接続して、ピッチクォータニオンqpを取得します。最後に、qpから次のようにピッチ角を取得します。
pitch = 2*asin(Dot(right, [qp[1], qp[2], qp[3]]))
ここで、「右」は正しい方向の単位ベクトルであり、ピッチ回転の軸とも呼ばれます。
私が言ったように、カメラにもロールがあると事態はより複雑になりますが、一般的な戦略は同じです。カメラの回転を回転コンポーネントの積として定式化し、必要なコンポーネント(この場合はピッチ)を分離します。たとえば、「ピッチ」を定義するために使用するオイラー系列が一般的なヨーピッチロールシーケンスである場合、qcを次のように定義します。
qc = qr * qp * qy
変数qxを、ピッチとロールの回転を組み合わせたものとして定義できます。
qx = qr * qp
これで、qcを次のように書くことができます。
qc = qx * qy
上記でqpを解決するために使用した手順をたどることにより、この形式でqxを解決する方法をすでに知っています。qxの定義を並べ替えると、次のようになります。
qp = qr^-1 * qx
qxを解いたばかりなので、ピッチ回転qpを解くには、ロールqrだけが必要です。以前と同じように、ベクトルを使用して構築できます。vcを再び順方向ベクトルとします。ロールは、このベクトルを中心とした回転になります。vuをカメラのアップベクトル(ワールド座標)とし、vu0をゼロロールのカメラのアップベクトルとします。グローバルアップベクトルをvcに垂直な平面に射影し、正規化することでvu0を構築できます。ロール回転qrは、vu0からvuへの回転です。この回転の軸は前方ベクトルvcです。ロール角は
roll = asin(Dot(vc, cross(vu0, vu)))
対応するクォータニオンは次のとおりです。
qr = { cos(roll/2), forward * sin(roll/2) }
ここで、「前方」はロール回転の軸です。
ピッチは完全な回転の 1 つのコンポーネントにすぎないため、回転についてそのように考えたい場合は、可能であればオイラー角を使用して、ピッチを個別に保存することをお勧めします。
動きを制限する必要があるときにオイラー角に変換して元に戻すだけでは、うまく機能しない可能性があります。制限を超えたかどうか、およびどの方向にあるかを確認するには、最後のフレーム (ある場合) を覚えておく必要があるためです。
私が見ているように、クォータニオンの主なポイントは、そのような制限を気にする必要がないということです。すべてが特異点なしで機能します。なぜピッチを制限したいのですか?
カメラ回転クォータニオンは、次のように定義できます。
vector A = [x, y, z]
Q.x = A.x * sin(theta/2)
Q.y = A.y * sin(theta/2)
Q.z = A.z * sin(theta/2)
Q.w = cos(theta/2)
ここで、Aは位置、シータはカメラを回転させたい角度です(ピッチを調整します)。
したがって、クォータニオンを使用できる場合は、回転角に実際の角度をプラス/マイナスしても問題がないかどうかを毎回確認することで、ピッチ角を制限できます。
制限を次のように設定すると、変換を回避できると思います
+cos(supLim/2) < (Q.w + P.w) < -cos(infLim/2)
コサインは連続関数であるため、これは機能するはずです。
実際に使用しているコードを投稿できれば、もう少しお手伝いできます。
私はパーティーに少し遅れるかもしれませんが、これが私がそれを解決した方法です:
// "Up" = local vector -> rotation * Vector3.UnitY
// "Forward" = local vector -> rotation * Vector3.UnitZ
// "Right" = local vector -> rotation * Vector3.UnitX
public void Rotate(Vector3 axis, float angle)
{
if (LerpRotation)
{
RotationTarget *= Quaternion.FromAxisAngle(axis, angle);
}
else
{
Rotation *= Quaternion.FromAxisAngle(axis, angle);
}
//Locking the Pitch in 180°
float a = Vector3.CalculateAngle(Vector3.UnitY, Up);
float sign = Math.Sign(Forward.Y);
float delta = (float)Math.PI / 2 - a;
if(delta < 0)
Rotation *= Quaternion.FromAxisAngle(Right, delta * sign);
}