Unity 四元数代码实现(自己实现)

using UnityEngine;

namespace MyMath
{
    [System.Serializable]
    public class Quat
    {
        #region 字段

        public float x;
        public float y;
        public float z;
        public float w;

        public static readonly Quat Identity = new Quat(0f, 0f, 0f, 1f);

        #endregion

        #region 属性

        public float Length => Mathf.Sqrt(Dot(this, this));

        public Quat Normalized
        {
            get
            {
                float l = 1 / Length;
                return this * l;
            }
        }

        public Quat Conjugate => new Quat(-x, -y, -z, w);

        public Quat Inverse
        {
            get
            {
                float l = 1 / Length;
                return Conjugate * l;
            }
        }

        public Mat3x3 ToMat3x3
        {
            get
            {
                float num1 = 2 * x;
                float num2 = 2 * y;
                float num3 = 2 * z;
                float num4 = x * num1;
                float num5 = y * num2;
                float num6 = z * num3;
                float num7 = x * num2;
                float num8 = x * num3;
                float num9 = y * num3;
                float num10 = w * num1;
                float num11 = w * num2;
                float num12 = w * num3;

                return new Mat3x3(
                    1 - num5 - num6, num7 + num12, num8 - num11,
                    num7 - num12, 1 - num4 - num6, num9 + num10,
                    num8 + num11, num9 - num10, 1 - num4 - num5);
            }
        }

        public Vec3 ToEuler
        {
            get
            {
                float sp = -2.0f * (y * z - w * x);

                var (p, h, b) = (0f, 0f, 0f);
                if (Mathf.Abs(sp) > 0.9999f)
                {
                    p = Mathf.PI / 2 * sp;
                    h = Mathf.Atan2(-x * z + w * y, 0.5f - y * y - z * z);
                    b = 0f;
                }
                else
                {
                    p = Mathf.Asin(sp);
                    h = Mathf.Atan2(x * z + w * y, 0.5f - x * x - y * y);
                    b = Mathf.Atan2(x * y + w * z, 0.5f - x * x - z * z);
                }

                return new Vec3(p * Mathf.Rad2Deg, h * Mathf.Rad2Deg, b * Mathf.Rad2Deg);
            }
        }

        public Quaternion ToQuaternion => new Quaternion(x, y, z, w);

        #endregion

        #region 构造函数
        
        private Quat()
        {
            x = 0f;
            y = 0f;
            z = 0f;
            w = 1f;
        }

        public Quat(float x, float y, float z, float w)
        {
            this.x = x;
            this.y = y;
            this.z = z;
            this.w = w;
        }

        #endregion

        #region 运算符

        public static Quat operator -(Quat q1)
        {
            return new Quat(-q1.x, -q1.y, -q1.z, -q1.w);
        }

        public static Quat operator +(Quat q1, Quat q2)
        {
            return new Quat(q1.x + q2.x, q1.y + q2.y, q1.z + q2.z, q1.w + q2.w);
        }

        public static Quat operator -(Quat q1, Quat q2)
        {
            return new Quat(q1.x - q2.x, q1.y - q2.y, q1.z - q2.z, q1.w - q2.w);
        }

        public static Quat operator *(float k, Quat q)
        {
            return new Quat(q.x * k, q.y * k, q.z * k, q.w * k);
        }
        
        public static Quat operator *(Quat q, float k)
        {
            return new Quat(q.x * k, q.y * k, q.z * k, q.w * k);
        }
        
        public static Quat operator /(float k, Quat q)
        {
            return new Quat(q.x / k, q.y / k, q.z / k, q.w / k);
        }
        
        public static Quat operator /(Quat q, float k)
        {
            return new Quat(q.x / k, q.y / k, q.z / k, q.w / k);
        }
        
        //旋转的连接来设置矢量需要左乘
        public static Quat operator *(Quat q1, Quat q2)
        {
            return new Quat
            {
                x = q1.w * q2.x + q2.w * q1.x + q1.y * q2.z - q2.y * q1.z,
                y = q1.w * q2.y + q2.w * q1.y + q1.z * q2.x - q2.z * q1.x,
                z = q1.w * q2.z + q2.w * q1.z + q1.x * q2.y - q1.y * q2.x,
                w = q1.w * q2.w - q1.x * q2.x - q1.y * q2.y - q1.z * q2.z,
            };
        }
        
        //还有一种写法,通过将四元数转换为矩阵,矩阵与矢量相乘,来旋转矢量。(Unity使用的就是这种方法)
        public static Vec3 operator *(Vec3 v, Quat q)
        {
            Quat q1 = new Quat(v.x, v.y, v.z, 0);
            Quat q2 = q * q1 * q.Conjugate;

            return new Vec3(q2.x, q2.y, q2.z);
        }

