OpenGL中矩形旋转失真问题排查:我的实现是否存在疏漏?
问题:使用glRotatef旋转矩形时出现失真
我在使用glRotatef旋转矩形时遇到了失真问题。我了解旋转前需将图像移至屏幕中心,且自认为已正确完成该操作,具体实现代码如下:
import os import pygame from PIL import Image from pathlib import Path from numpy import array import pygame import math from pygame.locals import * from OpenGL.GL import * from OpenGL.GLU import * from OpenGL.GL import glClear, GL_COLOR_BUFFER_BIT, glBegin, GL_QUADS, glEnd, glColor3f, glVertex2f, glFlush, glClearColor os.environ['SDL_VIDEO_CENTERED'] = '1' windowSize = (1500, 800) Screen = pygame.display.set_mode(windowSize, pygame.DOUBLEBUF | pygame.OPENGL) HOME = str(Path.home()) path = HOME + '\\Desktop\piston2.png' path2 = HOME + '\\Desktop\piston3.png' path3 = HOME + '\\Desktop\Piston Platformer2\Images\Flower1.png' def loadTexture(texture, flip): try: text = Image.open(texture) except IOError as ex: print("Failed to open texture file: ", texture) text = Image.open("0.png") textData = array(list(text.getdata())) textID = glGenTextures(1) glPixelStorei(GL_UNPACK_ALIGNMENT, 1) glBindTexture(GL_TEXTURE_2D, textID) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0) glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, text.size[0], text.size[1], 0, GL_RGBA, GL_UNSIGNED_BYTE, textData) text.close() return (text.size[0], text.size[1]), textID def make_opengl_rect(image_sizes, cur_ID, offsets, rotate): texts = ((1, 0), (1, 1), (0, 1), (0, 0)) x_ratio = image_sizes[0]/windowSize[0] y_ratio = image_sizes[1]/windowSize[1] offset_x = 2*(offsets[0]/windowSize[0]) offset_y = 2*(offsets[1]/windowSize[1]) verts = [(2*x_ratio, 2*y_ratio), (2*x_ratio, 0), (0, 0), (0, 2*y_ratio)] # upright, downright, downleft, upleft additional_offset_x, additional_offset_y = 0, 0 verts = [(x-1+offset_x+additional_offset_x, y+1-verts[0][1]-offset_y+additional_offset_y) for (x, y) in verts] print('verts', verts) if rotate != 0: glPushMatrix() glTranslatef(-verts[0][0], -verts[0][1], 0) glRotatef(rotate, 0, 0, 1) glTranslatef(verts[0][0], verts[0][1], 0) glEnable(GL_TEXTURE_2D) glBindTexture(GL_TEXTURE_2D, cur_ID) glBegin(GL_QUADS) for i in range(4): glTexCoord2f(texts[i][0], texts[i][1]) glVertex2f(verts[i][0], verts[i][1]) glEnd() print('verts', verts) if rotate != 0: glPopMatrix() glDisable(GL_TEXTURE_2D) my_image1_sizes, my_image1_ID = loadTexture(path, False) my_image2_sizes, my_image2_ID = loadTexture(path2, False) my_image3_sizes, my_image3_ID = loadTexture(path3, True) glEnable(GL_BLEND) glBlendEquation(GL_FUNC_ADD) running = True while running: for event in pygame.event.get(): if event.type == pygame.QUIT: raise SystemExit elif event.type == pygame.KEYDOWN: if event.key == pygame.K_ESCAPE: raise SystemExit # glClear(GL_COLOR_BUFFER_BIT) glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) #make_opengl_rect(my_image1_sizes, my_image1_ID, (100,100), 0) #make_opengl_rect(my_image2_sizes, my_image2_ID, (100,100), 0) make_opengl_rect(my_image3_sizes, my_image3_ID, (300,300), 340) glFlush() pygame.display.flip() print('')
我还附上了旋转后和未旋转的示例图,请问我是否遗漏了关键步骤?
解决方案
你遇到的失真问题主要源于旋转中心点错误、纹理过滤设置不当以及顶点坐标计算逻辑混乱,以下是具体修正方案:
1. 修正旋转中心点计算
你当前用矩形的右上角顶点作为旋转中心,这会导致旋转后图形偏移且变形。需要计算矩形的真正中心:
# 在make_opengl_rect函数中生成verts后,添加中心计算 rect_center_x = (verts[0][0] + verts[2][0]) / 2 rect_center_y = (verts[0][1] + verts[2][1]) / 2
然后调整矩阵变换顺序,确保围绕中心旋转:
if rotate != 0: glPushMatrix() # 正确顺序:先将中心移到原点→旋转→移回原位置 glTranslatef(rect_center_x, rect_center_y, 0) glRotatef(rotate, 0, 0, 1) glTranslatef(-rect_center_x, -rect_center_y, 0)
2. 优化纹理过滤设置
当前使用的GL_NEAREST过滤会在旋转后产生严重锯齿,换成线性过滤可大幅改善失真:
# 在loadTexture函数中修改过滤参数 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
3. 简化顶点坐标计算逻辑
原顶点转换逻辑过于复杂,重新梳理为标准化设备坐标(NDC)的正确转换:
def make_opengl_rect(image_sizes, cur_ID, offsets, rotate): texts = ((1, 0), (1, 1), (0, 1), (0, 0)) x_ratio = image_sizes[0]/windowSize[0] y_ratio = image_sizes[1]/windowSize[1] # 窗口偏移转NDC(适配OpenGL y轴向上的特性) x_offset = (offsets[0] / windowSize[0]) * 2 - 1 y_offset = 1 - (offsets[1] / windowSize[1]) * 2 # 生成原始矩形顶点(以原点为左下角) verts = [ (x_ratio*2, y_ratio*2), # 右上角 (x_ratio*2, 0), # 右下角 (0, 0), # 左下角 (0, y_ratio*2) # 左上角 ] # 应用偏移到正确位置 verts = [(x + x_offset, y + y_offset - y_ratio*2) for x, y in verts] # 计算矩形中心 rect_center_x = (verts[0][0] + verts[2][0]) / 2 rect_center_y = (verts[0][1] + verts[2][1]) / 2 glEnable(GL_TEXTURE_2D) glBindTexture(GL_TEXTURE_2D, cur_ID) if rotate != 0: glPushMatrix() glTranslatef(rect_center_x, rect_center_y, 0) glRotatef(rotate, 0, 0, 1) glTranslatef(-rect_center_x, -rect_center_y, 0) glBegin(GL_QUADS) for i in range(4): glTexCoord2f(texts[i][0], texts[i][1]) glVertex2f(verts[i][0], verts[i][1]) glEnd() if rotate != 0: glPopMatrix() glDisable(GL_TEXTURE_2D)
额外优化建议
- 启用抗锯齿:创建窗口时添加
pygame.MULTISAMPLE标志,并在初始化时调用glEnable(GL_MULTISAMPLE) - 统一纹理尺寸:尽量使用2的幂次尺寸的纹理,避免OpenGL对非标准尺寸纹理的兼容性问题
内容的提问来源于stack exchange,提问作者Kayla
相关产品推荐
相关产品推荐

