【OpenGL教程4】高级 OpenGL实现:使用 PyGame 和 PyOpenGL混合编程

这篇具有很好参考价值的文章主要介绍了【OpenGL教程4】高级 OpenGL实现:使用 PyGame 和 PyOpenGL混合编程。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、介绍

   继上一篇文章《通过 Python 了解 OpenGL》中我们为进一步学习奠定了基础之后,我们可以使用PyGame和PyOpenGL进入OpenGL。

   PyOpenGL 是用作 Python 和 OpenGL API 之间桥梁的标准化库,PyGame 是用于用 Python 制作游戏的标准化库。它提供了内置的方便的图形和音频库,我们将在本文末尾使用它来更轻松地呈现结果。

   正如上一篇文章中提到的,OpenGL 非常古老,因此您不会在网上找到很多关于如何正确使用它和理解它的教程,因为所有顶尖技术都已经深入了解新技术。

   在本文中,我们将讨论您需要了解的几个基本主题:

  • 使用 PyGame 初始化项目
  • 绘制对象
  • 迭代动画
  • 利用变换矩阵
  • 多重转换执行
  • 实施例

二、使用 PyGame 初始化项目

   首先,如果您还没有安装 PyGame 和 PyOpenGL,我们需要安装:

$ python3 -m pip install -U pygame --user
$ python3 -m pip install PyOpenGL PyOpenGL_accelerate

   注意:您可以在之前的 OpenGL 文章中找到更详细的安装信息。

   如果您遇到安装问题,PyGame 的“入门”部分可能是一个不错的选择。

   由于向您卸载 3 本书的图形理论是没有意义的,因此我们将使用 PyGame 库来为我们提供一个良好的开端。它本质上只会缩短从项目初始化到实际建模和动画的过程。

   首先,我们需要从 OpenGL 和 PyGame 导入所有必需的内容:

import pygame as pg
from pygame.locals import *

from OpenGL.GL import *
from OpenGL.GLU import *

   接下来,我们进行初始化:

pg.init()
windowSize = (1920,1080)
pg.display.set_mode(display, DOUBLEBUF|OPENGL)

   虽然初始化只有三行代码,但每行代码至少都值得一个简单的解释:

pg.init():所有 PyGame 模块的初始化 - 这个函数是天赐之物
windowSize = (1920, 1080):定义固定窗口大小
pg.display.set_mode(display, DOUBLEBUF|OPENGL):在这里,我们指定我们将使用带有双缓冲的OpenGL

   双缓冲意味着在任何给定时间都有两幅图像 - 一幅我们可以看到,另一幅我们可以根据需要进行转换。当两个缓冲区交换时,我们可以看到由转换引起的实际变化。

   既然我们已经设置了视口,接下来我们需要指定我们将看到的内容,或者更确切地说,“相机”将放置在哪里,以及它可以看到的距离和宽度。

   这被称为截锥体- 它只是一个截断的金字塔,在视觉上代表相机的视线(它能看到和不能看到的东西)。

   平截头体由 4 个关键参数定义:

  • FOV(Field of View视野):角度(以度为单位)
  • The Aspect Ratio(纵横比):定义为宽度和高度的比率
  • The z coordinate of the near Clipping Plane(近剪裁平面的z坐标):最小绘制距离
  • The z coordinate of the far Clipping Plane(远剪裁平面的z坐标):最大绘制距离

   因此,让我们继续使用 OpenGL C 代码来实现考虑这些参数的相机:

void gluPerspective(GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar);
gluPerspective(60, (display[0]/display[1]), 0.1, 100.0)

   为了更好地理解平截头体的工作原理,这里有一张参考图:
pygame opengl,3D图形渲染和OpenGL编程,pygame,python,开发语言

   使用近平面和远平面以获得更好的性能。实际上,渲染我们视野之外的任何东西都是对硬件性能的浪费,而硬件性能本来可以用来渲染我们实际看到的东西。

   因此,玩家看不到的所有内容都会隐式存储在内存中,即使它在视觉上并不存在。这是一个精彩的视频,展示了仅在视锥体内渲染的样子。

