Unity3D数学库的几个关键点

Unity3D中的数学库和OpenGL数学库的对比:

选取的数学库: 
我自己实现的mathlib,标准的opengl右手系实现
D3D xnamath,按照D3D左手系标准

1) 局部到全局的矩阵变换:

Direct3D : ChildMatrix  * ParentMatrix  * ..... * AncestorMatrix)
OpenGL :  (AncestorMarix * ..... * ParentMatrix * ChildMatrix) 
Unity3D : (AncestorMarix * ..... * ParentMatrix * ChildMatrix)   

结论:unity3D的局部向全局的变换结合方式是和opengl相同的
      四元数的变换结合方式同矩阵

2) 顶点定义的手向性(winding):

Direct3D :  左手系/CW
OpenGL :    右手系/CCW
Unity3D :   左手系/CW 

结论:unity3D的顶点定义的方向是左手定则

3) Matrix4x4的欧拉角结合性是:

 [Yaw * Pitch * Roll ]

4) 矩阵变换顶点,unity3D中不要使用*操作符(该操作符用于变换vec4),而是用TransformPoint函数,否则结果不正确,切记!!

5) unity3中的Quaternion 的 * 操作符用于变换一个顶点,其作用如下:

public static Vector3 QuatTransformPoint(Quaternion qat,Vector3 inV)
{
            Vector3 retVec = inV;
            Vector3 vMltA = new Vector3(2.0f * qat.w, 2.0f * qat.w, 2.0f * qat.w);
            Vector3 vMltB = new Vector3(2.0f, 2.0f, 2.0f);

            Vector3 tmpV = new Vector3(qat.x, qat.y, qat.z);

            Vector3 tmpvCP = Vector3.Cross(tmpV, inV);
            Vector3 tmpvcpCP = Vector3.Cross(tmpV, tmpvCP);

            tmpvCP.x *= vMltA.x; tmpvCP.y *= vMltA.y; tmpvCP.z *= vMltA.z;

            tmpvcpCP.x *= vMltB.x; tmpvcpCP.y *= vMltB.y; tmpvcpCP.z *= vMltB.z;

            retVec += tmpvCP + tmpvcpCP;

            return retVec;
}

6) mathlib中的quat.transformPoint和unity3D中的quat * vec3的结果一致,但是欧拉结合的数值有不一样,需要了解研究结果如何

7) 测试 unity3D 中quaternion 相乘后与mathlib中quat相乘后的结果

//测试 quaternion的yaw * pitch结合
//my mathlib
yaw.set(vec3(0, 1, 0), 31.5F);
pitch.set(vec3(1, 0, 0), 21.5F);
qret = yaw * pitch;
vret = qret.transformPoint(vec3(100,200,300));

//unity3D
Quaternion yaw = Quaternion.Euler(0, 31.5F, 0);
Quaternion pitch = Quaternion.Euler(21.5F, 0, 0);
quat = yaw * pitch;
ret = quat * new Vector3(100, 200, 300);

上面代码测试结果一样,没问题

//测试 quaternion的yaw * pitch * roll结合
//my mathlib
yaw.set(vec3(0, 1, 0), 31.5F);
pitch.set(vec3(1, 0, 0), 21.5F);
qret = yaw * pitch * roll;
vret = qret.transformPoint(vec3(100,200,300));

//unity3D
Quaternion yaw = Quaternion.Euler(0, 31.5F, 0);
Quaternion pitch = Quaternion.Euler(21.5F, 0, 0);
Quaternion roll = Quaternion.Euler(0, 0, 111.1F);

quat = yaw * pitch * roll;
ret = quat * new Vector3(100, 200, 300);
Helper.PrintVector3(ret);

//使用欧拉直接生成quaternion看看其结合是否是ypr方式
 quat = Quaternion.Euler(21.5F, 31.5F, 111.1F);
 ret = quat * new Vector3(100, 200, 300);
 Helper.PrintVector3(ret);

上面代码输出结果不一样了

结论:

1 我的yaw * pitch * roll结果和unity3D的不一致,但是yaw * pitch一致,说明*有问题??
     
2. unity3D的matrix和quaternion使用欧拉角输入时其结合性是: yaw * pitch * roll方式

真的是我的四元数的相乘操作有问题:

/*
    quat operator*(const quat &q) const {
        quat ret;
                //原来的错误
                // x * q.x 错误,要改成x * q.w就正确了!!
        ret.x = w * q.x + x * q.x + y * q.z - z * q.y;
        ret.y = w * q.y + y * q.w + z * q.x - x * q.z;
        ret.z = w * q.z + z * q.w + x * q.y - y * q.x;
        ret.w = w * q.w - x * q.x - y * q.y - z * q.z;
        return ret;
    }
*/

quat operator*(const quat &q) const {
        quat ret;
        ret.x = w * q.x + x * q.w + y * q.z - z * q.y;//修正后正确了
        ret.y = w * q.y + y * q.w + z * q.x - x * q.z;
        ret.z = w * q.z + z * q.w + x * q.y - y * q.x;
        ret.w = w * q.w - x * q.x - y * q.y - z * q.z;
        return ret;
}

8) Unity3D中的Plane定义是ax+by+cz+d = 0 方式还是ax+by+cz = d方式(我的数学库使用这种方式)

public static Plane MakeMyPlane(Vector3 a,Vector3 b,Vector3 c)
{
            Plane ret = new Plane();
            Vector3 diff1 = b - a;
            Vector3 diff2 = c - a;
            Vector3 normal = Vector3.Cross(diff1, diff2);
            normal.Normalize();
            float d = -(Vector3.Dot(normal,a));
            ret.normal = normal;
            ret.distance = d;
            return ret;
}
//测试代码
Plane plane = new Plane(new Vector3(1, 2, 3),
                        new Vector3(10, 2, 3), 
                        new Vector3(1, 20, 3));

Helper.PrintPlane(plane);

Plane plane2 = Helper.MakeMyPlane(new Vector3(1, 2, 3),
                                  new Vector3(10, 2, 3), 
                                  new Vector3(1, 20, 3));
Helper.PrintPlane(plane2);

//结果一样
结论: unity3d中平面的定义方式是: ax+by+cz+d = 0方式

通过上述这些代码,了解了unity3d中的顶点定义的手向性,矩阵四元数的结合方向,欧拉角的结合方向以及平面定义的公式,这样就了解了整个数学体系结构,有利于将Doom3 MD5骨骼动画在Unity3D中实现,毕竟骨骼动画涉及大量的数学变换操作。

下一个主题,了解一下Unity3D底层的渲染系统,例如GL,如何处理D3D/GL的投影以及裁剪,Graphics以及Mesh他们之间的关系

你可能感兴趣的:(Unity3D数学库的几个关键点)