【OpenGL实践08】现代渲染管线在GLUT和Pygame和Qt.QOpenGLWidget上各自的实现代码

Qt.QOpenGLWidget进行现代渲染管线实验效果
在这里插入图片描述

一、说明

   据说QOpenGLWidget是用来取代QGLWidget的继承者,我们试图将GLUT上的旧代码改成QOpenGLWidget,本以为差别不大,轻易搞定,经实践发现要付出极大努力才能完成。经多次实验发现GLUT窗口、Pygame窗口、Qt.QOpenGLWidget上的实现方法无法兼容,甚至GL方法都不同,经好多天的百度查询,终于总结出各自的实现方法。作文以记录。

二、窗口和后台GL库的关系

   一种窗口是否能支持OpenGL,根本的一条设计是:窗口下对应的显示buffer能够指向GPU的buffer,这实际上是个底层驱动的问题。
   而检验这种关联关系是有方法:

       print("1 OK?", bool(gl.glGenBuffers))

   该方法能确定是否有窗口映射到GPUbuffer上面。这是渲染的第一个步骤,我将上述检测语句到处放置,终于找到最恰当的位置,在该处设置gl.glGenBuffers语句,用以生成VOB。

2.1 盲目使用glGenBuffers的后果

   我们经常不加检验地使用glGenBuffers函数:

global VBO
VBO = gl.glGenBuffers(1)

   会出现下列提示:

raise error.NullFunctionError( OpenGL.error.NullFunctionError: Attempt to call an undefined function glGenBuffers, check for bool(glGenBuffers) before calling

解决方法
   需要完整实现窗口,GL准备,一般需要先运行 glutInit、glutInitDisplayMode 和 glutCreateWindow。上述错误原因是OpenGL初始化尚未完成。这一点首先明白了!

2.2 在GLUT窗口初始化的检验

   在GLUT实现buffer映射时候,需要有三步铺垫,

  • glutInit、
  • glutInitDisplayMode 和
  • glutCreateWindow
       我们执行下面测试代码:
    def __init__(self):self.number = 0glutInit([])print("1 OK?",bool(glGenBuffers))glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA)  # 显示模式 双缓存glutInitWindowPosition(100, 100)  # 窗口位置glutInitWindowSize(500, 500)  # 窗口大小print("2 OK?", bool(glGenBuffers))glutCreateWindow("sanjiao")  # 创建窗口print("3 OK?",bool(glGenBuffers))glutInitContextVersion(4, 3)  # 为了兼容glutInitContextProfile(GLUT_CORE_PROFILE)  # 为了兼容glutDisplayFunc(self.Draw)  # 回调函数glClearColor(0.0, 0.0, 0.0, 1.0)print("4 OK?",bool(glGenBuffers))self.CreateBuffer()self.Compile_Shader()glutIdleFunc(self.Draw)glutMainLoop()

   执行后提示:

1 OK? False

2 OK? False

3 OK? True

4 OK? True

   显然,我们验证了,只有

  • glutInit([])、
  • glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA)
  • glutCreateWindow(“sanjiao”) # 创建窗口
       以上三个方面满足了,glGenBuffers函数的“使能”开关被触发。

2.3 在pygame窗口初始化的检验

   在pygame也需要初始化gl函数库,其代码如下:

    def _set_up_pygame(self) -> None:"""Initialize and configure pygame."""pg.init()pg.display.gl_set_attribute(pg.GL_CONTEXT_MAJOR_VERSION, 3)pg.display.gl_set_attribute(pg.GL_CONTEXT_MINOR_VERSION, 3)pg.display.gl_set_attribute(pg.GL_CONTEXT_PROFILE_MASK, pg.GL_CONTEXT_PROFILE_CORE)pg.display.set_mode((640,480), pg.OPENGL|pg.DOUBLEBUF)

   同样,我们总结它的四个步骤是:

  • 初始化gl库
  • 初始化上下文
  • 设置显示模式
  • 窗口生成
       显然,与GLUT使用方法不同,而Qt5的方法更有不同。

