You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 16:55:24