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他们之间的关系