Qt OpenGL 光照和键盘控制

news2024/9/23 7:29:54

这次教程中,我们将添加光照和键盘控制,它让程序看起来更美观。我将教大家如何使用键盘来移动场景中的对象,还会教大家在OpenGL场景中应用简单的光照,让我们的程序更加视觉效果更好且受我们控制。

程序运行时效果如下:

 

下面进入教程:

我们这次将在第06课的基础上修改代码,首先打开myglwidget.h文件,将类声明更改如下:

 1 #ifndef MYGLWIDGET_H
 2 #define MYGLWIDGET_H
 3  
 4 #include <QWidget>
 5 #include <QGLWidget>
 6  
 7 class MyGLWidget : public QGLWidget
 8 {
 9     Q_OBJECT
10 public:
11     explicit MyGLWidget(QWidget *parent = 0);
12     ~MyGLWidget();
13  
14 protected:
15     //对3个纯虚函数的重定义
16     void initializeGL();
17     void resizeGL(int w, int h);
18     void paintGL();
19  
20     void keyPressEvent(QKeyEvent *event);           //处理键盘按下事件
21  
22 private:
23     bool fullscreen;                                //是否全屏显示
24  
25     QString m_FileName;                             //图片的路径及文件名
26     GLuint m_Texture;                               //储存一个纹理    
27     bool m_Light;                                   //光源的开/关
28     
29     GLfloat m_xRot;                                 //x旋转角度
30     GLfloat m_yRot;                                 //y旋转角度
31     GLfloat m_xSpeed;                               //x旋转速度
32     GLfloat m_ySpeed;                               //y旋转速度
33     GLfloat m_Deep;                                 //深入屏幕的距离
34 };
35  
36 #endif // MYGLWIDGET_H

CSDN QT技术栈大纲:Qt开发必备技术栈学习路线和资料

增加了一个布尔变量表示光源的开关,剩下的五个浮点变量用于控制对象的旋转角度,旋转速度以及距离屏幕的位置。

接下来,我们需要打开myglwidget.cpp,加上声明#include <QTimer>,在构造函数中对新增变量(除了m_Texture)进行初始化,同样不作过多解释,代码如下:

 1 MyGLWidget::MyGLWidget(QWidget *parent) :
 2     QGLWidget(parent)
 3 {
 4     fullscreen = false;
 5     m_FileName = "D:/QtOpenGL/QtImage/Crate.bmp";        //应根据实际存放图片的路径进行修改
 6     m_Light = false;
 7     
 8     m_xRot = 0.0f;
 9     m_yRot = 0.0f;
10     m_xSpeed = 0.0f;
11     m_ySpeed = 0.0f;
12     m_Deep = -5.0f;
13     
14     QTimer *timer = new QTimer(this);                   //创建一个定时器
15     //将定时器的计时信号与updateGL()绑定
16     connect(timer, SIGNAL(timeout()), this, SLOT(updateGL()));
17     timer->start(10);                                   //以10ms为一个计时周期
18 }

然后,我们要来添加光照,只需要在initializeGL()函数增加几行代码,具体修改后代码如下:

 1 void MyGLWidget::initializeGL()                         //此处开始对OpenGL进行所以设置
 2 {
 3     m_Texture = bindTexture(QPixmap(m_FileName));       //载入位图并转换成纹理
 4     glEnable(GL_TEXTURE_2D);                            //启用纹理映射
 5  
 6     glClearColor(0.0f, 0.0f, 0.0f, 0.0f);               //黑色背景
 7     glShadeModel(GL_SMOOTH);                            //启用阴影平滑
 8  
 9     glClearDepth(1.0);                                  //设置深度缓存
10     glEnable(GL_DEPTH_TEST);                            //启用深度测试
11     glDepthFunc(GL_LEQUAL);                             //所作深度测试的类型
12     glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);  //告诉系统对透视进行修正
13  
14     GLfloat LightAmbient[] = {0.5f, 0.5f, 0.5f, 1.0f};  //环境光参数
15     GLfloat LightDiffuse[] = {1.0f, 1.0f, 1.0f, 1.0f};  //漫散光参数
16     GLfloat LightPosition[] = {0.0f, 0.0f, 2.0f, 1.0f}; //光源位置
17     glLightfv(GL_LIGHT1, GL_AMBIENT, LightAmbient);     //设置环境光
18     glLightfv(GL_LIGHT1, GL_DIFFUSE, LightDiffuse);     //设置漫射光
19     glLightfv(GL_LIGHT1, GL_POSITION, LightPosition);   //设置光源位置
20     glEnable(GL_LIGHT1);                                //启动一号光源
21 }

