開発環境 Microsoft Visual C# 2010 Express (SP1)
実行環境 Microsoft Windows XP Home Edition (SP3)
プロジェクトの種類 空のプロジェクト
プロジェクト名 Quaternion

参考

Program.cs
// Quaternion2 球面線形補間の実験2
using System;
using Microsoft.Xna.Framework; // .NET参照
 
class Program
{
    static void Main()
    {
        float rad = MathHelper.ToRadians(120);
        float x = (float)Math.Cos(rad);
        float y = (float)Math.Sin(rad);
 
        Vector3 v1 = new Vector3(1, 0, 0);
        Vector3 v2 = new Vector3(x, y, 0);
        Quaternion q1 = new Quaternion(v1, 0);
        Quaternion q2 = new Quaternion(v2, 0);
 
        Print(v1);
        Print(v2);
        Print(q1);
        Print(q2);
 
        int div = 4;
        float dot = Vector3.Dot(v1, v2);
        Console.WriteLine(string.Format("dot={0:f2}", dot));
 
        Console.WriteLine("Quaternion.Slerp");
        for (int n = 0; n <= div; n++)
        {
            float t = n / (float)div;
            Quaternion q = Quaternion.Slerp(q1, q2, t);
            float lat = MathHelper.ToDegrees((float)Math.Asin(q.Y));
            Console.Write(string.Format("{0} lat={1:f1} / ", n, lat));
            Print(q);
        }
 
        Console.WriteLine("Slerp(Quaternion)");
        for (int n = 0; n <= div; n++)
        {
            float t = n / (float)div;
            Quaternion q = Slerp(q1, q2, t);
            float lat = MathHelper.ToDegrees((float)Math.Asin(q.Y));
            Console.Write(string.Format("{0} lat={1:f1} / ", n, lat));
            Print(q);
        }
 
        Console.WriteLine("Slerp(Vector3)");
        for (int n = 0; n <= div; n++)
        {
            float t = n / (float)div;
            Vector3 v = Slerp(v1, v2, t);
            float lat = MathHelper.ToDegrees((float)Math.Asin(v.Y));
            Console.Write(string.Format("{0} lat={1:f1} / ", n, lat));
            Print(v);
        }
 
        Console.ReadLine();
    }
 
    static void Print(Vector3 v)
    {
        Console.WriteLine(string.Format("x={0:f2} y={1:f2} z={2:f2} len={3:f2}",
            v.X, v.Y, v.Z, v.Length()));
    }
 
    static void Print(Quaternion q)
    {
        Console.WriteLine(string.Format("x={0:f2} y={1:f2} z={2:f2} w={3:f2} len={4:f2}",
            q.X, q.Y, q.Z, q.W, q.Length()));
    }
 
    static Vector3 Slerp(Vector3 value1, Vector3 value2, float amount)
    {
        value1.Normalize();
        value2.Normalize();
        float dot = Vector3.Dot(value1, value2); // cosθ
        float angle = (float)Math.Acos(dot); // 2ベクトル間の角度
        float Ps = (float)Math.Sin(angle * (1 - amount));
        float Pe = (float)Math.Sin(angle * amount);
        Vector3 v = (Ps * value1 + Pe * value2) / (float)Math.Sin(angle);
        v.Normalize();
        return v;
    }
 
    static Quaternion Slerp(Quaternion value1, Quaternion value2, float amount)
    {
        value1.Normalize();
        value2.Normalize();
        float dot = Quaternion.Dot(value1, value2); // cosθ
        float angle = (float)Math.Acos(dot); // 2ベクトル間の角度
        float sinTheta = (float)Math.Sin(angle);
        float Ps = (float)Math.Sin(angle * (1 - amount)) / sinTheta;
        float Pe = (float)Math.Sin(angle * amount) / sinTheta;
        Quaternion q = value1 * Ps + value2 * Pe;
        q.Normalize();
        return q;
    }
}
 

出力
x=1.00 y=0.00 z=0.00 len=1.00
x=-0.50 y=0.87 z=0.00 len=1.00
x=1.00 y=0.00 z=0.00 w=0.00 len=1.00
x=-0.50 y=0.87 z=0.00 w=0.00 len=1.00
dot=-0.50
Quaternion.Slerp
0 lat=0.0 / x=1.00 y=0.00 z=0.00 w=0.00 len=1.00
1 lat=-15.0 / x=0.97 y=-0.26 z=0.00 w=0.00 len=1.00
2 lat=-30.0 / x=0.87 y=-0.50 z=0.00 w=0.00 len=1.00
3 lat=-45.0 / x=0.71 y=-0.71 z=0.00 w=0.00 len=1.00
4 lat=-60.0 / x=0.50 y=-0.87 z=0.00 w=0.00 len=1.00
Slerp(Quaternion)
0 lat=0.0 / x=1.00 y=0.00 z=0.00 w=0.00 len=1.00
1 lat=30.0 / x=0.87 y=0.50 z=0.00 w=0.00 len=1.00
2 lat=60.0 / x=0.50 y=0.87 z=0.00 w=0.00 len=1.00
3 lat=90.0 / x=0.00 y=1.00 z=0.00 w=0.00 len=1.00
4 lat=60.0 / x=-0.50 y=0.87 z=0.00 w=0.00 len=1.00
Slerp(Vector3)
0 lat=0.0 / x=1.00 y=0.00 z=0.00 len=1.00
1 lat=30.0 / x=0.87 y=0.50 z=0.00 len=1.00
2 lat=60.0 / x=0.50 y=0.87 z=0.00 len=1.00
3 lat=90.0 / x=0.00 y=1.00 z=0.00 len=1.00
4 lat=60.0 / x=-0.50 y=0.87 z=0.00 len=1.00

考察
クォータニオンの内積は|q1||q2|cosθであり、θが+-90°を超えるとマイナスになる。
つまりベクトルの地球を突き抜けた後ろ側が近くなる。
円周でたとえるなら、0°と120°のベクトルでは120°の後ろ側-60°との距離が最短になる。
それがQuaternion.Slerpの仕様なのかお節介なのかは分からない。
Slerp関数を用意するなら球面線形補間はVector3で済む。
最終更新:2012年12月15日 08:57