PyOpenGL如何绘制2D图像?纹理加载代码后续如何实现?
一、纹理加载代码评价
你的代码核心逻辑是对的,其中tobytes传入stride为-1翻转行序的操作刚好适配OpenGL纹理坐标的原点方向,是正确的写法,但存在3个容易出问题的点:
- 通道处理有隐患:你定义了
mode变量但完全没使用,强制调用tobytes("raw", "RGBA", 0, -1)把所有图像按RGBA格式读数据,如果加载的是RGB格式(无alpha通道)的图片,会出现通道错位、花屏的问题。稳妥的做法是加载图片后先统一调用tex = tex.convert("RGBA")转成RGBA模式,再读数据,不用额外判断原格式。 - 参数接口使用不规范:
glTexParameterf是浮点型参数设置接口,你传入的GL_REPEAT、GL_LINEAR这些都是整型枚举值,虽然大部分驱动会做隐式转换,但部分环境下可能出现参数失效的问题,统一换成glTexParameteri更稳妥。 - 过滤策略适配问题:你开启了mipmap和三线性过滤,如果是做像素精确对齐的2D UI绘制,这个配置会让图像在非整数缩放时发糊,不需要mipmap的话可以把
GL_TEXTURE_MIN_FILTER改成GL_LINEAR,同时去掉glGenerateMipmap调用即可。
修正后的纹理加载代码参考:
texture = glGenTextures(1) glBindTexture(GL_TEXTURE_2D, texture) # 替换f为i,设置整型参数 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) # 不需要mipmap就用这个 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) tex = Image.open("Resources/bg55.png").convert("RGBA") # 统一转RGBA,避免通道问题 data = tex.tobytes("raw", "RGBA", 0, -1) glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex.width, tex.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data) # 不用mipmap就注释掉下面这行 # glGenerateMipmap(GL_TEXTURE_2D)
二、2D图像绘制的后续必做步骤
2D绘制本质是在正交投影下绘制带纹理的矩形,不需要3D相关的透视、光照逻辑,按以下步骤走即可:
配置基础渲染状态
3D场景默认用透视投影,2D绘制要切换为正射投影,直接把坐标映射到屏幕像素即可。比如窗口宽度为win_w、高度为win_h,可以把投影坐标系原点设在左下角,x范围0win_w、y范围0win_h,深度范围-1~1,这样传顶点坐标时直接用屏幕像素坐标即可。
如果不需要深度遮挡,直接调用glDisable(GL_DEPTH_TEST)关闭深度测试;如果绘制带透明通道的图像,还要开启混合:glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA),否则透明区域会显示为黑色。编写极简着色器程序
不需要复杂的光照、变换逻辑,两个着色器就够用:
- 顶点着色器:接收顶点位置、纹理坐标两个属性,乘投影矩阵转换到裁剪空间,把纹理坐标传递给片元着色器。
- 片元着色器:绑定纹理单元,对传入的纹理坐标采样,直接输出采样得到的颜色值。
准备矩形顶点数据
2D图像的载体是一个和图像尺寸匹配的矩形(由2个三角形共6个顶点组成),每个顶点包含两个属性:2维位置坐标、2维纹理坐标。比如要把图像绘制在屏幕坐标(draw_x, draw_y)位置,宽img_w高img_h,四个顶点的位置对应矩形四个角的像素坐标,纹理坐标对应(0,0)到(1,1)的范围即可。把顶点数据存入VAO/VBO,不用每帧重复生成。执行每帧绘制逻辑
渲染循环里按顺序执行以下操作即可:
- 调用
glClear清空颜色缓冲、深度缓冲 - 绑定着色器程序,把提前算好的正交投影矩阵传入着色器的uniform变量
- 激活对应的纹理单元,绑定之前加载好的纹理
- 绑定存储顶点数据的VAO,调用
glDrawArrays(GL_TRIANGLES, 0, 6)绘制矩形 - 交换GLFW窗口缓冲,轮询事件
注意:如果需要绘制多张2D图像,只需要重复绑定不同纹理、更新顶点位置(或者把位置做成uniform传入)、调用绘制接口即可,逻辑完全一致。
内容的提问来源于stack exchange,提问作者asdf asdfs8g2