2.4 在Qt.QOpenGLWidget窗下的初始化动作

   在Qt.QOpenGLWidget中嵌入了GL函数库,并且,不需要:

import OpenGL.GL as gl

   而是:

self.gl = self.context().versionFunctions(version_profile)

   因此,初始化GL库的代码如下:

    def initializeGL(self):version_profile = QOpenGLVersionProfile()version_profile.setVersion(2, 0)self.gl = self.context().versionFunctions(version_profile)self.gl.initializeOpenGLFunctions()

   除了上述Qt.QOpenGLWidget初始化方法外,还有一种方法,本篇着重介绍另一个方法。

三、另一种Qt.QOpenGLWidget的窗口设计

   因为这里用到着色器,因此,除了检验glGenBuffers,要检验glCreateShader,否则将再次出错。因此,下面将需要两个检验,查看函数是否可调用,分别是:
print(“create Shander”, bool(gl.glCreateShader))
print(“1 OK?”, bool(gl.glGenBuffers))

3.1 将Qt.QOpenGLWidget作为一般部件

   有两个办法用Qt.QOpenGLWidget类,其一,将其看成QmainWindow的部件,以函数指针赋值的方法指定三个虚函数。如下图所示:

在这里插入图片描述
   具体代码少有复杂,有兴趣的读者可以下载代码研究。

3.2 关键代码展现

3.2.1 基本库包含


from PyQt5 import QtCore
import OpenGL.GL as gl
import OpenGL.GLU as glu
import OpenGL.GLUT as glut
import sys
import os
import numpy as np
from PyQt5 import QtWidgets as qWidget
import math
from OpenGL.GL.shaders import compileProgram,compileShader

3.2.2 着色器编译

   这个着色器代码较为干净利索,建议以后编译着色器就照此法较为简单。

def create_shader(vertex_filepath: str, fragment_filepath: str) -> int:"""Compile and link shader modules to make a shader program.Parameters:vertex_filepath: path to the text file storing the vertexsource codefragment_filepath: path to the text file storing thefragment source codeReturns:A handle to the created shader program"""with open(vertex_filepath, 'r') as f:vertex_src = f.readlines()with open(fragment_filepath, 'r') as f:fragment_src = f.readlines()shader = compileProgram(compileShader(vertex_src, gl.GL_VERTEX_SHADER),compileShader(fragment_src, gl.GL_FRAGMENT_SHADER))return shader

3.2.3 paintGL函数

   这里的关键函数是:gl.glUseProgram(self.Shader_Program)还有 self.loadScene(),此函数自己写的是关于相机的观察矩阵设定。其次, gl.glFlush() 不能有,加了反而黑屏,大概是QOpenGLWidget已经有了,不能重复。

def paintGL(self):self.loadScene()gl.glClear(gl.GL_COLOR_BUFFER_BIT)# gl.glPointSize(self.psize)gl.glLineWidth(self.psize)gl.glEnableVertexAttribArray(0)gl.glBindBuffer(gl.GL_ARRAY_BUFFER, self.VBO)# gl.glVertexAttribPointer(0, 3, gl.GL_FLOAT, gl.GL_FALSE, 12, None) #这里的None不能写为0gl.glVertexAttribPointer(0, 3, gl.GL_FLOAT, gl.GL_FALSE, 12, gl.ctypes.c_void_p(0))# gl.glDrawArrays(gl.GL_POINTS, 0, 401)gl.glUseProgram(self.Shader_Program)gl.glDrawArrays(gl.GL_LINE_STRIP, 0, 401)gl.glDisableVertexAttribArray(0)  #解析数据 例如一个矩阵里含有 位置 、颜色、多种信息# gl.glFlush()  # 此处不需要

3.2.3 initializeGL函数

此处关键是数据准备,费事费力。

def initializeGL(self):print("\033[4;30;102m INITIALIZE GL \033[0m")gl.glEnable(gl.GL_BLEND)gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA)gl.glEnable(gl.GL_DEPTH_TEST)self.prepareData()

3.2.4 resizeble的替代函数

