zoukankan      html  css  js  c++  java
  • OpenGL实现3D漫游的理解

    这篇文章主要参考以下两篇博客:

    推导相机变换矩阵

    OpenGL系列教程之五:OpenGL矩阵类

    上面的第一篇是理论的讲解,第二篇有实例代码,我在后面会给出自己写的主函数,依赖的类可以从第二篇参考中下载。

    我这篇文主要谈我个人对OpenGL中实现3D漫游的思路的理解。经过这些天的学习,主要是研究别人写的代码和网上的的博客,我初步理解了OpenGL中对于多方位观察物体的实现策略。其实,对于3维坐标变换,每个人都可以有自己的理解方式,有的人喜欢研究一堆矩阵,有些人喜欢从空间几何的角度去理解。

    一  要实现3D漫游,第一步就要确定投影方向

    OpenGL首先定义了一个世界坐标系(xyz),还有一个UVN坐标系,两个都是右手系。观察者总是沿着N轴负方向去观察xyz,而物体也总是放在xyz中的。在OpenGL中,xyz其实是一个与物体保持相对位置不动的坐标系,而UVN坐标系其实是一个与观察点位置保持相对不动的坐标系。那么怎样才能全方位观察物体呢?无外乎两个思路:1,让xyz绕着UVN转;2,让UVN绕着xyz转。从OpenGL提供的接口函数及其实现的效果来看,其采用的是前者。

    OpenGL中,屏幕中所呈现的视平面其实是UOV平面,屏幕上方为V轴正方向,屏幕右方为U轴正方向,屏幕由里到外为N轴正方向。因此视线方向总是沿N轴负方向。要想从某个特殊的角度去观察xyz坐标系(物体),只需要确定一个变换矩阵,将xyz先绕着UVN旋转,然后再沿着NO的方向平移,就能一览无余了。其实又可以这样理解,最初xyz与UVN其实是重合的,然后让xyz先乘一个旋转矩阵,再乘一个平移矩阵,这样xyz就以一个特定的姿态出现在视野中了,准确的说是,物体在UOV平面产生了一个特定的投影。

    下面是一些变换矩阵:

    1.平移矩阵:

    其逆矩阵从平移过程去思考的话,显然是反向平移对应的矩阵,因此其逆矩阵只是在x,y,z前面加个负号。

    2.绕x,y,z轴旋转的矩阵:

    三个旋转矩阵Rx,Ry,Rz,它们的列项向量都是两两互相垂直,并且都是单位向量,所以Rx,Ry,Rz都是正交矩阵,它们的逆就是其自身的转置。并且可以证明:有限个正交矩阵的乘积仍为正交矩阵。

    3.绕空间某一向量(x, y, z)旋转的矩阵。注意向量是有方向的,旋转规则仍是右手螺旋定则:

    二  投影方向确定了,下一步就是确定投影面的位置,并且还需要在投影面中选取一个区域,该区域就是最终显示在屏幕上的图像

    下面这张图我认为很清晰地展示了投影面的确定过程

    上图采用的是透视投影,OpenGL中还有一种平行投影,分别由函数

    void gluPerspective (GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar )
    void glOrtho( GLdouble left, GLdouble right,
                                     GLdouble bottom, GLdouble top,
                                     GLdouble near_val, GLdouble far_val )

    确定投影的参数。gluPerspective参数中的fovy就是图中的视角,以度为单位,zNear为近平面距离,zFar为远平面距离。

    选定投影区域后,会将该区域的坐标标准化到[-1, 1]之间,再通过函数void glViewport( GLint x, GLint y, GLsizei width, GLsizei height )

    将标准化的坐标映射到图像坐标,映射公式如下

    图中xnd,ynd是归一化后的坐标,xw,yw是对应的图像坐标,以像素为单位,当xnd=-1,ynd=-1时,映射的是图像左下角的坐标,即以像素为单位时,图像坐下角坐标为(0, 0)。下面是一段实现3D漫游的代码,按x键让茶壶绕U轴旋转,按y键让茶壶绕V轴旋转,也可以用鼠标控制茶壶旋转。代码中依赖的矩阵类可从参考博客中获得。

      1 #include <GL/glut.h>
      2 #include "Matrices.h"
      3 #include "Vectors.h"
      4 
      5 // GLUT CALLBACK functions
      6 void displayCB();
      7 void reshapeCB(int w, int h);
      8 void timerCB(int millisec);
      9 void keyboardCB(unsigned char key, int x, int y);
     10 void mouseCB(int button, int stat, int x, int y);
     11 void mouseMotionCB(int x, int y);
     12 void initGL();
     13 void initLights();
     14 void drawAxis(float size = 0.6f);
     15 
     16 const int   SCREEN_WIDTH    = 600;
     17 const int   SCREEN_HEIGHT   = 600;
     18 const float CAMERA_DISTANCE = 1.5f;
     19 const int   TEXT_WIDTH      = 8;
     20 const int   TEXT_HEIGHT     = 13;
     21 const float DEG2RAD         = 3.141593f / 180;
     22 
     23 // global variables
     24 int screenWidth;
     25 int screenHeight;
     26 bool mouseLeftDown;
     27 bool mouseRightDown;
     28 float mouseX, mouseY;
     29 float cameraAngleX;
     30 float cameraAngleY;
     31 float cameraDistance;
     32 Matrix4 matrixView;
     33 Matrix4 matrixModel;
     34 Matrix4 matrixModelView;
     35 Matrix4 matrixProjection;
     36 
     37 int main(int argc, char **argv)
     38 {
     39     // init global vars
     40     screenWidth = SCREEN_WIDTH;
     41     screenHeight = SCREEN_HEIGHT;
     42     mouseLeftDown = mouseRightDown = false;
     43     mouseX = mouseY = 0;
     44     cameraAngleX = cameraAngleY = 0;
     45     cameraDistance = CAMERA_DISTANCE;
     46 
     47     glutInit(&argc, argv);
     48     glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH );                 // display mode
     49     glutInitWindowSize(screenWidth, screenHeight);                             // window size
     50     glutInitWindowPosition(100, 100);                                          // window location
     51     glutCreateWindow(argv[0]);                                                 // param is the title of window
     52 
     53     glEnable(GL_DEPTH_TEST);
     54     glutReshapeFunc(reshapeCB);
     55     glutDisplayFunc(displayCB);
     56     glutTimerFunc(33, timerCB, 33);                                            // redraw only every given millisec
     57 
     58     glutKeyboardFunc(keyboardCB);
     59     glutMouseFunc(mouseCB);
     60     glutMotionFunc(mouseMotionCB);
     61 
     62     glutMainLoop();
     63 
     64     return 0;
     65 }
     66 
     67 void drawAxis(float size)
     68 {
     69     glDepthFunc(GL_ALWAYS);     // to avoid visual artifacts with grid lines
     70 
     71     // draw axis
     72     glLineWidth(3);
     73     glBegin(GL_LINES);
     74         glColor3f(1, 0, 0);
     75         glVertex3f(0, 0, 0);
     76         glVertex3f(size, 0, 0);
     77         glColor3f(0, 1, 0);
     78         glVertex3f(0, 0, 0);
     79         glVertex3f(0, size, 0);
     80         glColor3f(0, 0, 1);
     81         glVertex3f(0, 0, 0);
     82         glVertex3f(0, 0, size);
     83     glEnd();
     84     glLineWidth(1);
     85 
     86     // draw arrows(actually big square dots)
     87     glPointSize(5);
     88     glBegin(GL_POINTS);
     89         glColor3f(1, 0, 0);
     90         glVertex3f(size, 0, 0);
     91         glColor3f(0, 1, 0);
     92         glVertex3f(0, size, 0);
     93         glColor3f(0, 0, 1);
     94         glVertex3f(0, 0, size);
     95     glEnd();
     96     glPointSize(1);
     97 
     98     // restore default settings
     99     glDepthFunc(GL_LEQUAL);
    100 }
    101 
    102 void displayCB()
    103 {
    104     // clear buffer
    105     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    106 
    107     // transform camera
    108     matrixView.translate(0, 0, cameraDistance);
    109     matrixView.rotate(cameraAngleX, 1, 0, 0);     //cameraAngleX
    110     matrixView.rotate(cameraAngleY, 0, 1, 0);     //cameraAngleY
    111     matrixView.translate(0, 0, -cameraDistance);
    112     cameraAngleX = 0;
    113     cameraAngleY = 0;
    114     glLoadMatrixf(matrixView.get());
    115 
    116     glColor3f(1.0f, 0.3f, 1.0f);
    117     glutWireTeapot(0.6f);
    118     drawAxis();
    119 
    120     glutSwapBuffers();
    121 }
    122 
    123 void reshapeCB(int w, int h)
    124 {
    125     // set viewport to be the entire window
    126     glViewport(0, 0, (GLsizei)w, (GLsizei)h);
    127 
    128     glMatrixMode(GL_PROJECTION);
    129     gluPerspective(80.0f, (float)(w)/h, 0.5f, 10.0f); // FOV, AspectRatio, NearClip, FarClip
    130 
    131     // switch to modelview matrix in order to set scene
    132     glMatrixMode(GL_MODELVIEW);
    133     matrixView.identity();
    134     matrixView.translate(0, 0, -cameraDistance);
    135     glLoadMatrixf(matrixView.get());
    136 }
    137 
    138 void timerCB(int millisec)
    139 {
    140     glutTimerFunc(millisec, timerCB, millisec);
    141     glutPostRedisplay();
    142 }
    143 
    144 void keyboardCB(unsigned char key, int x, int y)
    145 {
    146     switch(key)
    147     {
    148     case 'x':
    149         cameraAngleX = 3;
    150         break;
    151     case 'X':
    152         cameraAngleX = -3;
    153         break;
    154     case 'y':
    155         cameraAngleY = 3;
    156         break;
    157     case 'Y':
    158         cameraAngleY = -3;
    159         break;
    160     case 27:              // ESCAPE
    161         exit(0);
    162         break;
    163     default:
    164         break;
    165     }
    166 }
    167 
    168 void mouseCB(int button, int state, int x, int y)
    169 {
    170     mouseX = x;
    171     mouseY = y;
    172 
    173     if(button == GLUT_LEFT_BUTTON)
    174     {
    175         if(state == GLUT_DOWN)
    176         {
    177             mouseLeftDown = true;
    178         }
    179         else if(state == GLUT_UP)
    180             mouseLeftDown = false;
    181     }
    182     else if(button == GLUT_RIGHT_BUTTON)
    183     {
    184         if(state == GLUT_DOWN)
    185         {
    186             mouseRightDown = true;
    187         }
    188         else if(state == GLUT_UP)
    189             mouseRightDown = false;
    190     }
    191 }
    192 
    193 void mouseMotionCB(int x, int y)
    194 {
    195     if(mouseLeftDown)
    196     {
    197         cameraAngleY = (x - mouseX);
    198         cameraAngleX = (y - mouseY);
    199         mouseX = x;
    200         mouseY = y;
    201     }
    202     if(mouseRightDown)
    203     {
    204         matrixView.translate(0, 0, cameraDistance);
    205         cameraDistance += (y - mouseY) * 0.01f;
    206         mouseY = y;
    207         matrixView.translate(0, 0, -cameraDistance);
    208     }
    209 }
    View Code

    效果如下图:

  • 相关阅读:
    1210 BBS admin后台管理及侧边栏筛选个人站点
    1209 BBS 登录
    更换 npm 源国内镜像 cnpm
    Linux软件管理
    apt-get / yum 软件安装源(国内)
    修改pip源为国内镜像源(加速下载)
    修改浏览器搜索引擎:网址应该如何填写
    如何根据实际问题选择一个合适的数学模型
    安装向量和矩阵运算库函数
    配置编译器(GCC和GFortran)
  • 原文地址:https://www.cnblogs.com/pursuiting/p/6234751.html
Copyright © 2011-2022 走看看