我们知道,在opengl中,如果模型中点不在原点,先平移后旋转会导致模型的位移发生变化。但在项目中遇到这么一个问题,如果不知道模型中点,或者知道模型中点一定不在原点,但我们也只需要模型方向,那么我们直接将模型旋转会得到我们想要的方向吗?即如果先平移后旋转能不能得到想要的方向?
这个地方我疑惑了很久,因为轴只有方向没有位置,默认的旋转会朝着原点计算,当时的想法是不能,想着可能整个模型会绕着原点旋转,相当于位移了,而本身形状不会变化,但结果显然是错的。
首先做了一个demo,一个中心在原点的正方形(灰色)先绕z轴旋转30度,再沿x轴平移0.5个单位(红色),另一个正方形则相反(蓝色):
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//原图
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glBegin(GL_QUADS);
glColor3f(0.5,0.0,0.0);
glVertex3f(-0.1,0.1,0.0);
glVertex3f(0.1,0.1,0.0);
glVertex3f(0.1,-0.1,0);
glVertex3f(-0.1,-0.1,0);
glEnd();
//先旋转再平移
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.5,0.0,0.0);
glRotatef(30.0,0.0,0.0,1.0);
glBegin(GL_QUADS);
glColor3f(0.5,0.0,0.0);
glVertex3f(-0.1,0.1,0.0);
glVertex3f(0.1,0.1,0.0);
glVertex3f(0.1,-0.1,0);
glVertex3f(-0.1,-0.1,0);
glEnd();
//先平移再旋转
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glRotatef(30.0,0.0,0.0,1.0);
glTranslatef(0.5,0.0,0.0);
glBegin(GL_QUADS);
glColor3f(0.0,0.0,0.5);
glVertex3f(-0.1,0.1,0.0);
glVertex3f(0.1,0.1,0.0);
glVertex3f(0.1,-0.1,0);
glVertex3f(-0.1,-0.1,0);
glEnd();
//坐标轴
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glBegin(GL_LINES);
glColor3f(1.0,1.0,1.0);
glVertex3f(0.0,1.0,0.0);
glVertex3f(0.0,-1.0,0.0);
glVertex3f(1.0,0.0,0.0);
glVertex3f(-1.0,0.0,0.0);
glEnd();
demo结果:
我们可以看到,两种行为的正方形都成功绕着z轴旋转了30度,仅是位移不同。
我们来看看为什么?
在https://jingyan.baidu.com/article/414eccf617a9c66b421f0a5e.html文章中讲解了一个原因,看着很有道理,但是他这个原理起点有点歧义,他是以矩阵乘法的先后顺序来标定模型动作的先后,也就是说,在上面例子中,他认为是红色图形先做了平移,再做了旋转。我查了一些资料,这种看法也有,但主流还是本文这么认为,因为最先与顶点接触的是右边的矩阵: res=TranslateMat*RotateMat*ScaleMat*Pos,所以先后顺序应该从右向左读这个乘法。
好了,那如果先旋转再平移,从数学上怎么解释这个现象呢?
我们设旋转矩阵为R,位移为T,原位置为P,那么先旋转再平移,就可以表示为:res=RP+T; 同理,先平移再旋转结果表达式为:res=R(P+T)=RP+RT;
从表达式可以看出:最终两种结果模型本身都一样地旋转了R,但是先平移的结果位移也绕原点旋转了R。也就是说,如果只考虑模型方向,两种结果并没有差别。
最后再做一个笔记,也是以前理解的一个错误(以前我认为四元数旋转就直接以模型中点旋转了),以此为鉴。旋转有三种表达形式:旋转矩阵,欧拉角和四元数,但这三种仅是在数学上表达不一样,也有各自的优劣,但本质上还是一样的,归根到底,最终都转换到了旋转矩阵和顶点的乘法运算。