3D空间中绕X轴旋转正方形异常:仅移动不旋转问题求助
问题排查:3D正方形绕X轴旋转异常的原因与修复
我帮你找出了几个导致图形只移动不旋转的关键问题,下面逐一说明并给出修复方案:
1. 向量与矩阵乘法的z分量计算错误
在multVecMatrix函数中,z分量的计算最后一项引用了错误的矩阵元素,这会直接导致旋转后的z值计算错误,进而影响投影和整体姿态:
错误代码:
z = vec3.x * mat3.matrix[2][0] + vec3.y * mat3.matrix[2][1] + vec3.z * mat3.matrix[1][2]
修正后:
z = vec3.x * mat3.matrix[2][0] + vec3.y * mat3.matrix[2][1] + vec3.z * mat3.matrix[2][2]
这里应该引用矩阵第三行第三列的元素(mat3.matrix[2][2]),而不是第二行第三列的元素,这是矩阵乘法的基础规则。
2. 旋转时重复修改顶点导致逻辑错误
在应用循环中,你对每个顶点的x、y、z分量分别调用旋转函数,并且每次修改后都会用更新后的顶点去计算下一个分量——这相当于对同一个顶点连续执行了三次旋转操作,完全偏离了预期的单次旋转逻辑。
错误代码:
p1.x = rotation.rotate(p1, thetax, axis='x').x p1.y = rotation.rotate(p1, thetax, axis='x').y p1.z = rotation.rotate(p1, thetax, axis='x').z
修正后:
# 先计算出完整的旋转后顶点,再一次性赋值 rotated_p1 = rotation.rotate(p1, thetax, axis='x') p1.x, p1.y, p1.z = rotated_p1.x, rotated_p1.y, rotated_p1.z
对p2、p3、p4也采用同样的方式处理,确保每个顶点只执行一次旋转计算。
3. 绘制点列表冗余(优化项)
你的points列表中重复了大量顶点,比如(point2.x, point2.y)出现两次,这会导致绘制多余的线段。pygame的draw.lines只需要按顺序传入四个顶点即可自动连接:
优化后的points定义:
points = ( (point1.x, point1.y), (point2.x, point2.y), (point3.x, point3.y), (point4.x, point4.y), (point1.x, point1.y) # 如果要闭合正方形的话加上这一行,否则可以去掉 )
完整修复后的代码
import pygame from math import sin, cos, radians pygame.init() ### PYGAME STUFF ###################################### SCREENWIDTH = 600 SCREENHEIGHT = 600 D = pygame.display.set_mode((SCREENWIDTH, SCREENHEIGHT)) pygame.display.set_caption("PRESS SPACE TO ROTATE AROUND X") ######### MATH FUNCTIONS AND CLASSES #################### class Mat3: # 3X3 MATRIX INITIALIZED WITH ALL 0's def __init__(self): self.matrix = [[0 for i in range(3)], [0 for i in range(3)], [0 for i in range(3)]] class Vec2: # 2D VECTOR def __init__(self, x, y): self.x = x self.y = y class Vec3: # 3D VECTOR def __init__(self, x, y, z): self.x = x self.y = y self.z = z def multVecMatrix(vec3, mat3): # MULTIPLIES A Vec3 OBJECT WITH Mat3 OBJECT AND RETURNS A NEW Vec3 x = vec3.x * mat3.matrix[0][0] + vec3.y * mat3.matrix[0][1] + vec3.z * mat3.matrix[0][2] y = vec3.x * mat3.matrix[1][0] + vec3.y * mat3.matrix[1][1] + vec3.z * mat3.matrix[1][2] # 修复z分量的矩阵元素引用错误 z = vec3.x * mat3.matrix[2][0] + vec3.y * mat3.matrix[2][1] + vec3.z * mat3.matrix[2][2] return Vec3(x, y, z) class Transform: # IT TRANSFORMS THE X AND Y FROM NORMALIZED SPACE TO SCREEN SPACE WITH PROJECTION APPLIED def worldSpaceTransform(self, vec3, w, h): if vec3.z == 0: vec3.z = 0.001 zInverse = 1/ vec3.z xTransformed = ((vec3.x * zInverse) + 1) * (w/2) yTransformed = ((-vec3.y * zInverse) + 1) * (h/2) xTransformed = str(xTransformed)[:6] yTransformed = str(yTransformed)[:6] return Vec2(float(xTransformed), float(yTransformed)) class Rotation: def rotateX(self, theta): # ROTATION MATRIX IN X AXIS sinTheta = sin(theta) cosTheta = cos(theta) m = Mat3() m.matrix = [[1, 0, 0], [0, cosTheta, sinTheta], [0, -sinTheta, cosTheta]] return m def rotateY(self, theta): # 补全未实现的Y轴旋转(可选) sinTheta = sin(theta) cosTheta = cos(theta) m = Mat3() m.matrix = [[cosTheta, 0, -sinTheta], [0, 1, 0], [sinTheta, 0, cosTheta]] return m def rotateZ(self, theta): # 补全未实现的Z轴旋转(可选) sinTheta = sin(theta) cosTheta = cos(theta) m = Mat3() m.matrix = [[cosTheta, sinTheta, 0], [-sinTheta, cosTheta, 0], [0, 0, 1]] return m def rotate(self, vec3, theta, axis=None): # ROTATES A Vec3 BY GIVEN THETA AND AXIS if axis == "x": return multVecMatrix(vec3, self.rotateX(theta)) if axis == "y": return multVecMatrix(vec3, self.rotateY(theta)) if axis == "z": return multVecMatrix(vec3, self.rotateZ(theta)) transform = Transform() rotation = Rotation() # ASSIGNING 4 Vec3's FOR 4 SIDES OF SQUARE IN NORMALIZED SPACE s = 1 p1 = Vec3(-s, -s, -s) p2 = Vec3(s, -s, -s) p3 = Vec3(s, s, -s) p4 = Vec3(-s, s, -s) # TRANSLATING THE POINTS OF THE CUBE A LITTLE BIT INTO THE SCREEN p1.z += 6 p2.z += 6 p3.z += 6 p4.z += 6 # ASSIGNING THE ROTATION ANGLES thetax = 0 # APPLICATION LOOP running = True while running: for event in pygame.event.get(): if event.type == pygame.QUIT: running = False D.fill((255, 255, 255)) # ROTATING THE POINTS AROUND X AXIS - 修复重复旋转的问题 rotated_p1 = rotation.rotate(p1, thetax, axis='x') p1.x, p1.y, p1.z = rotated_p1.x, rotated_p1.y, rotated_p1.z rotated_p2 = rotation.rotate(p2, thetax, axis='x') p2.x, p2.y, p2.z = rotated_p2.x, rotated_p2.y, rotated_p2.z rotated_p3 = rotation.rotate(p3, thetax, axis='x') p3.x, p3.y, p3.z = rotated_p3.x, rotated_p3.y, rotated_p3.z rotated_p4 = rotation.rotate(p4, thetax, axis='x') p4.x, p4.y, p4.z = rotated_p4.x, rotated_p4.y, rotated_p4.z # TRANSLATING THE SQUARE SHEET INTO THE SCREEN SPACE point1 = transform.worldSpaceTransform(p1, SCREENWIDTH, SCREENHEIGHT) point2 = transform.worldSpaceTransform(p2, SCREENWIDTH, SCREENHEIGHT) point3 = transform.worldSpaceTransform(p3, SCREENWIDTH, SCREENHEIGHT) point4 = transform.worldSpaceTransform(p4, SCREENWIDTH, SCREENHEIGHT) # 优化点列表,去掉冗余顶点 points = ( (point1.x, point1.y), (point2.x, point2.y), (point3.x, point3.y), (point4.x, point4.y), (point1.x, point1.y) # 闭合正方形 ) keys = pygame.key.get_pressed() # ROTATE X ? if keys[pygame.K_SPACE]: thetax -= 0.005 pygame.draw.lines(D, (0, 0, 0), False, points, 2) # 增加线宽让效果更明显 pygame.display.flip() pygame.quit()
另外,我还补充了未实现的Y、Z轴旋转方法,完善了事件循环(原来的pygame.event.get()没有处理退出事件),同时增加了线宽让旋转效果更清晰。
内容的提问来源于stack exchange,提问作者user12291970
相关产品推荐
相关产品推荐

