Qt+OpenGL实现“摄像机”

摄像机       

       OpenGL本身没有摄像机(Camera)的概念,但可以通过把场景中的所有物体往相反方向移动的方式来模拟出摄像机,产生一种在移动的感觉,而不是场景在移动。

       当讨论摄像机(Camera)的时候,是在讨论以摄像机的视角作为场景原点时场景中所有的顶点坐标:观察矩阵把所有的世界坐标变换为相对于摄像机位置与方向的观察坐标。要定义一个摄像机,需要它在世界空间中的位置、观察的方向、一个指向它右侧的向量以及一个指向它上方的向量。

1. 摄像机位置

       获取摄像机位置很简单。摄像机位置简单来说就是世界空间中一个指向摄像机位置的向量。

2. 摄像机方向

       下一个需要的向量是摄像机的方向,这里指的是摄像机指向哪个方向。现在让摄像机指向场景原点:(0, 0, 0)。用场景原点向量减去摄像机位置向量的结果就是摄像机的指向向量。由于知道摄像机指向z轴负方向,但希望方向向量(Direction Vector)指向摄像机的z轴正方向。如果交换相减的顺序,就会获得一个指向摄像机正z轴方向的向量。

3. 右轴

       需要的另一个向量是一个右向量(Right Vector),它代表摄像机空间的x轴的正方向。为获取右向量需要先使用一个小技巧:先定义一个上向量(Up Vector)。接下来把上向量和第二步得到的方向向量进行叉乘。两个向量叉乘的结果会同时垂直于两向量,因此会得到指向x轴正方向的那个向量(如果交换两个向量叉乘的顺序就会得到相反的指向x轴负方向的向量)。

4. 上轴

       现在已经有了x轴向量和z轴向量,获取一个指向摄像机的正y轴向量就相对简单了:把右向量和方向向量进行叉乘。

    //摄像机位置

    QVector3D cameraPos=QVector3D(0.0f,0.0f,2.0f);
    //摄像机方向

    QVector3D cameraTarget = QVector3D(0.0f,0.0f,0.0f);
    QVector3D cameraDirection =QVector3D(cameraPos-cameraTarget);
    cameraDirection.normalize();

    //右轴
    QVector3D up=QVector3D(0.0f,1.0f,0.0f);
    QVector3D cameraRight =QVector3D::crossProduct(up,cameraDirection);
    cameraRight.normalize();
    
    //上轴
    QVector3D cameraUp=QVector3D::crossProduct(cameraDirection,cameraRight);

Look At   

   

        其中R是右向量,U是上向量,D是方向向量,P是摄像机位置向量。注意,位置向量是相反的,因为最终希望把世界平移到与自身移动的相反方向。把这个LookAt矩阵作为观察矩阵可以很高效地把所有世界坐标变换到刚刚定义的观察空间。LookAt矩阵就像它的名字表达的那样:它会创建一个看着(Look at)给定目标的观察矩阵。

     QMatrix4x4 view;
     view.lookAt(cameraPos,cameraPos+cameraFront,up);

        LookAt函数需要一个位置、目标和上向量。它会创建一个和在上一节使用的一样的观察矩阵。

自由移动

       让摄像机绕着场景转的确很有趣,但是让自己移动摄像机会更有趣!首先必须设置一个摄像机系统,所以在程序前面定义一些摄像机变量很有用。

       首先将摄像机位置设置为之前定义的cameraPos。方向是当前的位置加上刚定义的方向向量。这样能保证无论怎么移动,摄像机都会注视着目标方向。让摆弄一下这些向量,在按下某些按钮时更新cameraPos向量。