        public static bool operator ==(Quat q1, Quat q2)
        {
            return Dot(q1, q2) > 0.9999f;
        }

        public static bool operator !=(Quat q1, Quat q2)
        {
            return !(q1 == q2);
        }

        public static implicit operator Quat(Quaternion q)
        {
            return new Quat(q.x, q.y, q.z, q.w);
        }

        #endregion

        #region 方法

        public static float Dot(Quat q1, Quat q2)
        {
            return q1.x * q2.x + q1.y * q2.y + q1.z * q2.z + q1.w * q2.w;
        }

        public static Quat FromAxisAngle(Vec3 axis, float angle)
        {
            angle = angle * 0.5f * Mathf.Deg2Rad;
            float sin = Mathf.Sin(angle);
            float cos = Mathf.Cos(angle);

            return new Quat(axis.x * sin, axis.y * sin, axis.z * sin, cos);
        }

        public static float Angle(Quat q1, Quat q2)
        {
            float p = Dot(q1, q2);

            if (p > 0.9999f) return 0f;

            return 2 * Mathf.Acos(Mathf.Abs(Mathf.Clamp(p, -1f, 1f))) * Mathf.Rad2Deg;
        }

        public static Quat Exp(Quat q, float t)
        {
            //提取半角
            float alpha = Mathf.Acos(q.w);

            float newAlpha = t * alpha;

            float mult = Mathf.Sin(newAlpha) / Mathf.Sin(alpha);

            return new Quat(q.x * mult, q.y * mult, q.z * mult, Mathf.Cos(newAlpha));
        }
        
        //Lerp 和 Slerp 效果相同,但 Slerp 效率更快,所以我们一般使用 Slerp
        public static Quat Lerp(Quat q1, Quat q2, float t)
        {
            //线性插值
            return Exp(q2 * q1.Conjugate, t) * q1;
        }

        public static Quat Slerp(Quat q1, Quat q2, float t)
        {
            float p = Dot(q1, q2);

            if (p < 0)
            {
                q2 = -q2;
                p = -p;
            }

            if (p > 0.9999f) return q1;

            float red = Mathf.Acos(p) * 2;
            float sin = Mathf.Sin(red);

            return Mathf.Sin((1 - t) * red) / sin * q1 + Mathf.Sin(t * red) / sin * q2;
        }

        public static Quat FormEuler(float x, float y, float z)
        {
            x = 0.5f * x * Mathf.Deg2Rad;
            y = 0.5f * y * Mathf.Deg2Rad;
            z = 0.5f * z * Mathf.Deg2Rad;
            
            float ch = Mathf.Cos(y);
            float sh = Mathf.Sin(y);
            float cp = Mathf.Cos(x);
            float sp = Mathf.Sin(x);
            float cb = Mathf.Cos(z);
            float sb = Mathf.Sin(z);
            
            //由于四元数从右向左乘,所以顺序是 YXZ
            return new Quat(
                ch * sp * cb + sh * cp * sb,
                sh * cp * cb - ch * sp * sb,
                ch * cp * sb - sh * sp * cb,
                ch * cp * cb + sh * sp * sb);
        }

        public static Quat FormToRotation(Vec3 fromDir, Vec3 toDir)
        {
            //保证传入的方向是归一化的
            float red = Mathf.Acos(Mathf.Clamp(Vec3.Dot(fromDir, toDir), -1f, 1f)) * 0.5f;
            Vec3 nor = Vec3.Cross(fromDir, toDir);
            float sin = Mathf.Sin(red);
            float cos = Mathf.Cos(red);

            return new Quat(nor.x * sin, nor.y * sin, nor.z * sin, cos);
        }

        public static Quat FormEuler(Vec3 v) => FormEuler(v.x, v.y, v.z);
        
        public bool Equals(Quat other)
        {
            return x.Equals(other.x) && y.Equals(other.y) && z.Equals(other.z) && w.Equals(other.w);
        }

        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            if (ReferenceEquals(this, obj)) return true;
            if (obj.GetType() != this.GetType()) return false;
            return Equals((Quat) obj);
        }

        public override int GetHashCode()
        {
            unchecked
            {
                var hashCode = x.GetHashCode();
                hashCode = (hashCode * 397) ^ y.GetHashCode();
                hashCode = (hashCode * 397) ^ z.GetHashCode();
                hashCode = (hashCode * 397) ^ w.GetHashCode();
                return hashCode;
            }
        }

        public override string ToString()
        {
            return $"<{x} {y} {z} {w}>";
        }

        #endregion
    }
}

值得注意的是四元数使用的是左乘,Unity中Transition中rotation使用的右乘。

代码(Github) -> Unity中数学库的实现
我的数学库实现代码中,有对矢量矩阵四元数等常见的数学实现。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值