Python、GTK、OpenGL程序点击三角形无旋转更新问题求助及代码修改
问题修复:Gtk3+OpenGL3.3点击三角形内部旋转功能
问题原因
- 点-in-三角形判断逻辑错误:原代码中
sign函数的符号判断方向与顶点环绕顺序不匹配,导致无法正确识别点击是否在三角形内部。 ctypes导入位置错误:在类的__init__方法中导入ctypes不符合规范,可能导致后续glVertexAttribPointer调用出现潜在问题。
修复后的完整代码
import gi import ctypes gi.require_version('Gtk', '3.0') from gi.repository import Gtk, Gdk from OpenGL.GL import * from OpenGL.GL import shaders import numpy as np # 顶点着色器源码 vertex_shader_source = """ #version 330 core layout(location = 0) in vec3 a_position; uniform mat3 rotation; void main() { vec3 pos = rotation * a_position; gl_Position = vec4(pos, 1.0); } """ # 片段着色器源码 fragment_shader_source = """ #version 330 core out vec4 frag_color; void main() { frag_color = vec4(1.0, 0.5, 0.2, 1.0); } """ class OpenGLDrawingArea(Gtk.GLArea): def __init__(self): super().__init__() self.connect("realize", self.on_realize) self.connect("render", self.on_render) self.connect("button-press-event", self.on_button_press_event) self.set_has_depth_buffer(False) self.set_has_stencil_buffer(False) self.set_auto_render(True) self.angle = 0.0 self.set_events(Gdk.EventMask.BUTTON_PRESS_MASK) def on_realize(self, area): ctx = self.get_context() ctx.make_current() # 编译着色器 vertex_shader = shaders.compileShader(vertex_shader_source, GL_VERTEX_SHADER) fragment_shader = shaders.compileShader(fragment_shader_source, GL_FRAGMENT_SHADER) self.shader_program = shaders.compileProgram(vertex_shader, fragment_shader) # 定义三角形顶点 self.vertices = np.array([ -0.5, -0.5, 0.0, 0.5, -0.5, 0.0, 0.0, 0.5, 0.0 ], dtype=np.float32) # 创建并绑定顶点数组对象(VAO) self.VAO = glGenVertexArrays(1) glBindVertexArray(self.VAO) # 创建并绑定顶点缓冲对象(VBO) self.VBO = glGenBuffers(1) glBindBuffer(GL_ARRAY_BUFFER, self.VBO) glBufferData(GL_ARRAY_BUFFER, self.vertices.nbytes, self.vertices, GL_STATIC_DRAW) # 指定顶点属性指针 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), ctypes.c_void_p(0)) glEnableVertexAttribArray(0) # 解绑缓冲和VAO glBindBuffer(GL_ARRAY_BUFFER, 0) glBindVertexArray(0) def on_render(self, area, ctx): glClearColor(0.0, 0.0, 0.0, 1.0) glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) glUseProgram(self.shader_program) glBindVertexArray(self.VAO) # 应用旋转矩阵 rotation_matrix = np.array([ [np.cos(np.radians(self.angle)), -np.sin(np.radians(self.angle)), 0.0], [np.sin(np.radians(self.angle)), np.cos(np.radians(self.angle)), 0.0], [0.0, 0.0, 1.0] ], dtype=np.float32) glUniformMatrix3fv(glGetUniformLocation(self.shader_program, "rotation"), 1, GL_TRUE, rotation_matrix) glDrawArrays(GL_TRIANGLES, 0, 3) glBindVertexArray(0) def on_button_press_event(self, area, event): if event.button == Gdk.BUTTON_PRIMARY: # 鼠标左键 # 转换鼠标坐标到OpenGL标准化设备坐标 x, y = event.x, event.y width, height = area.get_allocated_width(), area.get_allocated_height() gl_x = (2.0 * x / width) - 1.0 gl_y = 1.0 - (2.0 * y / height) # 判断点击是否在三角形内部 if self.point_in_triangle(gl_x, gl_y): self.angle += 10.0 # 顺时针旋转10度 self.queue_render() # 请求重绘 def point_in_triangle(self, x, y): # 计算三点构成的叉积符号,判断点是否在三角形内 def sign(p1, p2, p3): return (p1[0] - p3[0]) * (p2[1] - p3[1]) - (p2[0] - p3[0]) * (p1[1] - p3[1]) # 生成当前角度的2D旋转矩阵 rot_mat = np.array([ [np.cos(np.radians(self.angle)), -np.sin(np.radians(self.angle))], [np.sin(np.radians(self.angle)), np.cos(np.radians(self.angle))] ], dtype=np.float32) # 提取原始顶点的x,y并应用旋转 v1 = rot_mat.dot(self.vertices[0:2]) v2 = rot_mat.dot(self.vertices[3:5]) v3 = rot_mat.dot(self.vertices[6:8]) # 计算三个叉积的符号 b1 = sign((x, y), v1, v2) >= 0.0 b2 = sign((x, y), v2, v3) >= 0.0 b3 = sign((x, y), v3, v1) >= 0.0 # 三个符号一致则点在三角形内 return (b1 == b2) and (b2 == b3) class MainWindow(Gtk.Window): def __init__(self): super().__init__(title="OpenGL in GTK Window") self.set_default_size(300, 300) self.set_resizable(False) # 窗口不可调整大小 self.drawing_area = OpenGLDrawingArea() self.add(self.drawing_area) self.connect("destroy", Gtk.main_quit) def main(): win = MainWindow() win.show_all() Gtk.main() if __name__ == "__main__": main()
关键修复点
- 将
import ctypes移至文件顶部,避免类初始化时的潜在问题。 - 调整
point_in_triangle中的符号判断逻辑,将<0.0改为>=0.0,匹配顶点的顺时针环绕顺序,确保正确识别点击区域。
内容的提问来源于stack exchange,提问作者frits
相关产品推荐
相关产品推荐