三、绘制对象图

   完成此设置后,我想我们会问自己同样的问题:

   好吧,这一切都很好,但我如何制作一个立体图呢?

   嗯…有点难。OpenGL 对象中的每个模型都存储为一组顶点及其关系(哪些顶点相互连接)。因此,从理论上讲,如果您知道用于绘制超级歼星舰的每个点的位置,您很可能会画出一艘!

   我们可以通过几种方法在 OpenGL 中对对象进行建模:

  1. 使用顶点进行绘制,根据 OpenGL 如何解释这些顶点,我们可以使用以下命令进行绘制:
  • point:就像不以任何方式连接的字面上的点一样
  • lines:每对顶点构造一条连接线
  • 三角形:每三个顶点构成一个三角形
  • 四边形:每四个顶点构成一个四边形
  • 多边形:你明白了
  • 还有很多…
  1. 使用 OpenGL 贡献者精心建模的内置形状和对象进行绘图
  2. 导入已经完全建模的对象
       因此,例如要绘制一个立方体,我们首先需要定义它的顶点:
cubeVertices = ((1,1,1),(1,1,-1),(1,-1,-1),(1,-1,1),(-1,1,1),(-1,-1,-1),(-1,-1,1),(-1, 1,-1))

   画出立方体:
pygame opengl,3D图形渲染和OpenGL编程,pygame,python,开发语言

   然后,我们需要定义它们是如何连接的。如果我们想制作一个线立方体,我们需要定义立方体的边缘:

cubeEdges = ((0,1),(0,3),(0,4),(1,2),(1,7),(2,5),(2,3),(3,6),(4,6),(4,7),(5,6),(5,7))

   这非常直观 - 该点0有一条边1, , 3, 和4。该点1有一条边,其中有点3、5、 和7,依此类推。

   如果我们想要制作一个实心立方体,那么我们需要定义立方体的四边形:

cubeQuads = ((0,3,6,4),(2,5,6,3),(1,2,5,7),(1,0,4,7),(7,4,6,5),(2,3,0,1))

   这也很直观 - 要在立方体的顶部制作一个四边形,我们需要为点0、3、6和之间的所有内容“着色” 4。

   请记住,我们将顶点标记为定义它们的数组的索引是有实际原因的。这使得编写连接它们的代码变得非常容易。

   以下函数用于绘制有线立方体:

def wireCube():
    glBegin(GL_LINES)
    for cubeEdge in cubeEdges:
        for cubeVertex in cubeEdge:
            glVertex3fv(cubeVertices[cubeVertex])
    glEnd()

   glBegin()是一个函数,指示我们将在下面的代码中定义图元的顶点。当我们定义完原语后,我们使用该函数glEnd()。

   GL_LINES是一个宏,指示我们将绘制线条。

   glVertex3fv()是一个定义空间顶点的函数,这个函数有几个版本,所以为了清楚起见,让我们看看名称是如何构造的:

glVertex:定义顶点的函数
glVertex3:使用 3 个坐标定义顶点的函数
glVertex3f:使用 3 个坐标类型定义顶点的函数GLfloat
glVertex3fv:使用 3 个类型坐标定义顶点的函数,GLfloat这些坐标放入向量

   (元组)内(另一种方法是glVertex3fl使用参数列表而不是向量)
按照类似的逻辑,以下函数用于绘制实心立方体:

def solidCube():
    glBegin(GL_QUADS)
    for cubeQuad in cubeQuads:
        for cubeVertex in cubeQuad:
            glVertex3fv(cubeVertices[cubeVertex])
    glEnd()

四、迭代动画

   为了使我们的程序“可杀死”,我们需要插入以下代码片段:

for event in pg.event.get():
    if event.type == pg.QUIT:
        pg.quit()
        quit()

   它基本上只是一个滚动浏览 PyGame 事件的监听器,如果它检测到我们单击了“终止窗口”按钮,它就会退出应用程序。

   我们将在以后的文章中介绍更多 PyGame 的事件 - 立即介绍这个事件是因为用户和您自己每次想要退出应用程序时都必须启动任务管理器,这会让他们感到非常不舒服。

   在此示例中,我们将使用双缓冲,这意味着我们将使用两个缓冲区(您可以将它们视为绘图的画布),它们将以固定间隔交换并给出运动的错觉。

   知道了这一点,我们的代码必须具有以下模式:

handleEvents()
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
doTransformationsAndDrawing()
pg.display.flip()
pg.time.wait(1)
  • glClear:清除指定缓冲区(画布)的函数,在本例中为颜色缓冲区(包含用于绘制生成对象的颜色信息)和深度缓冲区(存储对象的前后关系的缓冲区)所有生成的对象)。
  • pg.display.flip():使用活动缓冲区内容更新窗口的函数
  • pg.time.wait(1):暂停程序一段时间的功能
  • glClear必须使用,因为如果我们不使用它,我们将只是在已经绘制的画布上绘画,在这种情况下,这是我们的屏幕,我们最终会变得一团糟。

   接下来,如果我们想像动画一样不断更新屏幕,我们必须将所有代码放入一个while循环中,在该循环中:

  • 处理事件(在本例中,只是退出)
  • 清除颜色和深度缓冲区,以便可以再次绘制它们
  • 变换和绘制对象
  • 更新屏幕
  • 转到 1。
       代码应该看起来像这样:
while True:
    handleEvents()
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
    doTransformationsAndDrawing()
    pg.display.flip()
    pg.time.wait(1)

五、利用变换矩阵

   在上一篇文章中,我们解释了理论上我们需要如何构建具有引用点的转换。

OpenGL 的工作方式相同,如以下代码所示:

glTranslatef(1,1,1)
glRotatef(30,0,0,1)
glTranslatef(-1,-1,-1)

   在此示例中,我们在xy 平面上进行了z 轴旋转,旋转中心为30 度。(1,1,1)

   如果这些术语听起来有点令人困惑,让我们回顾一下:

  1. z 轴旋转意味着我们绕 z 轴旋转
    这只是意味着我们正在用 3D 空间逼近 2D 平面,整个变换基本上就像围绕 2D 空间中的参考点进行正常旋转。

  2. 我们通过将整个 3D 空间压缩到一个具有xy 平面的z=0平面(我们以各种方式消除 z 参数)

  3. 旋转中心是我们将围绕其旋转给定对象的顶点(默认旋转中心是原点顶点(0,0,0))
       但有一个问题 - OpenGL 通过不断记住和修改一个全局变换矩阵来理解上面的代码。

   因此,当您用 OpenGL 编写内容时,您所说的是:

# This part of the code is not translated
# transformation matrix = E (neutral)
glTranslatef(1,1,1)
# transformation matrix = TxE
# ALL OBJECTS FROM NOW ON ARE TRANSLATED BY (1,1,1)

   正如您可能想象的那样,这提出了一个巨大的问题,因为有时我们希望对单个对象而不是整个源代码进行转换。这是低级 OpenGL 中出现错误的一个非常常见的原因。

   为了解决 OpenGL 的这个有问题的功能,我们提出了推入和弹出变换矩阵 -glPushMatrix()并且glPopMatrix():

# Transformation matrix is T1 before this block of code
glPushMatrix()
glTranslatef(1,0,0)
generateObject() # This object is translated
glPopMatrix()
generateSecondObject() # This object isn't translated

   它们遵循简单的后进先出(LIFO) 原则。当我们希望对矩阵进行转换时,我们首先复制它,然后将其推入变换矩阵堆栈的顶部。

   换句话说,它通过创建一个本地矩阵来隔离我们在该块中执行的所有转换,我们可以在完成后废弃该矩阵。

   一旦对象被平移,我们就会从堆栈中弹出变换矩阵,而其余矩阵保持不变。