首先我们分别定义环境光参数,漫射光参数以及光源位置。环境光来自于四面八方,所以场景中的对象都处于环境光的照射中;漫射光由特定的光源产生,并在场景中的对象表明产生反射。处于漫射光直接照射下的任何对象表面都变得很亮,而几乎未被照到的区域显得要暗一些。这样我们所创建的木板箱的棱边上就会产生很不错的阴影效果。

创建光源的过程和颜色的创建完全一致,前三个参数分别是RGB三色分量,最后一个是alpha通道参数。最后光源位置前三个参数和glTranslate中的一样,一次表示x、y、z轴上的位移,最后一个参数取为1.0f,这将告诉OpenGL这里指定的坐标就是光源的位置,以后的教程中我会多加解释。

接着开始设置光源,使得光源GL_LIGHT1开始发光,然后是设置光源位置(位于木箱原中心在z方向移向观察者2.0单位),最后我们启用一号光源。要注意的是,我们还没有启用GL_LIGHTING,所以是看不见任何光线的。记住,只对光源进行设置、定位、甚至启用,光源都不会工作,除非我们启用GL_LIGHTING。

还有是对paintGL()函数的修改,修改后具体代码如下:

 1 void MyGLWidget::paintGL()                              //从这里开始进行所以的绘制
 2 {
 3     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //清除屏幕和深度缓存
 4     glLoadIdentity();                                   //重置模型观察矩阵
 5     glTranslatef(0.0f, 0.0f, m_Deep);                   //移入屏幕
 6     glRotatef(m_xRot, 1.0f, 0.0f, 0.0f);                //绕x轴旋转
 7     glRotatef(m_yRot, 0.0f, 1.0f, 0.0f);                //绕y轴旋转
 8  
 9     glBindTexture(GL_TEXTURE_2D, m_Texture);            //选择纹理
10     glBegin(GL_QUADS);                                  //开始绘制立方体
11         glNormal3f(0.0f, 1.0f, 0.0f);
12         glTexCoord2f(1.0f, 1.0f);
13         glVertex3f(1.0f, 1.0f, -1.0f);                  //右上(顶面)
14         glTexCoord2f(0.0f, 1.0f);
15         glVertex3f(-1.0f, 1.0f, -1.0f);                 //左上(顶面)
16         glTexCoord2f(0.0f, 0.0f);
17         glVertex3f(-1.0f, 1.0f, 1.0f);                  //左下(顶面)
18         glTexCoord2f(1.0f, 0.0f);
19         glVertex3f(1.0f, 1.0f, 1.0f);                   //右下(顶面)
20  
21         glNormal3f(0.0f, -1.0f, 0.0f);
22         glTexCoord2f(0.0f, 0.0f);
23         glVertex3f(1.0f, -1.0f, 1.0f);                  //右上(底面)
24         glTexCoord2f(1.0f, 0.0f);
25         glVertex3f(-1.0f, -1.0f, 1.0f);                 //左上(底面)
26         glTexCoord2f(1.0f, 1.0f);
27         glVertex3f(-1.0f, -1.0f, -1.0f);                //左下(底面)
28         glTexCoord2f(0.0f, 1.0f);
29         glVertex3f(1.0f, -1.0f, -1.0f);                 //右下(底面)
30  
31         glNormal3f(0.0f, 0.0f, 1.0f);
32         glTexCoord2f(1.0f, 1.0f);
33         glVertex3f(1.0f, 1.0f, 1.0f);                   //右上(前面)
34         glTexCoord2f(0.0f, 1.0f);
35         glVertex3f(-1.0f, 1.0f, 1.0f);                  //左上(前面)
36         glTexCoord2f(0.0f, 0.0f);
37         glVertex3f(-1.0f, -1.0f, 1.0f);                 //左下(前面)
38         glTexCoord2f(1.0f, 0.0f);
39         glVertex3f(1.0f, -1.0f, 1.0f);                  //右下(前面)
40  
41         glNormal3f(0.0f, 0.0f, -1.0f);
42         glTexCoord2f(0.0f, 0.0f);
43         glVertex3f(1.0f, -1.0f, -1.0f);                 //右上(后面)
44         glTexCoord2f(1.0f, 0.0f);
45         glVertex3f(-1.0f, -1.0f, -1.0f);                //左上(后面)
46         glTexCoord2f(1.0f, 1.0f);
47         glVertex3f(-1.0f, 1.0f, -1.0f);                 //左下(后面)
48         glTexCoord2f(0.0f, 1.0f);
49         glVertex3f(1.0f, 1.0f, -1.0f);                  //右下(后面)
50  
51         glNormal3f(-1.0f, 0.0f, 0.0f);
52         glTexCoord2f(1.0f, 1.0f);
53         glVertex3f(-1.0f, 1.0f, 1.0f);                  //右上(左面)
54         glTexCoord2f(0.0f, 1.0f);
55         glVertex3f(-1.0f, 1.0f, -1.0f);                 //左上(左面)
56         glTexCoord2f(0.0f, 0.0f);
57         glVertex3f(-1.0f, -1.0f, -1.0f);                //左下(左面)
58         glTexCoord2f(1.0f, 0.0f);
59         glVertex3f(-1.0f, -1.0f, 1.0f);                 //右下(左面)
60  
61         glNormal3f(1.0f, 0.0f, 0.0f);
62         glTexCoord2f(1.0f, 1.0f);
63         glVertex3f(1.0f, 1.0f, -1.0f);                  //右上(右面)
64         glTexCoord2f(0.0f, 1.0f);
65         glVertex3f(1.0f, 1.0f, 1.0f);                   //左上(右面)
66         glTexCoord2f(0.0f, 0.0f);
67         glVertex3f(1.0f, -1.0f, 1.0f);                  //左下(右面)
68         glTexCoord2f(1.0f, 0.0f);
69         glVertex3f(1.0f, -1.0f, -1.0f);                 //右下(右面)
70     glEnd();                                            //立方体绘制结束
71  
72     m_xRot += m_xSpeed;                                 //x轴旋转
73     m_yRot += m_ySpeed;                                 //y轴旋转
74 }

