PyOpenGL代码实战(一):创建窗口

一、前言

网络上有很多关于OpenGL的教程,但绝大多数都是C或C++的代码。本文章旨在教学如何在Python中编写OpenGL的代码。本文主要参考LearnOpenGL网站的教程,以实现一个Python版本的OpenGL代码框架。

二、前置知识

1、数学

学习PyOpenGL,你可能需要一些基础的数学知识,特别是线性代数几何学的相关知识。不用担心,你并不需要精通这些知识,只需要了解向量、矩阵、三角函数等基本概念及其运算即可。

2、计算机图形学

阅读本教程,你可能需要一定的计算机图形学基础。不过你没有学过也没有关系,在讲到相关内容时,我也会为大家简要讲解一下相应的基础理论。

3、 Python

本教程使用Python+PyOpenGL编写。你需要掌握如何编写Python代码。

三、PyOpenGL的配置

使用pip安装PyOpenGL。

pip install PyOpenGL PyOpenGL_accelerate

实际上,上述命令只是下载了 Python 与 OpenGL API的绑定,OpenGL 本身已经内置在硬件中,无需下载。

配置GLFW。关于GLFW的下载、安装以及配置,可以阅读这篇文章。注意一定要将GLFW添加到系统变量中,否则后续代码将无法运行。

GLFW 为 OpenGL 的底层 API 进行了一定的封装,提供了创建并管理窗口和 OpenGL 上下文的功能,同时还提供了处理手柄、键盘、鼠标输入的功能。

下载并配置完成后,在python中安装GLFW:

pip install glfw

在Python中导入PyOpenGL和GLFW:

from OpenGL.GL import *	# 导入PyOpenGL
import glfw				# 导入GLFW

四、创建窗口

首先,我们需要使用GLFW来创建窗口

# 初始化GLFW
if not glfw.init():
    raise RuntimeError("GLFW初始化失败!")
# 创建窗口
width, height, title = 1920, 1080, "Test"	# 窗口宽度、高度、标题
window = glfw.create_window(width, height, title, None, None)	# 暂时忽略后面两个参数
# 显示窗口
glfw.make_context_current(window)	# 通知 GLFW 将我们窗口的上下文设置为当前线程的主上下文
glfw.set_window_size_limits(window, width, height, width, height) # 使窗口大小不可修改
glViewport(0, 0, width, height) # 设置视口
glEnable(GL_CULL_FACE)  		# 开启背面剔除
glEnable(GL_DEPTH_TEST)  		# 开启深度测试

视口与 glViewport

视口是 OpenGL 的渲染窗口,前面glfw.create_window用于设置窗口的大小,而 glViewport 则定义了视口的大小。视口大小一般和窗口大小相同,但它们也可以不相等。如果视口大小小于窗口大小,多余的窗口位置将会显示为黑色。

glViewPort的前两个参数定义了视口左下角的屏幕坐标(屏幕坐标以窗口的左下角为原点),后两个参数视口的宽度和高度。

背面剔除与深度测试

背面剔除是指将用户看不到的面剔除掉,不再渲染这些资源以提高效率。深度测试是指通过深度缓冲来判断不同物体间的遮挡关系。这些内容会在之后的章节中详细介绍。

如果此时我们运行这段代码,你会发现窗口一闪而过,只出现了一瞬间就立刻消失。
在这里插入图片描述

这是因为在这段代码运行完成后,python程序终止,窗口也就消失了。为了让窗口一直显示,我们还需要创建渲染循环

在上述代码之后加上如下代码:

bgColor = (0.0, 0.0, 0.0, 1.0)
while not glfw.window_should_close(window):
    # 清空缓冲区
    glClearColor(*bgColor)	# 将窗口全部涂成背景色(注意前面的解包符号)
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)	# 清除颜色缓冲和深度缓冲
    
    # 在此处可以绘制物体
    # ...
    
    # 交换缓冲区
    glfw.swap_buffers(window)
    # 提交事件
    glfw.poll_events()
    # 检测窗口是否关闭
    if glfw.get_key(window, glfw.KEY_ESCAPE) == glfw.PRESS:
        glfw.set_window_should_close(window, True)
# 结束渲染循环后销毁窗口并终止运行
glfw.destroy_window(window)
glfw.terminate()

我们介绍一下上述代码中出现的一些函数:

glfw.window_should_close():用于判断窗口是否应该被关闭,如果用户点击了窗口右上角的关闭按钮,该函数会返回True。

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT):清除颜色缓冲和深度缓冲,具体功能会在后续章节中介绍。

glfw.swap_buffers(window):交换缓冲区。这条代码与双缓冲机制有关。

