zoukankan      html  css  js  c++  java
  • [译]GLUT教程

    Lighthouse3d.com >> GLUT Tutorial >> Input >> Move the Camera I

    下面来看一个更有趣的GLUT应用.本节我们会绘制一个雪人世界,并直接用按键移动镜头.向左和向右键会在XZ切面围绕Y轴旋转镜头.反之,向上和向下键会在当前方向向前向后移动镜头.

    实现代码已经在适当地方标上注释.首先我们要一些全局变量来保存镜头参数.这些变量会保存了镜头位置和目标方向的向量.我们还需要保存角度.由于y是常量,所以不用保存.

    angle: y轴上旋转的角度.该变量是旋转镜头用

    x,z: XZ平面的镜头位置

    lx, lz: 定义我们视线的向量

    以上变量的赋值如下:

    // angle of rotation for the camera direction
    float angle=0.0;
    // actual vector representing the camera's direction
    float lx=0.0f,lz=-1.0f;
    // XZ position of the camera
    float x=0.0f,z=5.0f;

    画雪人的代码下面给出,运行结果如下图:

    void drawSnowMan() {
    
        glColor3f(1.0f, 1.0f, 1.0f);
    
    // Draw Body
        glTranslatef(0.0f ,0.75f, 0.0f);
        glutSolidSphere(0.75f,20,20);
    
    // Draw Head
        glTranslatef(0.0f, 1.0f, 0.0f);
        glutSolidSphere(0.25f,20,20);
    
    // Draw Eyes
        glPushMatrix();
        glColor3f(0.0f,0.0f,0.0f);
        glTranslatef(0.05f, 0.10f, 0.18f);
        glutSolidSphere(0.05f,10,10);
        glTranslatef(-0.1f, 0.0f, 0.0f);
        glutSolidSphere(0.05f,10,10);
        glPopMatrix();
    
    // Draw Nose
        glColor3f(1.0f, 0.5f , 0.5f);
        glRotatef(0.0f,1.0f, 0.0f, 0.0f);
        glutSolidCone(0.08f,0.5f,10,2);
    }

    接下来我们得到新的渲染函数.它包含所有的绘制雪人世界的命令.另一个改变是gluLookAt函数.gluLookAt函数的参数从默认值变成了传入变量.你可能不熟悉这个函数,下面来解释一下.gluLookAt函数提供一个简单直观的方式来设置镜头位置和方向.一般而言它有三组参数,每组由三个浮点型值组成.第一组是标示了镜头位置.第二组是定义我们视线的点,它可以是我们视线上的任意点.最后一组是标示了向上的向量,一般设置为(0.0, 1.0, 0.0),意思是镜头是没有倾斜的.如果你希望镜头倾斜你可以更改这组值.例如,改成倒视视觉是(0.0, -1.0, 0.0).

    如前文所述, x,y,z表示了镜头的位置,所以它们的值和gluLookAt的第一个向量一致.第二组参数,目侧点,是通过视觉和镜头位置的向量相加所得.

    目视点 = 视线 + 镜头位置

    下面是渲染函数的实现代码:

    void renderScene(void) {
    
        // Clear Color and Depth Buffers
    
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
        // Reset transformations
        glLoadIdentity();
        // Set the camera
        gluLookAt(    x, 1.0f, z,
                x+lx, 1.0f,  z+lz,
                0.0f, 1.0f,  0.0f);
    
            // Draw ground
        glColor3f(0.9f, 0.9f, 0.9f);
        glBegin(GL_QUADS);
            glVertex3f(-100.0f, 0.0f, -100.0f);
            glVertex3f(-100.0f, 0.0f,  100.0f);
            glVertex3f( 100.0f, 0.0f,  100.0f);
            glVertex3f( 100.0f, 0.0f, -100.0f);
        glEnd();
    
            // Draw 36 SnowMen
        for(int i = -3; i < 3; i++)
            for(int j=-3; j < 3; j++) {
                glPushMatrix();
                glTranslatef(i*10.0,0,j * 10.0);
                drawSnowMan();
                glPopMatrix();
            }
    
        glutSwapBuffers();
    }

    现在开始处理箭头键(上下左右).我们用左右箭头键来旋转镜头,例如更改向量来定义视线.上下键用来移动当前方向的视线向前向后.

    当用户按左右键时,角度变量也会跟着改变.随着角度值的改变,程序会重新计算出视线向量的lx和lz组件相应的合适的值..留意到我们现在只是在XZ平面移动,所以我们不用改变视觉向量的ly坐标.新的lx和lz值会映射到单一的XZ平面的圆圈上.于是,下面得出了计算角度ang和新的lx,lz值的公式:

    lx = sin(ang)

    lz = -cos(ang)

    跟我们把极坐标转换成平面坐标一样.lz是负值,因为初始值为-1.

    注意,当更新lx和lz时镜头是不移动的,镜头位置不变,只有目视点改变.

    我们还想沿着视线移动镜头,例如下一个镜头位置需要沿着视线向量.为了达到这个效果我们需要分别在按上/下键的时候加/减一个粒度的视觉向量到当前位置.例如,移动镜头向前时,x和z的计算公式如下:

    x = x + lx * 粒度

    z = z + lz * 粒度

    这里的粒度是指一个合适的速率.我们知道lx和lz是一个单一向量(前面提及,它是一个单位周期中的点),因此如果粒度值保持在一个常量,速率便会维持在一个常量变化的感觉.增加粒度我们就移动得快一点,也就是说,我们会在每一帧移动得更远.

    void processSpecialKeys(int key, int xx, int yy) {
    
        float fraction = 0.1f;
    
        switch (key) {
            case GLUT_KEY_LEFT :
                angle -= 0.01f;
                lx = sin(angle);
                lz = -cos(angle);
                break;
            case GLUT_KEY_RIGHT :
                angle += 0.01f;
                lx = sin(angle);
                lz = -cos(angle);
                break;
            case GLUT_KEY_UP :
                x += lx * fraction;
                z += lz * fraction;
                break;
            case GLUT_KEY_DOWN :
                x -= lx * fraction;
                z -= lz * fraction;
                break;
        }
    }

    配合着以上代码的更改,main函数里面也增加一些代码.唯一的变化是开启了深度测试特性.

    int main(int argc, char **argv) {
    
        // init GLUT and create window
    
        glutInit(&argc, argv);
        glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
        glutInitWindowPosition(100,100);
        glutInitWindowSize(320,320);
        glutCreateWindow("Lighthouse3D - GLUT Tutorial");
    
        // register callbacks
        glutDisplayFunc(renderScene);
        glutReshapeFunc(changeSize);
        glutIdleFunc(renderScene);
        glutKeyboardFunc(processNormalKeys);
        glutSpecialFunc(processSpecialKeys);
    
        // OpenGL init
        glEnable(GL_DEPTH_TEST);
    
        // enter GLUT event processing cycle
        glutMainLoop();
    
        return 1;
    }
  • 相关阅读:
    云计算设计模式(十一)——健康端点监控模式
    大数据R语言简析
    git查看/修改 用户名和邮箱
    MySQL查询和修改auto_increment的方法
    git 配置用户名和邮箱
    discuz安装小云app
    二维码转化为链接
    discuz更换域名,登录不了解决
    数据结构很重要
    C++ vector错误(1)
  • 原文地址:https://www.cnblogs.com/live41/p/3386597.html
Copyright © 2011-2022 走看看