unigine 三维旋转矩阵(mat3,quat) scale translate 变换矩阵(mat4) 和forward up right 关系 normal binormal tangent

28 篇文章 6 订阅
10 篇文章 1 订阅

三维中旋转矩阵可以用mat3或quat表示。 两者可以转换。在数学上,旋转和反射对应线代中的正交变换。每行(或每列)相互正交的矩阵且每行(或每列)模长为1,叫正交矩阵


unigine中是右手坐标系。顺时针方向的角度为正,逆时针方向角度为负
知道y,z 求x:cross(y,z);(trick:将所求的轴朝向自己的反方向,顺时针代入已知的轴,只是在求轴的时候从这个视角,理解旋转的时候需要让轴朝向自己)
知道x,z 求y:cross(z,x);
知道x,y 求z:cross(x,y);
求两向量夹角:getAngle(Vec3 startvector,Vec3 endvector,vec3 up);

quat q = m_node->getWorldRotation();
mat3 m3 = q.getMat3();

//mat3 和quat之间的转换
quat q;
q.getMat3();
mat3 mat;
mat.getQuat();
quat(mat);
mat3(q);

mat4 表示旋转(rotate)和位移(translate)和缩放(scale)  
mat4 乘法表示变换的结合。

单位旋转矩阵刚好是单位的  -right -forward up 的组合,(也相当于x,y,z 的组合)。unigine中一般right 为-x,forward为-y, up为z,这个可能只是与导入的习惯有关系。

Log::message("%s\n",Engine::get()->runWorldFunction(Variable("typeinfo"), Variable(m3.getColumn(0))).getString());
Log::message("%s\n", Engine::get()->runWorldFunction(Variable("typeinfo"), Variable(m3.getColumn(1))).getString());
Log::message("%s\n", Engine::get()->runWorldFunction(Variable("typeinfo"), Variable(m3.getColumn(2))).getString());
Log::message("%s\n", Engine::get()->runWorldFunction(Variable("typeinfo"), Variable(m_node->getWorldTransform())).getString());
Log::message("%s\n", Engine::get()->runWorldFunction(Variable("typeinfo"), Variable(m_node->getWorldForward())).getString());
Log::message("%s\n", Engine::get()->runWorldFunction(Variable("typeinfo"), Variable(m_node->getWorldRight())).getString());
Log::message("%s\n", Engine::get()->runWorldFunction(Variable("typeinfo"), Variable(m_node->getWorldUp())).getString());

结果:

 

在2.7之后无forward left right之类的了,不过可以通过tanget normal binormal来获取前方。(还有一种更粗暴的方式是直接从矩阵中获取mat.getColumn3(0),mat.getColumn3(1),mat.getColumn3(2),分别对应tangent,binormal,normal)
对于单位矩阵 三者的值分别为tangent:(1,0,0)(红色,朝外为正),binormal(0,1,0)(绿色,朝外为正),normal(0,0,1)(蓝色,朝外为正)。(tangent,binormal,normal分别与 x,y,z对应)

近期发现通过binormal的方式获取的轴向有问题,binormal偶尔会有突变,这个突变在获取角度时产生很严重的问题,而通过getColumn要稳定很多。原因暂时不明。(cudir为-m_node->getWorldRotation().getBinormal() 。)

        float ifps = Game::get()->getIFps();
	Vec3 forward = -Vec3(m_node->getWorldRotation().getBinormal());// -m_node->getWorldTransform().getColumn3(1); //current forward in world coordinate  //problem code
	Vec3 up = -m_node->getWorldTransform().getColumn3(2); //current rotate axis in world coordinate
	Mat4 mat = m_node->getWorldTransform();
	
	Vec3 pos = mat.getTranslate();
	Visualizer::get()->renderLine3D(pos, pos + mat.getColumn3(0) * 1000, vec4(1, 0, 0, 1));
	Visualizer::get()->renderLine3D(pos, pos + Vec3(m_node->getWorldRotation().getBinormal()) * 1000, vec4(0, 1, 0, 1));
	Visualizer::get()->renderLine3D(pos, pos + mat.getColumn3(2) * 1000, vec4(0, 0, 1, 1));
	angleCu = getAngle(vec3(forward), vec3(1, 0, 0), vec3(0, 0, 1));
	if (fabs(angleCu - anglePre) > 100)
	{
		Log::message("aaaaaaaaaaaa");
		int tt = 0;
	}
	Log::message("angle:pre %f,cu %f  cudir(%f,%f,%f)\n", anglePre, angleCu, forward.x, forward.y, forward.z);
	anglePre = angleCu;
	

	Mat4 newTrans;// = translate(forward*ifps);
	Mat4 rot = Mat4(rotate(up, ifps * 30));
	Mat4 roto = Mat4(rotate(mat.getRotate()));
	Mat4 transo = translate(mat.getTranslate());
	mat = transo*newTrans*roto*rot;
	m_node->setWorldTransform(mat);

变换矩阵描述:

缩放是存放在旋转的部分的,直接将缩放乘到旋转矩阵部分,这个乘法不是矩阵乘向量,而是将元素直接按某种方式乘进去  
将scale设置为(10,10,10)

将scale设置为(10,100,1000);