六、多重转换执行

   在 OpenGL 中,如前所述,变换被添加到变换矩阵堆栈顶部的活动变换矩阵中。

   这意味着转换以相反的顺序执行。例如:

######### First example ##########
glTranslatef(-1,0,0)
glRotatef(30,0,0,1)
drawObject1()
##################################

######## Second Example #########
glRotatef(30,0,0,1)
glTranslatef(-1,0,0)
drawObject2()
#################################

   在此示例中,Object1 首先旋转,然后平移,Object2 首先平移,然后旋转。最后两个概念不会在实现示例中使用,但将在本系列的下一篇文章中实际使用。

七、实施例

下面的代码在屏幕上绘制一个实心立方体,并围绕(1,1,1)矢量连续旋转 1 度。并且可以很容易地对其进行修改,通过将 替换为 来绘制线cubeQuads立方体cubeEdges:

import pygame as pg
from pygame.locals import *

from OpenGL.GL import *
from OpenGL.GLU import *

cubeVertices = ((1,1,1),(1,1,-1),(1,-1,-1),(1,-1,1),(-1,1,1),(-1,-1,-1),(-1,-1,1),(-1,1,-1))
cubeEdges = ((0,1),(0,3),(0,4),(1,2),(1,7),(2,5),(2,3),(3,6),(4,6),(4,7),(5,6),(5,7))
cubeQuads = ((0,3,6,4),(2,5,6,3),(1,2,5,7),(1,0,4,7),(7,4,6,5),(2,3,0,1))

def wireCube():
    glBegin(GL_LINES)
    for cubeEdge in cubeEdges:
        for cubeVertex in cubeEdge:
            glVertex3fv(cubeVertices[cubeVertex])
    glEnd()

def solidCube():
    glBegin(GL_QUADS)
    for cubeQuad in cubeQuads:
        for cubeVertex in cubeQuad:
            glVertex3fv(cubeVertices[cubeVertex])
    glEnd()

def main():
    pg.init()
    display = (1680, 1050)
    pg.display.set_mode(display, DOUBLEBUF|OPENGL)

    gluPerspective(45, (display[0]/display[1]), 0.1, 50.0)

    glTranslatef(0.0, 0.0, -5)

    while True:
        for event in pg.event.get():
            if event.type == pg.QUIT:
                pg.quit()
                quit()

        glRotatef(1, 1, 1, 1)
        glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
        solidCube()
        #wireCube()
        pg.display.flip()
        pg.time.wait(10)

if __name__ == "__main__":
    main()

运行这段代码,会弹出一个 PyGame 窗口,渲染立方体动画:

pygame opengl,3D图形渲染和OpenGL编程,pygame,python,开发语言

八、结论

   关于 OpenGL还有很多东西需要学习 - 光照、纹理、高级曲面建模、复合模块化动画等等。但不用担心,所有这些都将在以下文章中进行解释。文章来源地址https://www.toymoban.com/news/detail-855333.html

