我正在开发一个FLTK项目(我第一次尝试使用GUI和opengl:请耐心等待我!)并有一个Fl_Gl_Window,根据其他一些小部件显示各种内容。其中一个选项是以3D显示屏幕内容,并且可以使用鼠标将其旋转为3D。原则上一切都很好(我在窗口处理程序中使用Fl :: event函数来实现鼠标/窗口位置并简单地更新以给定顺序应用的x,y和z旋转角度),但是我正在做的方式它是不直观的(因为非通勤轮换等)所以我正在实现一个轨迹球(类似于这里的轨迹球:http://www.csee.umbc.edu/~squire/download/trackball.c)。我理解这一切是如何工作的,并且可以通过第一次鼠标拖动使其沿着正确的轴旋转。但...
据我所知,问题在于,为了普遍工作(即使用多个鼠标拖动),必须维护模型视图矩阵,以便相对于当前显示的方向旋转对象,以便每次拖动鼠标时都会应用glRotatef。现在,我用FLTK学习一些基本的openGL的方法就是有一个draw()函数,只要有任何变化就会调用它,但是这个(据我在FLTK和我说的那样)必须每次都从头开始窗口具有根据用户选项而改变的内容(它们可能选择2D视图等等)并且还在稍后绘制不打算旋转的内容。因此,我无法看到如何对其进行编码,以便每次重绘都会对模型视图矩阵进行过度更新
a)在某些重绘上,它需要默认返回无旋转(例如2D选项)(但我仍然想'记住'3D对象的旋转)
b)场景的其余部分不能旋转,所以我必须将矩阵弹回到之前的glOrtho ...
我能看到的直接方式是
1)构建所有鼠标拖动的数组并相应地记录,例如,在第10次鼠标拖动时,通过glRotatef应用10次旋转(我不喜欢这个作为解决方案,它很难看!)
2)记录模型视图矩阵的状态并保存&适当时加载它。 (我读过的东西表明这不是应该如何使用openGL的?)
3)找到一个能够减少的数学变换
glRotatef(ang1,ax1_1,ax2_1,ax3_1);
glRotatef(ang2,ax1_2,ax2_2,ax3_2);
到
glRotatef(ang_tot,ax1_tot,ax2_tot,ax3_tot);
这个解决方案将是最令人愉快的。
(Psuedo-)代码:
class MyGl : public Fl_Gl_Window {
void draw();
int handle(int);
void map_to_trackball(double*);
void calc_rotation();
//data including:
double old_vec[3];//old mouse position on hemisphere
double new_vec[3];//new mouse position on hemisphere
double ang; //rotation amount;
double rot[3]; //axis of rotation information
public:
MyGl(int X, int Y, int W, int H, const char *L): Fl_Gl_Window(X, Y, W, H, L) {
//constructor...
ang=0;rot[0]=0;rot[1]=0;rot[2]=0;
}
}
void MyGl::draw(){
if (3D){
glLoadIdentity();
glViewport(0,0,w(),h());
glOrtho(minx,maxx,miny,maxy,minz,maxz);
glPushMatrix();
glRotatef(ang,rot[0],rot[1],rot[2]);
//could save previous rotations and put them here ie:
// glRotatef(ang_old,rot_old[0],rot_old[1],rot_old[2]);
// seems clunky and would require a limitless number of rotations and memory if
// the user keeps tracking
draw_object(); //including glBegin(), glVertex3f() and glEnd() etc.
glPopMatrix();
// draw other non rotated things
}
}
int MyGl::handle(int e){
switch(e){
case: MOUSE_DOWN
map_to_trackball(old_vec);//projects starting mouse position onto hemisphere
//if recording all old rotations for successive implementation in draw()
// would save them here.
return 1; //<-- needed in FLTK to recognise drag event
case: DRAG (//pseudocode
map_to_trackball(new_vec);//projects current dragged mouse
//position onto hemisphere
calc_rotation(); //calculates and sets ang and rot[3]
//using old_vec and new_vec
break;
}
return Fl_Gl_Window::handle(e);
}
void map_to_trackball(double* v){
// basically trackball_ptov() from http://www.csee.umbc.edu/~squire/download/trackball.c
}
void calc_rotation(){
// basically mouseMotion() from http://www.csee.umbc.edu/~squire/download/trackball.c
}
答案 0 :(得分:1)
答案 1 :(得分:0)
第二种方式确实是通常在这里完成的,但是在你自己的代码中。在引擎盖下,glRotatef只做矩阵乘法:它设置一个旋转矩阵并将其乘以当前选择的矩阵(可以推测为模型视图),然后将其存储回该矩阵。
我认为你的第二种方式真正意味着使用glLoadMatrixf和glGet来加载和存储矩阵,同时在GL矩阵堆栈上修改它。但是,现代的方法是自己进行所有矩阵计算(所有矩阵修改都在最新的OpenGL版本中弃用)。像GLM这样的库可以提供很多帮助,您只需存储多个矩阵并在需要时将其加载到GL中。
关于第三种方式,我猜它吸引你的原因是因为你不需要读取和写入GL,但只有 写入它。如果是这种情况,我会推荐使用像GLM这样的矩阵操作。如果“令人愉悦”的组件是你只存储4个值而不是16个,我建议你研究四元数,它可以存储旋转并以一种非常类似于glRotatef使用的轴角的形式连接旋转 - 它很容易就可以了转换为轴角度和从轴角度转换。当然,旋转矩阵也可以转换为轴角,但转换要困难一些。