除了旋转及移动上作了修改外(相信大家能看懂),多了glNormal3f()函数的调用。该函数指定一条法线,法线告诉OpenGL这个多边形的朝向,并指明多边形的正面和背面,如果没有法线,什么怪事情都可能发生:不该亮的面被照亮了,多边形的背面也被照亮了…还要注意的是,法线应指向多边形的外侧。
最后两行代码作了一定的修改,利用变量m_xSpeed、m_ySpeed来控制立方体的旋转速度。

最后当然就是键盘控制了,具体代码如下(相信大家结合注释可以很容易看懂):

 1 void MyGLWidget::keyPressEvent(QKeyEvent *event)
 2 {
 3     switch (event->key())
 4     {
 5     case Qt::Key_F1:                                    //F1为全屏和普通屏的切换键
 6         fullscreen = !fullscreen;
 7         if (fullscreen)
 8         {
 9             showFullScreen();
10         }
11         else
12         {
13             showNormal();
14         }
15         break;
16     case Qt::Key_Escape:                                //ESC为退出键
17         close();
18         break;
19     case Qt::Key_L:                                     //L为开启关闭光源的切换键
20         m_Light = !m_Light;
21         if (m_Light)
22         {
23             glEnable(GL_LIGHTING);                      //开启光源
24         }
25         else
26         {
27             glDisable(GL_LIGHTING);                     //关闭光源
28         }
29         break;
30     case Qt::Key_PageUp:                                //PageUp按下使木箱移向屏幕内部
31         m_Deep -= 0.1f;
32         break;
33     case Qt::Key_PageDown:                              //PageDown按下使木箱移向观察者
34         m_Deep += 0.1f;
35         break;
36     case Qt::Key_Up:                                    //Up按下减少m_xSpeed
37         m_xSpeed -= 0.1f;
38         break;
39     case Qt::Key_Down:                                  //Down按下增加m_xSpeed
40         m_xSpeed += 0.1f;
41         break;
42     case Qt::Key_Right:                                 //Right按下减少m_ySpeed
43         m_ySpeed -= 0.1f;
44         break;
45     case Qt::Key_Left:                                  //Left按下增加m_ySpeed
46         m_ySpeed += 0.1f;
47         break;
48     }
49 }