void testopengl::keyPressEvent(QKeyEvent *event)
{
    float cameraSpeed=2.5f*TIMEOUTMSEC/1000.0f;
    switch (event->key()) {
    case Qt::Key_W: cameraPos+=cameraSpeed*cameraFront;
        qDebug() << "Up pressed, cameraPos:" << cameraPos;
        break;
    case Qt::Key_S: cameraPos-=cameraSpeed*cameraFront;break;
    case Qt::Key_D: cameraPos+=cameraSpeed*cameraRight;break;
    case Qt::Key_A: cameraPos-=cameraSpeed*cameraRight;break;
    default:
        break;
    }
    shaderProgram.bind();
    update();
}

       当按下WASD键的任意一个,摄像机的位置都会相应更新。如果希望向前或向后移动,就把位置向量加上或减去方向向量。如果希望向左右移动,使用叉乘来创建一个右向量(Right Vector),并沿着它相应移动就可以了。这样就创建了使用摄像机时熟悉的横移(Strafe)效果。

视角移动

       为了能够改变视角,需要根据鼠标的输入改变cameraFront向量。

  1. 当鼠标移动时,是mouseMoveEvent被触发。
  2. 计算鼠标移动的位移量,并将其应用到yaw和pitch上。
  3. 根据新的yaw和pitch更新摄像机前方向量cameraFont。
  4. 调用update()重新绘制窗口内容,以应用新的视角。

当用户移动鼠标时,视角会随之改变,模拟第一人称视角的摄像机控制效果。

void testopengl::mouseMoveEvent(QMouseEvent *event)
{
    //yaw和pitch分别表示摄像机的左右(水平)和上下(垂直)旋转角度。yaw初始化为-90.0f,使摄像机初始时朝向-Z轴
    static float yaw=-90;
    static float pitch=0;

    //lastPos 保存上一次鼠标的位置,初始化为窗口的中心点
    static QPoint lastPos(width()/2,height()/2);
    auto currentPos=event->pos();

    //deltaPos 表示当前鼠标位置与上一次鼠标位置的差值,即鼠标的位移量
    deltaPos=currentPos-lastPos;

    //更新lastPos为当前鼠标位置,以便下次计算鼠标位移时使用
    lastPos=currentPos;

    //sensitivity 控制鼠标移动对视角改变的灵敏度,值越大,鼠标移动对视角影响越大
    float sensitivity=0.1f;
    deltaPos *=sensitivity;

    //yaw根据鼠标水平移动量(deltaPos.x())更新
    //pitch根据鼠标垂直移动量(deltaPos.y())更新
    yaw+=deltaPos.x();
    pitch-=deltaPos.y();

    //为了避免摄像机视角超过上下90度,限制pitch的范围在-89.0f到89.0f之间
    if(pitch>89.0f)pitch =89.0f;
    if(pitch<-89.0f)pitch =-89.0f;
    cameraFront.setX(cos(yaw*PI/180)*cos(pitch*PI/180));
    cameraFront.setY(sin(pitch*PI/180));
    cameraFront.setZ(sin(yaw*PI/180)*cos(pitch*PI/180));
    cameraFront.normalize();
    update();
}

运行结果:

缩放

作为摄像机系统的一个附加内容,实现一个缩放(Zoom)接口。在之前的教程中说视野(Field of View)或fov定义了看到场景中多大的范围。当视野变小时,场景投影出来的空间就会减小,产生放大(Zoom In)了的感觉。会使用鼠标的滚轮来放大。与鼠标移动、键盘输入一样,需要一个处理鼠标滚轮事件的函数

void testopengl::wheelEvent(QWheelEvent *event)
{
    if(fov >= 1.0f&&fov<=75.0f)
        fov -= event->angleDelta().y()/120;//滚轮一步是120
    if(fov<=1.0f) fov=1.0f;
    if(fov>=75.0f) fov=75.0f;
}