关于矩阵相乘的顺序及组装。看如下案例
Mat4 mat = translate(pos)*Mat4(rotate(rot))*Mat4(scale(s));
Vec3 p = mat.getTranslate();
quat r = mat.getRotate();
vec3 sc = mat.getScale();
//pos = {x=4.6566128730540935e-10 y=0.032559752464131522 z=10.953591346685917 ...}
//p   = {x=4.6566128730540935e-10 y=0.032559752464131522 z=10.953591346685917 ...}
//s = {x=0.00999999978 y=0.00999999512 z=0.00999999698 ...}
//sc = {x=0.00999999978 y=0.00999999512 z=0.00999999698 ...}
//rot = {x=-0.00148625416 y=-2.32830893e-10 z=0.000000000 ...}
//r = {x=-0.00148625416 y=-2.32830893e-10 z=5.42101732e-20 ...}
c++和C语言一样,表达式都是从左往右计算的。矩阵的组装应该按照translate->rotate->scale 的顺序来进行。否则会导致不一样的结果。
同理矩阵相乘其顺序也是很重要的,矩阵相乘描述的是变换的叠加。上面的组装其实也是矩阵相乘的结果。
类似于这两种变换: “绕x转90度再绕y转90度” 与 “先绕y转90度再绕x转90度”,这两种转换得到的是不一样的结果。矩阵相乘,顺序也是极为重要的!!!矩阵乘法不满足交换律

我平时使用的一种理解方式是:矩阵描述的是一个变换的动作(变换量),比如向左走10米作为一个动作(变换量), 描述为TL = translate(0,10,0) 。 假设物体A 正前方为x(1,0,0),左边为y(0,1,0),上方为z(0,0,1),在A经过变换T0后 ,其前左上依然是(x,y,z)(这里描述一定是使用物体本身的坐标系),如果此时要让A再向左走10米,应当以A当前的矩阵乘上新的变化,就能得到A新的位置了,即 Tnew = T0*TL,这样就得到了当前位置往左走10米的变换了。即一直以变换物体当前位置作为基础,因为物体本身的坐标系是与物体固定的,不变动的。
求变换量的时候最关键的一点就是坐标系了:你必须要使用物体本身的坐标系!!!!因为我们需要的是一个相对的变化量(变换量也必须是一个相对值)。如物体前方为(1,0,0),那他经过各种变换后,他的前方在他的坐标系下还是(1,0,0) ,千万不要与世界坐标系的搞混。
至此再回过去理解矩阵的组装应该就很方便了。而A所描述的T0又可以理解为相对于(0,0,0)且没有旋转变换和缩放变换的 一个总的变换。而那个(0,0,0),就是世界坐标系了。

当然上面的变换量也可以是一个复合变化,比如T1(你先向你的左边走10米再向你的左边转90度) 或者 T2(你先向你的左边转90度再向你的左边走10米)。这两种变换量表示的是不一样的。在T2中明显已经先左转了,左转后的左边10米是左转前的后面的10米。
对于缩放,有点尴尬,缩放会影响平移。所以操作的时候可能需要将缩放抽出,然后再在最后乘上缩放变换。

//we have no scale here 
float ifps = Game::get()->getIFps(); 
Mat4 mat = m_node->getWorldTransform();
Mat4 newMat =  translate(Vec3(0,-1,0)*ifps);//Vec3(0,-1,0) is defined forward of the m_node by me
Mat4 rot = Mat4((rotate(quat(vec3(0, 0, 1), ifps * 10))));//vec3(0, 0, 1) is defined up  of the m_node by me
newMat = newMat*rot;
mat = mat*newMat;
m_node->setWorldTransform(mat);

另外一种求变换的方式是使用全局的变换矩阵来求矩阵,需要将平移和旋转全部拆分开来。
假设物体当前的变换拆分开后编程位移变换矩阵T0 ,旋转变换矩阵R0,缩放矩阵S0.
当前相对于世界的前方的向量*速度*ifps 得到这一帧的全局位移变换T1.旋转矩阵为R1 = rotate(axis,angle*ifps) (axis为当前位置轴向相对于世界坐标系的描述,angle为角速度),缩放不变
那当前位置相对于世界的坐标为T0*T1*R0*R1*S0; 或者T1*T0*R0*R1*S0。永远要记住translate->rotate->scale的顺序。

//have no scale
float ifps = Game::get()->getIFps();
Vec3 forward = -m_node->getWorldTransform().getColumn3(1); //current forward in world coordinate ,it's model correspond ,you should check in you editor,or render it out,to find where is the forward
Vec3 up = -m_node->getWorldTransform().getColumn3(2); //current rotate axis in world coordinate
Mat4 mat = m_node->getWorldTransform();
Mat4 newTrans =  translate(forward*ifps);
Mat4 rot = Mat4(rotate(up, ifps * 10));
Mat4 roto = Mat4(rotate(mat.getRotate()));
Mat4 transo = translate(mat.getTranslate());
mat = transo*newTrans*roto*rot;
m_node->setWorldTransform(mat);

在没有旋转的矩阵中,平移变换的相乘或者绕坐标系同轴旋转变换的相乘在顺序上没有要求容易让人误解!!!!(至于为什么,可以理解下)

向量与旋转的左乘右乘:(quat 同mat3 两者是等价的且可相互替换的)
vec3 v = vec3(1, 0, 0);
vec3 v1 = v*quat(vec3(0, 0, 1), 30); //v1 = {x= 0.866025388 y= -0.500000000 z= 0.000000000 ...}
vec3 v2 = quat(vec3(0, 0, 1), 30)*v; //v2 = {x= 0.866025388 y= 0.500000000 z= 0.000000000 ...}
左乘右乘得出来的结果不一样。按上面的结果来理解,v2表示的是顺时针方向旋转。v1表示逆时针方向旋转。

 

关于mat4中setRotate(),setTranslate(),setRotateX(),setRotateY(),setRotateZ(),这些函数都会先将mat4中数据全部清空,然后再重新设置,也就是使用setTranslate() ,原来拥有的旋转会被清空;使用setRotate(),原来拥有的translate 会被置为0。(这些容易让人误解)
不过Mat4拥有12个构造函数,可以选择合适的构造函数来使用。或者用过乘法来组装

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值