PyOpenGL着色器无法正常工作仅显白屏,是否顶点存在问题?
问题描述
尝试使用Pygame字体表面,通过data = pygame.image.tostring(img, "RGBA", 1)将其转换为像素数据,再调用OpenGL函数创建纹理,结合着色器用glDrawArrays绘制,但结果仅显示白屏,无文字显示。
原代码
from __future__ import division import pygame import numpy as np from OpenGL.GL import * from OpenGL.GL import shaders from pygame import * pygame.init() screen = pygame.display.set_mode((600, 400), DOUBLEBUF | OPENGL) vertex_shader = shaders.compileShader(""" #version 130 in vec3 position; out vec2 fragTexCoord; uniform mat4 projectionMatrix; uniform mat4 modelViewMatrix; void main() { gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); fragTexCoord = gl_Position.xy; } """, GL_VERTEX_SHADER) fragment_shader = shaders.compileShader(""" #version 130 in vec2 fragTexCoord; uniform sampler2D textureSampler; out vec4 fragmentColor; void main() { fragmentColor = texture(textureSampler, fragTexCoord); } """, GL_FRAGMENT_SHADER) shader_program = shaders.compileProgram(vertex_shader, fragment_shader) img = pygame.font.Font(None, 50).render("Hello", True, (255, 255, 255)) w, h = img.get_size() texture = glGenTextures(1) glPixelStorei(GL_UNPACK_ALIGNMENT, 1) glBindTexture(GL_TEXTURE_2D, texture) glTexParameter(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST) glTexParameter(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST) data = pygame.image.tostring(img, "RGBA", 1) glTexImage2D(GL_TEXTURE_2D, 0, 4, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data) quad_vertices = np.array([ [-1.0, -1.0, 0.0], [ 1.0, -1.0, 0.0], [ 1.0, 1.0, 0.0], [-1.0, 1.0, 0.0], ], dtype=np.float32) projection_matrix_loc = glGetUniformLocation(shader_program, "projectionMatrix") modelview_matrix_loc = glGetUniformLocation(shader_program, "modelViewMatrix") projection_matrix = np.array([[1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, -1.0, -1.0], [0.0, 0.0, -1.0, 1.0]], dtype=np.float32) modelview_matrix = np.identity(4, dtype=np.float32) while True: for event in pygame.event.get(): if event.type == QUIT: pygame.quit() exit() glClear(GL_COLOR_BUFFER_BIT) glUseProgram(shader_program) glUniformMatrix4fv(projection_matrix_loc, 1, GL_FALSE, projection_matrix) glUniformMatrix4fv(modelview_matrix_loc, 1, GL_FALSE, modelview_matrix) VBO = glGenBuffers(1) glBindBuffer(GL_ARRAY_BUFFER, VBO) glBufferData(GL_ARRAY_BUFFER, quad_vertices.size * quad_vertices.dtype.itemsize, quad_vertices, GL_STATIC_DRAW) glEnableVertexAttribArray(0) glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, None) glBindTexture(GL_TEXTURE_2D, texture) glDrawArrays(GL_TRIANGLE_FAN, 0, 4) glUseProgram(0) pygame.display.flip() pygame.time.Clock().tick(60)
问题分析与修正
核心问题点
- 纹理坐标错误:原着色器用裁剪空间坐标(范围[-1,1])作为纹理坐标,不符合OpenGL纹理坐标[0,1]的要求,且未适配Pygame图像Y轴翻转的特性。
- 纹理上传不规范:
glTexImage2D内部格式使用4不兼容,需改用标准枚举值;未处理Pygame图像的Y轴翻转问题。 - VBO重复创建:循环内每次生成新VBO,导致GPU内存泄漏。
- 顶点属性位置硬编码:直接使用
0作为属性位置,未确保与着色器中属性位置匹配。 - 投影矩阵不适配:原矩阵无法正确映射屏幕像素到OpenGL空间,导致文字显示异常。
修正后的代码
from __future__ import division import pygame import numpy as np import ctypes from OpenGL.GL import * from OpenGL.GL import shaders from pygame import * pygame.init() screen_width, screen_height = 600, 400 screen = pygame.display.set_mode((screen_width, screen_height), DOUBLEBUF | OPENGL) # 编译着色器 vertex_shader = shaders.compileShader(""" #version 130 in vec3 position; in vec2 texCoord; out vec2 fragTexCoord; uniform mat4 projectionMatrix; uniform mat4 modelViewMatrix; void main() { gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); fragTexCoord = texCoord; } """, GL_VERTEX_SHADER) fragment_shader = shaders.compileShader(""" #version 130 in vec2 fragTexCoord; uniform sampler2D textureSampler; out vec4 fragmentColor; void main() { fragmentColor = texture(textureSampler, fragTexCoord); } """, GL_FRAGMENT_SHADER) shader_program = shaders.compileProgram(vertex_shader, fragment_shader) # 获取属性与Uniform位置 position_loc = glGetAttribLocation(shader_program, "position") tex_coord_loc = glGetAttribLocation(shader_program, "texCoord") projection_matrix_loc = glGetUniformLocation(shader_program, "projectionMatrix") modelview_matrix_loc = glGetUniformLocation(shader_program, "modelViewMatrix") texture_sampler_loc = glGetUniformLocation(shader_program, "textureSampler") # 创建文字表面(添加背景色避免透明采样异常) font = pygame.font.Font(None, 50) img = font.render("Hello", True, (255, 255, 255), (0, 0, 0)) w, h = img.get_size() # 创建并配置纹理 texture = glGenTextures(1) glBindTexture(GL_TEXTURE_2D, texture) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) # 转换图像数据并上传 data = pygame.image.tostring(img, "RGBA", True) glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data) # 定义带纹理坐标的四边形顶点(顶点格式:x,y,z,u,v) quad_data = np.array([ -0.5, -0.5, 0.0, 0.0, 1.0, # 左下 0.5, -0.5, 0.0, 1.0, 1.0, # 右下 0.5, 0.5, 0.0, 1.0, 0.0, # 右上 -0.5, 0.5, 0.0, 0.0, 0.0, # 左上 ], dtype=np.float32) # 创建VBO并上传数据(仅执行一次) VBO = glGenBuffers(1) glBindBuffer(GL_ARRAY_BUFFER, VBO) glBufferData(GL_ARRAY_BUFFER, quad_data.nbytes, quad_data, GL_STATIC_DRAW) # 正交投影矩阵:将屏幕像素坐标映射到OpenGL裁剪空间 projection_matrix = np.array([ [2/screen_width, 0, 0, -1], [0, -2/screen_height, 0, 1], [0, 0, -1, 0], [0, 0, 0, 1] ], dtype=np.float32) # 模型视图矩阵:将文字定位到屏幕中心并缩放至实际尺寸 modelview_matrix = np.identity(4, dtype=np.float32) modelview_matrix[3][0] = screen_width//2 - w//2 # X轴偏移 modelview_matrix[3][1] = screen_height//2 - h//2 # Y轴偏移 modelview_matrix[0][0] = w # X轴缩放 modelview_matrix[1][1] = h # Y轴缩放 running = True clock = pygame.time.Clock() while running: for event in pygame.event.get(): if event.type == QUIT: running = False glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) glClearColor(0.0, 0.0, 0.0, 1.0) # 设置清除颜色为黑色 glUseProgram(shader_program) # 设置Uniform参数 glUniformMatrix4fv(projection_matrix_loc, 1, GL_FALSE, projection_matrix) glUniformMatrix4fv(modelview_matrix_loc, 1, GL_FALSE, modelview_matrix) glUniform1i(texture_sampler_loc, 0) # 绑定纹理单元0 # 绑定VBO并配置顶点属性 glBindBuffer(GL_ARRAY_BUFFER, VBO) # 位置属性 glEnableVertexAttribArray(position_loc) glVertexAttribPointer(position_loc, 3, GL_FLOAT, GL_FALSE, 5*4, ctypes.c_void_p(0)) # 纹理坐标属性 glEnableVertexAttribArray(tex_coord_loc) glVertexAttribPointer(tex_coord_loc, 2, GL_FLOAT, GL_FALSE, 5*4, ctypes.c_void_p(3*4)) # 绑定纹理 glBindTexture(GL_TEXTURE_2D, texture) # 绘制四边形 glDrawArrays(GL_TRIANGLE_FAN, 0, 4) # 清理顶点属性 glDisableVertexAttribArray(position_loc) glDisableVertexAttribArray(tex_coord_loc) glUseProgram(0) pygame.display.flip() clock.tick(60) pygame.quit() exit()
关键修正说明
- 纹理坐标适配:单独定义每个顶点的纹理坐标,翻转Y轴(
v值从1.0到0.0),匹配Pygame图像的原点特性。 - 正交投影:使用正交投影直接映射屏幕像素坐标,简化文字的位置和尺寸调整。
- VBO优化:VBO仅创建一次,避免GPU内存泄漏。
- 属性位置动态获取:通过
glGetAttribLocation获取着色器属性位置,避免硬编码错误。 - 纹理参数配置:设置
GL_CLAMP_TO_EDGE避免纹理边缘的异常采样。
内容的提问来源于stack exchange,提问作者DryWall
相关产品推荐
相关产品推荐

