opengl学习笔记Ⅴ——磨人的轨迹球真可爱

这回我们继续优化交互。使用轨迹球来使得模型转得更加人性化。

轨迹球可以理解为模型外部套一个球体,在我们鼠标拖动旋转模型时,能够准确地模拟出球体被拖动的感觉。

首先,我在这里放弃了透视投影,考虑透视投影的话太复杂了。(搞不出来,就很烦)

用简单的正交投影来做吧

glMatrixMode(GL_PROJECTION);//设置投影矩阵
glLoadIdentity();
glOrtho(-1500, 1500, -1500, 1500, 1, 10000);
这样,我们可以简单地算出球在窗口中的大小。(这里我的窗口大小为250,1500为正交投影规定的视景体的一半)
work_size = a*(R/1500.0)*250;//a为缩放比例,初始为1

接着我们换算一下鼠标回调函数得到的坐标,把它转化成球心为原点的坐标系(窗口中间)。因为鼠标回馈的坐标是以左上为原点,向右为x,向下为y。所以我们这么转化:

x -= 250;
y -= 250;
y = -y;

这样,可以算出我们点击的点的“坐标”A,这里是针对窗口里显示的球(work_size为半径)的中心的坐标系,朝外为Z正。

r_x1 = x;
r_y1 = y;
r_z1 = sqrt(work_size*work_size - x*x - y*y);

同样,在鼠标移动的回调函数里,我们会得到另外一个点B的坐标(r_x2,r_y2,r_z2)。

计算出OA X OB(叉乘)的值,即为旋转轴,记得要把长度缩放为1。

rx = r_y1*r_z2-r_z1*r_y2;
ry = r_z1*r_x2 - r_x1*r_z2;
rz = r_x1*r_y2 - r_y1*r_x2;
mod = sqrt(rx*rx + ry*ry + rz*rz);
rx /= mod;
ry /= mod;
rz /= mod;

计算旋转角度,l为两点之间的距离。

l = sqrt((r_x1 - r_x2)*(r_x1 - r_x2) + (r_y1 - r_y2)*(r_y1 - r_y2) + (r_z1 - r_z2)*(r_z1 - r_z2));
theta =-2*asin(l/(2.0*work_size));

参考https://www.cnblogs.com/graphics/archive/2012/08/10/2627458.html里的文章,我们可以得到对应的旋转矩阵如下

m2[0] = rx*rx + (1 - rx*rx)*cos(theta);
m2[1] = rx*ry*(1 - cos(theta)) - rz*sin(theta);
m2[2]= rx*rz*(1 - cos(theta)) + ry*sin(theta);
m2[4] = rx*ry*(1 - cos(theta)) + rz*sin(theta);
m2[5] = ry*ry + (1 - ry*ry)*cos(theta);
m2[6] = ry*rz*(1 - cos(theta)) - rx*sin(theta);
m2[8] = rx*rz*(1 - cos(theta)) - ry*sin(theta);
m2[9] = ry*rz*(1 - cos(theta)) + rx*sin(theta);
m2[10] = rz*rz + (1 - rz*rz)*cos(theta);
m2[15] = 1;

注意opengl中,矩阵为4X4,实际上接受的是一个一维数组指针,所以我们定义为m2[16]。因为opengl是一列一列存数据,和我们正常的一行一行的存不一样,所以注意下标的选择哦。

在我们放开鼠标左键之后,把这一次的旋转矩阵作用在m1上,使得m1代表所有历史旋转矩阵的综合作用。

glLoadMatrixd(m2);
glMultMatrixd(m1);
glGetDoublev(GL_MODELVIEW_MATRIX, m1);
glLoadIdentity();
glGetDoublev(GL_MODELVIEW_MATRIX, m2);

这里要注意的是,opengl是右乘,如果我们直接把m2乘进m1,得到的结果会是m1m2 V,我们实际上是希望在完成m1旋转之后进行m2旋转,所以才有了上面的倒来倒去的操作。

同样的,在绘制函数里,我们的绘制顺序也是

glMultMatrixd(m2);
glMultMatrixd(m1);

这样我们的旋转已经基本完成,接下来谈一下画球的问题,因为我们用的是光照模型,所以球画出来会有明暗的问题。但我们实际上不希望球的颜色会随着位置变化,所以我们调用如下函数设置球的自发光颜色,注意,这个光不会像光源一样影响其他物体的颜色。

glMaterialfv(GL_FRONT, GL_EMISSION, ball);

效果还不错。


附:对于在球外部的拖动,我们会希望变为Z轴方向的旋转。这里具体的讲解读者自己思考,我只放下我写的代码

                GLfloat theta,la,lb,lc;
		r_x2 = x;
		r_y2 = y;
		la = sqrt(r_x1*r_x1 + r_y1*r_y1);
		lb = sqrt(r_x2*r_x2 + r_y2*r_y2);
		lc = sqrt((r_x1 - r_x2)*(r_x1 - r_x2) + (r_y1 - r_y2)*(r_y1 - r_y2));
		theta = 180/PI*acos((la*la + lb*lb - lc*lc) / (2 * la*lb));
		//if (theta > 0.1)
		//{
		//	cout << endl;
		//}
		glLoadIdentity();
		if (r_x1*r_y2 - r_y1*r_x2>0)
			glRotated(theta, 0, 0, 1);
		else
			glRotated(theta, 0, 0, -1);
		glGetDoublev(GL_MODELVIEW_MATRIX, m2);




猜你喜欢

转载自blog.csdn.net/qq_38782152/article/details/80292737