def loadScene(self):gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)gl.glMatrixMode(gl.GL_PROJECTION)gl.glLoadIdentity()x, y, width, height = gl.glGetDoublev(gl.GL_VIEWPORT)glu.gluPerspective(45,  # field of view in degreeswidth / float(height or 1),  # aspect ratio3,  # near clipping plane10,  # far clipping plane)gl.glMatrixMode(gl.GL_MODELVIEW)gl.glLoadIdentity()glu.gluLookAt(0, 0, 4, 0, 0,0, 0, 1, 0)

``

3.2.5 定时器

   为了定时刷屏,需要加定时器,在GLUT里面叫做 glutMainLoop();或者搞个死循环也可(不够酷!)

        glut.glutInit([])timer = QtCore.QTimer(self)timer.setInterval(20) # period, in millisecondstimer.timeout.connect(self.openGLWidget.update)timer.start()

   好了,这些地方均为要点。

3.3 实验效果

   见本文前部。

四、结论

   Qt.QOpenGLWidget编写渲染管线并不容易,需要处处理解正确,而且,需要对隐含的限定做种种尝试,但是一旦趟开一个路子,以后照喵画虎就成了可能。若需要完整代码,请从本文附加资源下载,非常值得收藏(我认为)。

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

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

相关文章

编译原理实验课

本人没咋学编译原理,能力有限,写的不好轻点喷,大佬路过的话,那你就路过就好 东大编译原理实验课原题,22年 1. 基本题:简单的扫描器设计 【问题描述】 熟悉并实现一个简单的扫描器,设计扫描器…

nosql数据库 redis

一、介绍 1、redis与mysql的区别: Redis是一种基于键值对的内存数据库,数据存储在内存中,因此读写速度非常快。它支持多种数据结构,如字符串、哈希、列表等。 MySQL是一种关系型数据库,数据以表格的形式组织存储在磁…

探索 Python 的动态类型系统:变量引用、不可变性及高效内存管理与垃圾回收机制的深入分析

文章目录 1. 动态类型及其内存管理解析1.1 变量与对象的引用关系1.2 对象的不可变性和内存地址的变化 2. 垃圾回收与内存优化策略2.1 动态内存分配的基础2.2 Python 的垃圾回收 Python作为一种流行的高级编程语言,以其代码的易读性和简洁性著称。尤其是它的动态类型…

泛私域新引擎:小程序AI智能名片S2B2C商城的分销式导购策略与案例剖析

在数字化浪潮的推动下,小程序AI智能名片S2B2C商城以其独特的分销式导购能力,逐渐在泛私域的特殊场景中占据了一席之地。这种模式不仅打破了传统线上线下的界限,更通过以人为核心的营销方式,实现了利益驱动的深度分销。 分销式导购…

Git学习笔记(四)远程仓库

根据前面几篇文章的介绍,在本地使用Git基本不成问题了,常用的基本命令和一些基本概念基本也介绍完毕了。这一张主要讲讲远程仓库的创建和使用。 概念 其实在前面第一篇文章中,我们就简单介绍过远程仓库,它其实就是一个托管在远程服…

yolo-驾驶行为监测:驾驶分心检测-抽烟打电话检测

在现代交通环境中,随着汽车技术的不断进步和智能驾驶辅助系统的普及,驾驶安全成为了公众关注的焦点之一 。 分心驾驶,尤其是抽烟、打电话等行为,是导致交通事故频发的重要因素。为了解决这一问题,研究人员和工程师们…

19-ESP32-S3外设IIC

ESP32-S3的IIC 引言 ESP32-S3是一款集成了Wi-Fi和蓝牙功能的低成本、多功能微控制器。在这篇博客中,我们将详细介绍ESP32-S3的IIC(Inter-Integrated Circuit)接口,也被称为I2C。 IIC简介 IIC是一种串行、同步、多设备、半双工…

CTF网络安全大赛详情

网络安全已成为现代社会的一个关键挑战,随着互联网技术的飞速发展,从个人隐私保护到国家安全,网络安全的重要性日益突显。为了应对这一挑战,CTF(Capture The Flag,中文:夺旗赛)应运而…

openEuler-22.03下载、安装

一、下载 下载地址:openEuler下载 | 欧拉系统ISO镜像 | openEuler社区官网 下载版本:openEuler-22.03-LTS-x86_64-dvd.iso 二、安装 配置完后开启虚拟机 设置完后,重启虚拟机 设置桥接模式的网络 cd /etc/sysconfig/network-scripts/ vi if…

LeetCode 2739. 总行驶距离

题目链接https://leetcode.cn/problems/total-distance-traveled/?envTypedaily-question&envId2024-04-25 简单题,看代码思考一下即可理解 class Solution {public int distanceTraveled(int mainTank, int additionalTank) {int res 0;while (mainTank >…

RK3568 学习笔记 : u-boot 千兆网络无法 ping 通PC问题的解决

前言 开发板型号: 【正点原子】 的 RK3568 开发板 使用 虚拟机 ubuntu 20.04 收到单独 编译 RK3568 u-boot 【问题】u-boot 千兆网络无法ping 通?Linux 下千兆网络正常,说明:开发板硬件正常 u-boot 下网络如果通了,…

【高频】基于GBDT-FM模型的level-2高频数据实证研究(二)

【高频】基于GBDT-FM模型的level-2高频数据实证研究(二) 原创 Yud. 2AMquant 2024-04-04 11:30 广东 上一篇中初步提及了Level2数据中常见变量指标的构建方式,以及其带来的价格冲击。此篇将使用GBDT-LM模型对短程价格走势进行简单预测。 ps…

Nginx下载安装,什么是nginx,什么是反向代理,Windows下、linux下安装nginx(保姆级教程)

文章目录 一、Nginx简介为什么要使用NginxNginx的特点Nginx的相关概念正向代理反向代理动静分离负载均衡 二、Nginx安装1. Windows安装2. Linux安装 一、Nginx简介 Nginx 是一个高性能的 HTTP(静态资源服务器) 和 反向代理 Web 服务器。 为什么要使用N…

AI-数学-高中-44导数的运算法则

原作者视频:【导数】【一数辞典】3导数的运算法则(略难)_哔哩哔哩_bilibili 三种求导表达方式一样的,中间的比较常用: 链式法则:从外向内:

如何部署Tensorrtx

因为模型跑起来太慢了,所以想要运用tensorrtx进行加速。但是这个是有难度的,且网络上的教程大多写的不是很好。我将以一个新人的视角,从头开始部署基于yolov5的tensorrtx加速。 知识补充:TensorRT和TensorRTX的区别 tensorRT是英…

统一SQL 支持Oracle cast函数转换

统一SQL介绍 https://www.light-pg.com/docs/LTSQL/current/index.html 源和目标 源数据库:Oracle 目标数据库:Postgresql,TDSQL-MySQL,达梦8,LightDB-Oracle 操作目标 在Oracle中,cast函数允许将一种…

yolov8 区域声光报警+计数

yolov8 区域报警计数 1. 基础2. 报警功能2. 1声音报警代码2. 2画面显示报警代码 3. 完整代码4. 源码 1. 基础 本项目是在 yolov8 区域多类别计数 的基础上实现的,具体区域计数原理可见上边文章 2. 报警功能 设置一个区域region_points,当行人这一类别…

链栈的基本操作(c语言实现)

栈的概念 栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。 栈的结构 定义栈…

JVM虚拟机监控及性能调优实战

目录 jvisualvm介绍 1. jvisualvm是JDK自带的可以远程监控内存,跟踪垃圾回收,执行时内存,CPU/线程分析,生成堆快照等的工具。 2. jvisualvm是从JDK1.6开始被继承到JDK中的。jvisualvm使用 jvisualvm监控远程服务器 开启远程监控…

智能家居—ESP32开发环境搭建

相关文章 毕业设计——基于ESP32的智能家居系统(语音识别、APP控制) 智能家居—ESP32开发环境搭建 一、下载安装二、验证三、资料获取 一、下载安装 下载安装 vscode 安装插件 创建工程 二、验证 写一个简单的函数来验证一下功能 void setup() {// put your setup c…