本文福利,费领取Qt开发学习资料包、技术视频,内容包括(C++语言基础,Qt编程入门,QT信号与槽机制,QT界面开发-图像绘制,QT网络,QT数据库编程,QT项目实战,QT嵌入式开发,Quick模块等等)↓↓↓↓↓↓见下面↓↓文章底部点击费领取↓↓ 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/59649.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

java项目_第168期ssm二手车交易网站-_计算机毕业设计

java项目_第168期ssm二手车交易网站-_计算机毕业设计 【源码请到资源专栏下载】 今天分享的项目是《ssm二手车交易网站》 该项目分为2个角色&#xff0c;管理员和用户。 用户可以浏览前台,包含功能有&#xff1a; 首页、商品信息、论坛信息、新闻资讯 、留言反馈、购物车、跳转…

目的:ubuntu配置使用opengl - 初探-创建一个空窗口

目的&#xff1a;ubuntu配置使用openGL - 初探-创建一个空窗口 环境&#xff1a; 系统&#xff1a;Ubuntu18.04 环境&#xff1a;g步骤&#xff1a; Ubuntu下使用openGL&#xff0c;搭建配置环境并测试窗口 1、openGL库&#xff0c;需要单独安装&#xff0c;由于本机是vmwar…

萌新源api管理系统更新教程

前言 萌新源API管理系统旨在大家提供一个更为方便的管理api的方法&#xff0c;经过几个月的时间&#xff0c;目前已经迭代到v4.41版本 那么这篇文章呢&#xff0c;也是想要教一下大家怎样去更新我们最新版本的管理系统&#xff0c;我最近也是在研究自动化更新&#xff0c;但是…

【CVPR2022】Detecting Camouflaged Object in Frequency Domain

【CVPR2022】Detecting Camouflaged Object in Frequency Domain 论文有一个非官方的实现&#xff1a;https://github.com/VisibleShadow/Implementation-of-Detecting-Camouflaged-Object-in-Frequency-Domain 感觉这个论文是CVPR2022 Learning in the frequency domain 论文…

具有 1 个射频链的 OFDM-MIMO 系统的波束训练(Matlab代码实现)

&#x1f468;‍&#x1f393;个人主页&#xff1a;研学社的博客 &#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜…