运行结果:

  • 16
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Qt 是一款跨平台应用程序开发框架,它提供了丰富的功能和工具来简化应用程序的开发OpenGL 是一种图形渲染库,用于创建高性能的2D和3D图形效果。那么,QtOpenGL 的结合能够为开发者提供强大的图形处理能力和丰富的用户界面设计功能。 在学习使用 QtOpenGL 进行应用程序开发时,书籍是非常有帮助的资源。下面是一些关于 QtOpenGL 的书籍推荐: 1.《Qt5 权威指南》:这本书全面介绍了 Qt 的各个方面,包括 Qt 的基础知识、GUI 编程、网络编程、数据库编程等内容,对初学者来说非常友好。同时,书中还有一章专门介绍了如何在 Qt 中使用 OpenGL 进行图形绘制。 2.《Qt5 与 OpenGL 高级编程指南》:这本书着重介绍了 Qt5 和 OpenGL 的结合应用,涵盖了 OpenGL 基础知识、渲染管线、光照、纹理、阴影等高级图形技术。它适合有一定编程经验且对图形编程有兴趣的开发者。 3.《OpenGL编程指南(第九版)》:虽然这本书不是专门讲解 Qt,但是它是学习 OpenGL 必备的经典教材之一。它详细介绍了 OpenGL 的基础知识和相关概念,包括顶点缓冲对象、渲染缓冲区、着色器编程等内容。通过学习这本书,开发者可以更加深入地理解 OpenGL 的工作原理。 以上是一些关于 QtOpenGL 的书籍推荐,选择适合自己水平和需求的书籍进行学习,可以帮助我们更好地掌握 QtOpenGL开发技巧,进而开发出功能丰富且高性能的应用程序。 ### 回答2: Qt是一种跨平台的应用程序开发框架,它提供了丰富的GUI界面设计工具和功能强大的库,可以帮助开发者轻松地开发出高质量的图形界面应用程序。而OpenGL是一种专门用于图形渲染的API,可以实现高性能、高质量的图形渲染效果。 在Qt中使用OpenGL可以帮助开发者更好地利用硬件加速来实现图形渲染,提高程序的性能和效果。因此,学习QtOpenGL的结合使用对于想要开发高质量图形应用程序的开发者来说是非常有价值的。 关于QtOpenGL的书籍,市面上有很多优秀的选择。其中,一本非常经典的书籍是《Qt5与OpenGL开发实战指南》。这本书适合有Qt基础并对OpenGL感兴趣的开发者。书中介绍了使用QtOpenGL进行图形编程的基本原理和技巧,并通过丰富的实例演示了如何使用QtOpenGL实现各种图形效果和交互特性。 除了这本书之外,还有一些其他的书籍也值得推荐。例如,《OpenGL编程指南》是一本经典的OpenGL入门书籍,可以帮助开发者深入理解OpenGL的基本原理和使用方法。《Qt5开发及实例精解》是一本全面介绍Qt开发的书籍,其中也包含了一些关于QtOpenGL结合使用的内容。 总之,选择适合自己的QtOpenGL书籍,可以帮助开发者更好地掌握QtOpenGL的基本知识和技术,从而开发出更加高效、高质量的图形应用程序。 ### 回答3: Qt是一个跨平台的C++应用程序开发框架,它提供了丰富的图形界面和功能库,可以帮助开发者快速地构建高质量的应用程序。OpenGL则是一个用于图形渲染的开放标准,通过与Qt结合使用,可以实现更高级的图形效果和游戏开发。 对于想深入学习QtOpenGL开发者来说,有一些经典的书籍可以提供帮助。首先推荐《Qt编程开发实战》一书,该书详细介绍了Qt的基本概念和常用功能,让读者能够快速入门。接下来,可以阅读《QtOpenGL开发指南》,该书介绍了如何使用QtOpenGL创建高级图形应用程序,并深入解析了底层原理和技术细节。此外,《OpenGL超级宝典》是一本非常经典的OpenGL入门书籍,其中包含了许多实用的代码示例和案例讲解,对于理解OpenGL的基本原理和使用方法非常有帮助。 除了这些书籍,互联网上也有许多免费的教程和博客可以供开发者参考,例如《Qt官方文档》和《OpenGL教程》等。此外,参加一些相关的培训课程或者在线教育平台提供的课程,也是学习QtOpenGL的有效途径。最重要的是,实践是学习的关键,通过动手实践一些小项目,可以帮助开发者更好地理解和应用所学知识。 总的来说,QtOpenGL是非常强大的开发工具,在学习过程中可以参考一些经典的书籍和在线资源,结合实践经验,迅速掌握它们的使用方法和技巧。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值