到了这里,关于【OpenGL教程4】高级 OpenGL实现:使用 PyGame 和 PyOpenGL混合编程的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Pygame 基础教程13: 使用 精灵(Sprite) 实现 按钮、开关按钮 和 复选框

    原文链接:https://xiets.blog.csdn.net/article/details/131395471 版权声明:原创文章禁止转载 专栏目录:Pygame 专栏(总目录) Pygame 中并没有按钮、开关按钮、复选框 等基础交互组件,但实际游戏开发中又经常用到。例如用于点击按钮开始游戏的「开始按钮」,用于开启/关闭背景音乐

    2024年02月04日
    浏览(54)
  • Python pygame(GUI编程)模块最完整教程(4)

    上一篇文章: Python pygame(GUI编程)模块最完整教程(3)_pygame教程pdf_Python-ZZY的博客-CSDN博客 总目录: README.md · Python-ZZY/Python-Pygame最完整教程 - Gitee.com 参考资料:pygame.draw — pygame-ce v2.4.0 documentation draw模块提供了一些直接在表面上绘制常用图形的操作,如绘制矩形、圆形、多

    2024年02月14日
    浏览(57)
  • Python pygame(GUI编程)模块最完整教程(1)

    提示:下滑文章左侧可以查看目录!本教程分为多篇,总目录如下。  总目录: README.md · Python-ZZY/Python-Pygame最完整教程 - Gitee.com pygame是python中一个流行的GUI编程模块,是专门为了开发游戏而设计的。这是一个第三方模块,是SDL和Python的接口。 pygame的最新官网是:https://pyg

    2024年02月02日
    浏览(45)
  • Python pygame(GUI编程)模块最完整教程(2)

    上一篇文章: Python pygame(GUI编程)模块最完整教程(1)_pygame模块详解_Python-ZZY的博客-CSDN博客 总目录: README.md · Python-ZZY/Python-Pygame最完整教程 - Gitee.com 参考资料:pygame.font — pygame-ce v2.4.0 documentation pygame中绘制文字的第一步是载入字体。载入字体的方式通常有两种,第一种是

    2024年02月04日
    浏览(47)
  • Python pygame(GUI编程)模块最完整教程(5)

    上一篇文章:Python pygame(GUI编程)模块最完整教程(4)_pygame绘制圆角矩形_Python-ZZY的博客-CSDN博客   总目录: README.md · Python-ZZY/Python-Pygame最完整教程 - Gitee.com 参考资料:pygame.display — pygame-ce v2.3.1 documentation pg.display.set_mode方法用于创建窗口。 size参数指定窗口的大小,一般是

    2024年02月14日
    浏览(50)
  • Python 和 C++ 混合编程:pybind11 使用

    我们通常在 Python 上进行算法开发,因为 Python 编程方便,易于快速验证算法。而在验证算法正确后,如果对运行效率有更高要求的话,则会将计算密集的模块使用 C/C++ 重新实现,来达到代码运行效率加速的效果。所以,这就涉及到了 Python 和 C++ 混合编程,而在这方面 pybin

    2024年02月08日
    浏览(45)
  • 中文编程开发语言工具编程实际案例:台球棋牌混合计时计费软件使用的编程构件说明

    台球棋牌混合计时计费软件使用的编程构件说明 上图说明:该软件可以用于桌球和棋牌同时计时计费,在没有开台的时候,图片是处于等待状态,这使用编程工具中的固定图像构件,在正在计时的时候,图片自动变换为 进行中的状态,在编程中可以动态读取图片。 上图说明

    2024年02月08日
    浏览(54)
  • 突破编程_C++_高级教程(模板编程的基础知识)

    C++ 的模板编程是一种编程技术,它允许程序员编写处理不同类型数据的通用代码。通过使用模板,可以创建与特定数据类型无关的函数或类,这些函数或类在编译时可以根据需要生成特定数据类型的版本。这增加了代码的复用性、灵活性和类型安全性。 从本质上来说, C++

    2024年02月19日
    浏览(44)
  • Pygame 教程(6):使用精灵

    本章,你将学习如何使用 Pygame 中的精灵。 上一章:监测游戏时间 当游戏对象变得繁多的时候,把所有游戏对象的处理存放在一个个函数中,虽然是一种可行的方法,但是会使代码逻辑比较混乱,难以维护。这时, 精灵(sprite) 应运而生。使用精灵,可以将游戏对象封装到

    2024年02月03日
    浏览(39)
  • Pygame使用教程

    Pygame是一个Python模块,专门用于游戏开发。它包含了图像、声音、键盘、鼠标、甚至是游戏手柄等游戏开发中必须用到的一切。 下面是Pygame的使用教程: 安装Pygame模块 在安装Pygame之前,需要先安装Python。可以到Python官网下载最新的Python安装包。安装好Python后,可以在命令行

    2024年02月06日
    浏览(30)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包