易码技术论坛

 找回密码
 加入易码
搜索
查看: 616217|回复: 12

[归档] 使用鼠标自由旋转三维图形的算法和思路

[复制链接]
发表于 2006-11-9 21:42:49 | 显示全部楼层
沙发!很好东西!刚学WINDOWS编程,所以还看不太懂!
 楼主| 发表于 2006-11-9 21:47:34 | 显示全部楼层
会LS,和Windows编程关系不是很大=v=
发表于 2006-11-10 12:29:08 | 显示全部楼层
地板!铺个毯子睡午觉=v=
发表于 2006-12-14 20:06:38 | 显示全部楼层
搂主的分析十分的透彻,对入门着十分的有用
 楼主| 发表于 2006-12-14 21:02:19 | 显示全部楼层
呃...回楼上,这个貌似不怎么适合入门者,这个至少需要读者对立体几何,向量,线性代数以及OpenGL编程有一定的了解。
发表于 2006-12-15 00:02:46 | 显示全部楼层
想起了shooting的图形学作业=v=
发表于 2006-12-16 10:11:17 | 显示全部楼层
请问:
// 计算x, y坐标
v[0] = (float)x * 2.0 - (float)d;
v[1] = (float)d - (float)y * 2.0;
这是怎么计算球面X,Y坐标得??
本人新手,有些不明白?
如果鼠标点击位置在球面以外得话,怎么计算呢?
说得不对,别笑话!
 楼主| 发表于 2006-12-16 11:55:48 | 显示全部楼层
回楼上
v[0]实际上等于 x - d / 2,不过因为后面有单位化,所以避免有除法就乘以2了。
x 是鼠标坐标, d / 2是中心横坐标。
v[1]是同样的道理,不过OpenGL里面y坐标是向上为正,而鼠标坐标是屏幕坐标,向下为正,所以要反一下。
如果鼠标在范围以外,必然算不出z来,可以用0来代替z或者忽略这种情况。
发表于 2006-12-16 16:54:11 | 显示全部楼层
如果我把V[0],V[1]写成
sqrtxy=sqrt((float)x * (float)x + (float)y * (float)y);
v[0]=x*d/sqrtxy;
v[1]=-y*d/sqrtxy;
是否就是超出范围得情况?我发现它很难旋转,呵呵
 楼主| 发表于 2006-12-16 17:14:57 | 显示全部楼层
v[0]、v[1]和x, y就是直接映射关系不需要别的更复杂的运算~
而且这个只是一个粗略的算法,不是很精确的,只是为了方便调试3D图像,可能会有所不便~
发表于 2006-12-21 18:28:12 | 显示全部楼层
JAY,你这些天貌似对图形学着迷了。。。。。。
 楼主| 发表于 2006-11-9 21:33:28 | 显示全部楼层 |阅读模式
不算是原创,看着别的程序的示例代码写出来的原理,供大家分享~

考虑鼠标移动在一个半球面而不是普通的平面,如图所示。
72006111318736_125.gif
鼠标移动时,不是简单从屏幕上的A点移动到B点,而是在球O上沿着弧AB移动到了B点。
也就是说,向量OA沿着圆O旋转到了向量OB。我们便可以将我们的图形也按照同样的方式进行旋转。
因此我们需要知道的是旋转的角度和旋转的法向量。
步骤:
1.计算鼠标在半球面上的坐标
我们可以通过鼠标在屏幕的平面坐标计算鼠标在半球面上的坐标
假设鼠标的屏幕坐标为(Xs, Ys),半球面坐标为(Xh, Yh, Zh)。显然有
Xh = Xs
Yh = Ys
Zh^2 = r^2–Xh^2–Yh^2
其中r为半球半径,一般可以设置为窗口高度的一半。
这样便可以求出向量OA和向量OB的坐标。

2.计算旋转角度
有了向量OA和向量OB的坐标,利用余弦定理我们可以求得角AOB的大小,不过这个计算很复杂,开销很大。
我们可以考虑用A到B的球面距离,即 弧AB的长度 / 圆O的周长 * 360度。
不过弧长也不是那么好算,好在每次鼠标移动的间隔都不大,我们可以简单的使用弦AB的距离来近似弧AB的长度。
因此我们的算法变得很简单。
角AOB = 弦AB的长度 / 圆O的周长 * 360度
弦AB的长度可以用两点间距离公式求得,即 d^2 = (Xa-Xb)^2+(Ya-Yb)^2+(Za-Zb)^2。

3.计算旋转法向量
旋转法向量n是垂直于向量OA和向量OB所决定的平面的,所以只需要求得向量OA与向量OB的外积(也就是叉乘),就可求得旋转法向量n。
即 n = OA×OB。

4.OpenGL的矩阵乘法
有了旋转角度和旋转的法向量就可以使用glRotate函数来旋转图形了,不过这里有一点需要注意的地方:
例如我们旋转一个图形:
  1. glLoadIdentity();
  2. glRotatef(theta, axis[0], axis[1], axis[2]);
  3. drawCube();
复制代码
在这里的glRotate函数实际是产生了一个旋转矩阵,并将其右乘到当前矩阵上,所以当前矩阵的变化为:M = E·R。
如果我们绘制的图形的某一点是P,那么它在绘制时候,将被旋转到点P' = M·P = E·R·P。
当我们第二次旋转的时候,glRotate函数又产生来一新的旋转矩阵R',它再次被右乘到当前矩阵上,所以当前矩阵的变化为:M = E·R·R'。
那么当我们再次绘制图形时,P点将被旋转到P' = M·P = E·R·R'·P。
此时我们发现P点实际上是先按照第二次的旋转矩阵旋转,再按照第一次的旋转,与我们想要的结果刚好相反。
因此我们需要将每一次的旋转矩阵左乘到当前矩阵上,所以我们需要下面的步骤:
  1. glLoadIdentity();
  2. glRotatef(theta, axis[0], axis[1], axis[2]);
  3. glMultMatrixf(lastMatrix);
  4. glGetFloatv(GL_MODELVIEW_MATRIX, lastMatrix);
复制代码
我们用lastMatrix保存当前矩阵,然后将其右乘到新的旋转矩阵上,实际相当于将新的旋转矩阵左乘到当前矩阵上,这样做了之后才能实现我们想要的目的。

下面这个是编译好的示例程序及源代码:点击下载
发表于 2008-2-24 21:15:48 | 显示全部楼层

1111

1111111111111111111111111111111
您需要登录后才可以回帖 登录 | 加入易码

本版积分规则

Archiver|手机版|小黑屋|EMAX Studio

GMT+8, 2024-3-29 08:52 , Processed in 0.018130 second(s), 21 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表