0

我正在开发一个 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);

这个解决方案将是最......令人愉悦的。

(伪)代码:

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
}
4

2 回答 2

1

这是一个相当不错的实现:https ://github.com/sanko/fltk-2.0.x/blob/master/test/trackball.c 。

于 2014-03-10T17:48:46.980 回答
0

第二种方式确实是这里通常做的,但是在你自己的代码中。在底层,glRotatef 只做矩阵乘法:它设置一个旋转矩阵并将其乘以当前选择的矩阵(可能是模型视图),然后将其存储回该矩阵。

我想你对第二种方法的真正意思是使用 glLoadMatrixf 和 glGet 来加载和存储矩阵,同时在 GL 矩阵堆栈上修改它。但是,执行此操作的现代方法是自己进行所有矩阵计算(所有矩阵修改在最新的 OpenGL 版本中已弃用)。像GLM这样的库有很大帮助,您可以简单地存储多个矩阵并在需要时将其加载到 GL。

关于第三种方式,我想它吸引您的原因是因为您不需要读取和写入 GL,而只需写入它。如果是这种情况,我建议您自己使用 GLM 之类的方法进行矩阵运算。如果“令人愉悦”的组件是您只存储 4 个值而不是 16 个,我建议您研究四元数,它可以存储旋转并以与 glRotatef 使用的轴角非常相似的形式轻松连接旋转 - 它可以很容易地转换为轴角和从轴角转换。当然,旋转矩阵也可以转换为轴角,但是转换要困难一些。

于 2014-03-10T13:31:47.500 回答