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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 23:52:54