基于多能互补的热电联供型微网优化运行(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

特征缩放是强制性的吗? 什么时候使用标准化? 什么时候使用归一化?数据的分布会发生什么变化?对异常值有什么影响?模型的准确性会提高吗?

以上问题也是面试中经常被问到的问题,我将在本博客中尝试通过提供合适的例子来回答上述问题。我们将使用 sklearn 的 StandardScaler 和 MinMaxScaler。 让我们考虑一个数据集,其中Age和Estimated Salary是输入特征,我们必须预测产品是否已购买(输出标签)或未购买。 看看…

【1796. 字符串中第二大的数字】

来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 描述&#xff1a; 给你一个混合字符串 s &#xff0c;请你返回 s 中 第二大 的数字&#xff0c;如果不存在第二大的数字&#xff0c;请你返回 -1 。 混合字符串 由小写英文字母和数字组成。 示例 1&#xff1a; 输入…

Spring - BeanFactoryAware扩展接口

文章目录Preorg.springframework.beans.factory.BeanFactoryAware扩展点说明Aware接口Spring内建Aware接口的执行时机及顺序源码解析 &#xff08;直接调用&#xff09;源码分析 _ BeanPostProcessor调用执行顺序扩展点示例Pre Spring Boot - 扩展接口一览 org.springframewor…

Kotlin Flow 背压和线程切换竟然如此相似

前言 上篇分析了Kotlin Flow原理&#xff0c;大部分操作符实现比较简单&#xff0c;相较而言背压和线程切换比较复杂&#xff0c;遗憾的是&#xff0c;纵观网上大部分文章&#xff0c;关于Flow背压和协程切换这块的原理说得比较少&#xff0c;语焉不详&#xff0c;鉴于此&…

5G无线技术基础自学系列 | 5G上行功率控制

素材来源&#xff1a;《5G无线网络规划与优化》 一边学习一边整理内容&#xff0c;并与大家分享&#xff0c;侵权即删&#xff0c;谢谢支持&#xff01; 附上汇总贴&#xff1a;5G无线技术基础自学系列 | 汇总_COCOgsta的博客-CSDN博客 5G上行功率控制是针对每个UE的不同信道…

CS224W 8 GNN Augmentation andTraining

目录 Graph Augmentation for GNNs 引入 Why Graph Augmentation Graph Augmentation Approaches Feature Augmentation on Graphs Input graph没有node features GNN很难学习的一些特定结构 Graph Structure augmentation Augment sparse graphs——添加虚拟节点或边…

不同平台下运行历程代码

不同平台下运行历程代码 所谓的大端模式,是指数据的低位(就是权值较小的后面那几位)保存在内存的高地址中,而数据的高位,保存在内存的低地址中,这样的存储模式有点儿类似于把数据当作字符串顺序处理:地址由小向大增加,而数据从高位往低位放; 所谓的小端模式,是指数据…

【Mysql】索引

文章目录一.索引的价值1.1. mysql与磁盘交互的基本单位建立共识1.2. 为什么IO交互的基本单位为Page理解单个Page理解多个Page提高在单个Page中的查找效率针对多页情况的页目录为什么选择B树,而不是其他数据结构&#xff1f;没有主键会怎么创建索引为什么推荐使用自增ID作为主键…

Spring(Bean 作用域和生命周期)

目录 1. 案例1: Bean作用域的问题 2. 作用域 3. 设置 Bean 的作用域 4. Spring 的执行流程 5. Bean 的生命周期 1. 案例1: Bean作用域的问题 现在有一个公共的 Bean,通过给 A 用户 和 B 用户使用, 然后在使用的过程中 A 偷偷的修改了公共 Bean 的数据, 导致 B 在使用时发…

html静态网站基于动漫网站网页设计与实现共计4个页面

HTML实例网页代码, 本实例适合于初学HTML的同学。该实例里面有设置了css的样式设置&#xff0c;有div的样式格局&#xff0c;这个实例比较全面&#xff0c;有助于同学的学习,本文将介绍如何通过从头开始设计个人网站并将其转换为代码的过程来实践设计。 ⚽精彩专栏推荐&#x1…

基于遗传算法的微电网调度(风、光、蓄电池、微型燃气轮机)(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️❤️&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清…

Docker介绍

目录 docker定义 docker解决了什么问题 docker技术边界 docker给我们带来了哪些改变 docker和虚拟机的区别 docker基本架构 基本架构图 RootFs Linux Namespace 进程命名空间 查看元祖进程命名空间 查看当前用户进程命名空间 容器进程命名空间 容器进程命名空间的…

[激光原理与应用-33]:典型激光器 -5- 不同激光器的全面、综合比较

目录 第1章 五类激光器的性能及应用对比 第2章 各类激光器的区别特点及应用三张表看懂-超米激光 2.1 固体激光器 2.2 气体激光器 2.3 化学激光器 2.4 染料激光器 2.5 半导体激光器 2.6 光纤激光器 2.7 自由电子激光器 第3章 10多种激光器的全面梳理 3.1 激光器的分类…

二叉树-31-37对称二叉树

31. 对称的二叉树 递归&#xff1a; 把原问题化成更小规模的问题&#xff0c;并且具有相同的问题性质&#xff0c;重复调用本身函数 二叉树的递归&#xff0c;是将某个节点的左子树、右子树看成一颗完整的树&#xff0c;那么对于子树的访问或者操作就是对于原树的访问或者操作…