何为双缓冲机制?绘制物体需要时间,因此当我们在视口中绘制物体时,我们可以看到绘制过程,这将会造成画面的闪烁。为了解决这个问题,我们可以定义两个窗口,一个窗口显示给用户,另一个窗口则隐藏起来。渲染时,先在隐藏起来的窗口中绘制,等这一帧完全绘制完成后,交换两个窗口,将原来显示的窗口隐藏,原来隐藏的窗口则显示。这样就不会看到画面的闪烁效果了。

glfw.poll_events():提交事件。用于处理键盘、鼠标事件。在后续章节中详细介绍。

if glfw.get_key(window, glfw.KEY_ESCAPE) == glfw.PRESS:
	glfw.set_window_should_close(window, True)

glfw.get_key(window, key)用于判断键盘上某个按键当前的状态。其返回有两种可能:glfw.PRESSglfw.RELEASE,分别表示按键按下和释放。glfw.set_window_should_close(window, True or False)用于设置窗口是否应该被关闭。所以这段代码的功能是用户按下ESC按键后关闭窗口。

注意,调用glfw.set_window_should_close(window, True)后,窗口并不会立即关闭。只是之后再调用glfw.window_should_close()函数时会返回 True,此时跳出循环,运行glfw.destroy_window(window)时关闭窗口。

最后,我们得到了创建窗口的完整代码:

# 初始化GLFW
if not glfw.init():
    raise RuntimeError("GLFW初始化失败!")
# 创建窗口
width, height, title = 1920, 1080, "Test"	# 窗口宽度、高度、标题
window = glfw.create_window(width, height, title, None, None)	# 暂时忽略后面两个参数
# 显示窗口
glfw.make_context_current(window)	# 通知 GLFW 将我们窗口的上下文设置为当前线程的主上下文
glfw.set_window_size_limits(window, width, height, width, height) # 使窗口大小不可修改
glViewport(0, 0, width, height) # 设置视口
glEnable(GL_CULL_FACE)  		# 开启背面剔除
glEnable(GL_DEPTH_TEST)  		# 开启深度测试

bgColor = (0.0, 0.0, 0.0, 1.0)
while not glfw.window_should_close(window):
    # 清空缓冲区
    glClearColor(*bgColor)	# 将窗口全部涂成背景色
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)	# 清除颜色缓冲和深度缓冲
    
    # 在此处可以绘制物体
    # ...
    
    # 交换缓冲区
    glfw.swap_buffers(window)
    # 提交事件
    glfw.poll_events()
    # 检测窗口是否关闭
    if glfw.get_key(window, glfw.KEY_ESCAPE) == glfw.PRESS:
        glfw.set_window_should_close(window, True)
# 结束渲染循环后销毁窗口并终止运行
glfw.destroy_window(window)
glfw.terminate()

运行结果如下:
在这里插入图片描述

当我们点击右上角或者按下ESC时,窗口会关闭,程序会结束运行。

五、封装

我们已经成功完成了创建窗口的代码。但这些代码都写在一起,不利于后续的扩展。因此,我们可以定义窗口类,封装代码以提高可扩展性。

class Window:
    def __init__(self, width, height, title, bgColor=(0.0, 0.0, 0.0, 1.0)):
        # 初始化GLFW
        if not glfw.init():
            raise RuntimeError("GLFW初始化失败!")
        # 创建窗口
        self.width, self.height, self.title, self.bgColor = width, height, title, bgColor
        self.window = glfw.create_window(width, height, title, None, None)
        # 显示窗口
        self.show()

    def show(self):
        glfw.make_context_current(self.window)
        glfw.set_window_size_limits(self.window, self.width, self.height, self.width, self.height)
        glViewport(0, 0, self.width, self.height)
        glEnable(GL_CULL_FACE)
        glEnable(GL_DEPTH_TEST)

    def loop(self):
        while not glfw.window_should_close(self.window):
            glClearColor(*self.bgColor)
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

            # 在此处可以绘制物体
            # ...

            glfw.swap_buffers(self.window)
            glfw.poll_events()
            if glfw.get_key(self.window, glfw.KEY_ESCAPE) == glfw.PRESS:
                glfw.set_window_should_close(self.window, True)

        glfw.destroy_window(self.window)
        glfw.terminate()


if __name__ == '__main__':
    w = Window(1920, 1080, "Test")
    w.loop()

这段代码和之前的创建窗口的代码并无不同,这里不再介绍。

六、结语

本文介绍了如何使用PyOpenGL创建窗口。在下一节中,我们将学习如何在窗口中绘制图形。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值