如何在 3D 空间中围绕 x 轴旋转正方形 [英] How to rotate a square around x-axis in a 3D space

查看:39
本文介绍了如何在 3D 空间中围绕 x 轴旋转正方形的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以我一直在努力学习 3D 渲染的工作原理.我尝试编写一个脚本,目标是在 3D 空间中旋转平面(2D)正方形.我首先在归一化空间 (-1, 1) 中定义一个正方形.请注意,只有 x 和 y 被归一化.

Vec3 类:# 3D 矢量def __init__(self, x, y, z):自我.x = x自我.y = yself.z = zs = 1p1 = Vec3(-s, -s, -s)p2 = Vec3(s, -s, -s)p3 = Vec3(s, s, -s)p4 = Vec3(-s, s, -s)

然后将点翻译到屏幕上:

p1.z += 6p2.z += 6p3.z += 6p4.z += 6

此后的一切都在应用程序循环中完成.我使用以下函数将点缩放到屏幕上并应用了投影:

类转换:# 它通过投影将 X 和 Y 从标准化空间转换为屏幕空间def worldSpaceTransform(self, vec3, w, h):如果 vec3.z == 0:vec3.z = 0.001zInverse = 1/vec3.zxTransformed = ((vec3.x * zInverse) + 1) * (w/2)yTransformed = ((-vec3.y * zInverse) + 1) * (h/2)xTransformed = str(xTransformed)[:6]yTransformed = str(yTransformed)[:6]返回 Vec2(float(xTransformed), float(yTransformed))

像这样:

# 将正方形纸转换到屏幕空间point1 = transform.worldSpaceTransform(p1, SCREENWIDTH, SCREENHEIGHT)point2 = transform.worldSpaceTransform(p2, SCREENWIDTH, SCREENHEIGHT)point3 = transform.worldSpaceTransform(p3, SCREENWIDTH, SCREENHEIGHT)point4 = transform.worldSpaceTransform(p4, SCREENWIDTH, SCREENHEIGHT)

并绘制点:

# 将点存储到元组中,以便可以使用 pygame.draw.lines 绘制它点 = ((point1.x, point1.y), (point2.x, point2.y),(point2.x, point2.y), (point3.x, point3.y),(point3.x, point3.y), (point4.x, point4.y),(point4.x, point4.y), (point1.x, point1.y))pygame.draw.lines(D, (0, 0, 0), False, points)

到目前为止一切正常(我认为),因为它绘制了一个正方形,正如它应该的那样.

现在轮换.我尝试了所有轴的旋转,但它们都不起作用,但为了具体起见,我将讨论 x 轴.下面是轮换类.我从维基百科复制了旋转矩阵.我不完全确定它们是如何工作的,所以我也不知道它是否与我上面描述的系统兼容.

def multVecMatrix(vec3, mat3):# 将 Vec3 对象与 Mat3 对象相乘并返回一个新的 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 = vec3.x * mat3.matrix[2][0] + vec3.y * mat3.matrix[2][1] + vec3.z * mat3.matrix[2][2]返回 Vec3(x, y, z)班级轮换:def rotateX(self, theta):# X 轴旋转矩阵 ??sinTheta = 罪(θ)cosTheta = cos(theta)m = Mat3()m.matrix = [[1, 0, 0],[0, cosTheta, sinTheta],[0, -sinTheta, cosTheta]]返回米def旋转(自我,vec3,theta,轴=无):# 根据给定的 Theta 和 AXIS 旋转 Vec3 ??如果轴 == x":返回 multVecMatrix(vec3, self.rotateX(theta))如果轴 == y":返回 multVecMatrix(vec3, self.rotateY(theta))如果轴 == z":返回 multVecMatrix(vec3, self.rotateZ(theta))

在填充屏幕白色之后,在将点从归一化空间缩放到屏幕空间之前,它是这样调用的.

 # 屏幕以白色填充# 围绕 X 轴旋转点 ?????p1.x = rotation.rotate(p1, thetax, axis='x').xp1.y = rotation.rotate(p1, thetay,axis='x').yp1.z = rotation.rotate(p1, thetax, axis='x').zp2.x = rotation.rotate(p2, thetax, axis='x').xp2.y = rotation.rotate(p2, thetay,axis='x').yp2.z = rotation.rotate(p2, thetax, axis='x').zp3.x = rotation.rotate(p3, thetax, axis='x').xp3.y = rotation.rotate(p3, thetay,axis='x').yp3.z = rotation.rotate(p3, thetax, axis='x').zp4.x = rotation.rotate(p4, thetax, axis='x').xp4.y = rotation.rotate(p4, thetay,axis='x').yp4.z = rotation.rotate(p4, thetax, axis='x').z# 然后这些点被转换成世界空间

应用旋转后,它看起来像在移动并绕 x 轴旋转但不旋转.我希望它在原地不动的同时旋转.我做错了什么?

完整的复制粘贴代码供参考:

导入pygame从数学导入 sin、cos、弧度pygame.init()### PYGAME STUFF ###################################屏幕宽度 = 600屏幕高度 = 600D = pygame.display.set_mode((屏幕宽度,屏幕高度))pygame.display.set_caption(按空格旋转 X")######### 数学函数和类####################类 Mat3:# 3X3 矩阵初始化为全 0def __init__(self):self.matrix = [[0 for i in range(3)],[0 for i in range(3)],[0 for i in range(3)]]Vec2 类:# 二维矢量def __init__(self, x, y):自我.x = x自我.y = yVec3 类:# 3D 矢量def __init__(self, x, y, z):自我.x = x自我.y = yself.z = zdef multVecMatrix(vec3, mat3):# 将 Vec3 对象与 Mat3 对象相乘并返回一个新的 Vec3x = 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 = vec3.x * mat3.matrix[2][0] + vec3.y * mat3.matrix[2][1] + vec3.z * mat3.matrix[1][2]返回 Vec3(x, y, z)类转换:# 它通过投影将 X 和 Y 从标准化空间转换为屏幕空间def worldSpaceTransform(self, vec3, w, h):如果 vec3.z == 0:vec3.z = 0.001zInverse = 1/vec3.zxTransformed = ((vec3.x * zInverse) + 1) * (w/2)yTransformed = ((-vec3.y * zInverse) + 1) * (h/2)xTransformed = str(xTransformed)[:6]yTransformed = str(yTransformed)[:6]返回 Vec2(float(xTransformed), float(yTransformed))班级轮换:def rotateX(self, theta):# X 轴旋转矩阵sinTheta = 罪(θ)cosTheta = cos(theta)m = Mat3()m.matrix = [[1, 0, 0],[0, cosTheta, sinTheta],[0, -sinTheta, cosTheta]]返回米def旋转(自我,vec3,theta,轴=无):# 根据给定的 Theta 和 AXIS 旋转 Vec3如果轴 == x":返回 multVecMatrix(vec3, self.rotateX(theta))如果轴 == y":返回 multVecMatrix(vec3, self.rotateY(theta))如果轴 == z":返回 multVecMatrix(vec3, self.rotateZ(theta))变换 = 变换()旋转 = 旋转()# 在标准化空间中为正方形的 4 个边分配 4 个 Vec3s = 1p1 = Vec3(-s, -s, -s)p2 = Vec3(s, -s, -s)p3 = Vec3(s, s, -s)p4 = Vec3(-s, s, -s)# 将立方体的点稍微转移到屏幕上p1.z += 6p2.z += 6p3.z += 6p4.z += 6# 分配旋转角度税收 = 0# 应用循环而真:pygame.event.get()D.fill((255, 255, 255))# 围绕 X 轴旋转点p1.x = rotation.rotate(p1, thetax, axis='x').xp1.y = rotation.rotate(p1, thetax, axis='x').yp1.z = rotation.rotate(p1, thetax, axis='x').zp2.x = rotation.rotate(p2, thetax, axis='x').xp2.y = rotation.rotate(p2, thetax, axis='x').yp2.z = rotation.rotate(p2, thetax, axis='x').zp3.x = rotation.rotate(p3, thetax, axis='x').xp3.y = rotation.rotate(p3, thetax, axis='x').yp3.z = rotation.rotate(p3, thetax, axis='x').zp4.x = rotation.rotate(p4, thetax, axis='x').xp4.y = rotation.rotate(p4, thetax, axis='x').yp4.z = rotation.rotate(p4, thetax, axis='x').z# 将正方形纸转换到屏幕空间point1 = transform.worldSpaceTransform(p1, SCREENWIDTH, SCREENHEIGHT)point2 = transform.worldSpaceTransform(p2, SCREENWIDTH, SCREENHEIGHT)point3 = transform.worldSpaceTransform(p3, SCREENWIDTH, SCREENHEIGHT)point4 = transform.worldSpaceTransform(p4, SCREENWIDTH, SCREENHEIGHT)# 将点存储到元组中,以便可以使用 pygame.draw.lines 绘制它点 = ((point1.x, point1.y), (point2.x, point2.y),(point2.x, point2.y), (point3.x, point3.y),(point3.x, point3.y), (point4.x, point4.y),(point4.x, point4.y), (point1.x, point1.y))键 = pygame.key.get_pressed()# 旋转 X ?如果键[pygame.K_SPACE]:税收 -= 0.005pygame.draw.lines(D, (0, 0, 0), False, points)pygame.display.flip()

解决方案

不必单独旋转向量的每个组件.如果你这样做

<块引用>

p1.x = rotation.rotate(p1, thetax,axis='x').x

那么p1x组件发生了变化,传递给下一条指令的p1是不同的

<块引用>

p1.y = rotation.rotate(p1, thetay,axis='x').y

旋转整个顶点一次就足够了:

p1 = rotation.rotate(p1, thetax,axis='x')p2 = rotation.rotate(p2, thetax,axis='x')p3 = rotation.rotate(p3, thetax,axis='x')p4 = rotation.rotate(p4, thetax,axis='x')

当您将一个向量乘以一个旋转矩阵时,该向量将旋转一圈 (0, 0, 0).您必须在旋转后进行翻译.
+-operator 添加到 Vec3 类:

class Vec3:# 3D 矢量def __init__(self, x, y, z):自我.x = x自我.y = yself.z = zdef __add__(a, b):返回 Vec3(a.x+b.x, a.y+b.y, a.z+b.z)

永远不要改变原始顶点坐标p1p2p3p4.计算旋转然后平移:

# 将立方体的点稍微转换到屏幕上#p1.z += 6 <--- 删除#p2.z += 6#p3.z += 6#p4.z += 6transVec = Vec3(0, 0, 6)# [...]运行时:# 围绕 X 轴旋转点point1 = rotation.rotate(p1, thetax,axis='x')# [...]# 将立方体的点稍微转移到屏幕上点 1 = 点 1 + transVec# [...]# 将正方形纸转换到屏幕空间point1 = transform.worldSpaceTransform(point1, SCREENWIDTH, SCREENHEIGHT)# [...]

我建议在列表中组织顶点坐标:

# 分配 4 个 Vec3 用于规范化空间中正方形的 4 个边s = 1模型点 = [Vec3(-s, -s, -s), Vec3(s, -s, -s), Vec3(s, s, -s), Vec3(-s, s, -s)]# 将立方体的点稍微转移到屏幕上transVec = Vec3(0, 0, 6)# 分配旋转角度税收 = 0# 应用循环运行 = 真运行时:对于 pygame.event.get() 中的事件:如果 event.type == pygame.QUIT:运行 = 错误D.fill((255, 255, 255))# 围绕 X 轴旋转点点 = [rotation.rotate(pt, thetax,axis='x') for pt in modelPoints]# 将立方体的点稍微转移到屏幕上点 = [pt + transVec for pt in points]# 将正方形纸转换到屏幕空间点 = [transform.worldSpaceTransform(pt, SCREENWIDTH, SCREENHEIGHT) for pt in points]# 将点存储到元组中,以便可以使用 pygame.draw.lines 绘制它点 = [(pt.x, pt.y) for pt in points]


查看完整示例:

导入pygame从数学导入 sin、cos、弧度pygame.init()### PYGAME STUFF ###################################屏幕宽度 = 600屏幕高度 = 600D = pygame.display.set_mode((屏幕宽度,屏幕高度))pygame.display.set_caption(按空格旋转 X")######### 数学函数和类####################类 Mat3:# 3X3 矩阵初始化为全 0def __init__(self):self.matrix = [[0 for i in range(3)],[0 for i in range(3)],[0 for i in range(3)]]Vec2 类:# 二维矢量def __init__(self, x, y):自我.x = x自我.y = yVec3 类:# 3D 矢量def __init__(self, x, y, z):自我.x = x自我.y = yself.z = zdef __add__(a, b):返回 Vec3(a.x+b.x, a.y+b.y, a.z+b.z)def multVecMatrix(vec3, mat3):# 将 Vec3 对象与 Mat3 对象相乘并返回一个新的 Vec3x = 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 = vec3.x * mat3.matrix[2][0] + vec3.y * mat3.matrix[2][1] + vec3.z * mat3.matrix[2][2]返回 Vec3(x, y, z)类转换:# 它通过投影将 X 和 Y 从标准化空间转换为屏幕空间def worldSpaceTransform(self, vec3, w, h):如果 vec3.z == 0:vec3.z = 0.001zInverse = 1/vec3.zxTransformed = ((vec3.x * zInverse) + 1) * (w/2)yTransformed = ((-vec3.y * zInverse) + 1) * (h/2)xTransformed = str(xTransformed)[:6]yTransformed = str(yTransformed)[:6]返回 Vec2(float(xTransformed), float(yTransformed))班级轮换:def rotateX(self, theta):# X 轴旋转矩阵sinTheta = 罪(θ)cosTheta = cos(theta)m = Mat3()m.matrix = [[1, 0, 0],[0, cosTheta, sinTheta],[0, -sinTheta, cosTheta]]返回米def旋转(自我,vec3,theta,轴=无):# 根据给定的 Theta 和 AXIS 旋转 Vec3如果轴 == x":返回 multVecMatrix(vec3, self.rotateX(theta))如果轴 == y":返回 multVecMatrix(vec3, self.rotateY(theta))如果轴 == z":返回 multVecMatrix(vec3, self.rotateZ(theta))变换 = 变换()旋转 = 旋转()# 在标准化空间中为正方形的 4 个边分配 4 个 Vec3s = 1模型点 = [Vec3(-s, -s, -s), Vec3(s, -s, -s), Vec3(s, s, -s), Vec3(-s, s, -s)]# 将立方体的点稍微转移到屏幕上transVec = Vec3(0, 0, 6)# 分配旋转角度税收 = 0# 应用循环运行 = 真运行时:对于 pygame.event.get() 中的事件:如果 event.type == pygame.QUIT:运行 = 错误D.fill((255, 255, 255))# 围绕 X 轴旋转点点 = [rotation.rotate(pt, thetax,axis='x') for pt in modelPoints]# 将立方体的点稍微转移到屏幕上点 = [pt + transVec for pt in points]# 将正方形纸转换到屏幕空间点 = [transform.worldSpaceTransform(pt, SCREENWIDTH, SCREENHEIGHT) for pt in points]# 将点存储到元组中,以便可以使用 pygame.draw.lines 绘制它点 = [(pt.x, pt.y) for pt in points]键 = pygame.key.get_pressed()# 旋转 X ?如果键[pygame.K_SPACE]:税收 -= 0.005pygame.draw.lines(D, (0, 0, 0), True, 点)pygame.display.flip()

So i have been trying to learn how 3D rendering works. I tried write a script with the goal to rotate a flat (2D) square in 3D space. I started by defining a square in a normalised space (-1, 1). Note that only x and y is normalised.

class Vec3:
    #  3D VECTOR
    def __init__(self, x, y, z):
        self.x = x
        self.y = y
        self.z = z

s = 1
p1 = Vec3(-s, -s, -s) 
p2 = Vec3(s, -s, -s)
p3 = Vec3(s, s, -s)
p4 = Vec3(-s, s, -s)

Then translated the points into the screen:

p1.z += 6
p2.z += 6
p3.z += 6
p4.z += 6

Everything after this is done inside the application loop. I scaled the points into the the screen with projection applied using the function:

class Transform:
    # IT TRANSFORMS THE X AND Y FROM NORMALISED 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))

like this:

# 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)

and drew the points:

# STORING THE POINTS TO A TUPLE SO IT CAN BE DRAWN USING pygame.draw.lines
    points = ((point1.x, point1.y), (point2.x, point2.y),
              (point2.x, point2.y), (point3.x, point3.y),
              (point3.x, point3.y), (point4.x, point4.y),
              (point4.x, point4.y), (point1.x, point1.y))
    pygame.draw.lines(D, (0, 0, 0), False, points)

Everything so far works (i think) because it draws a square, as it's supposed to.

Now the rotation. I tried rotation for all the axis and none of them work, but for the sake of being specific, i will talk about the x-axis. The following is the rotation class. I copied the rotation matrices from wikipedia. I am not completely sure about how they work so i also dont know if it is compatible with the system i described above.

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 = vec3.x * mat3.matrix[2][0] + vec3.y * mat3.matrix[2][1] + vec3.z * mat3.matrix[2][2]
    return Vec3(x, y, z)

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 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)) 

And it is called like this after filling the screen white and before scaling the points from normalised space to screen space.

    # screen is filled with white color
    # ROTATING THE POINTS AROUND X AXIS ?????

    p1.x = rotation.rotate(p1, thetax, axis='x').x
    p1.y = rotation.rotate(p1, thetay, axis='x').y
    p1.z = rotation.rotate(p1, thetax, axis='x').z

    p2.x = rotation.rotate(p2, thetax, axis='x').x
    p2.y = rotation.rotate(p2, thetay, axis='x').y
    p2.z = rotation.rotate(p2, thetax, axis='x').z

    p3.x = rotation.rotate(p3, thetax, axis='x').x
    p3.y = rotation.rotate(p3, thetay, axis='x').y
    p3.z = rotation.rotate(p3, thetax, axis='x').z

    p4.x = rotation.rotate(p4, thetax, axis='x').x
    p4.y = rotation.rotate(p4, thetay, axis='x').y
    p4.z = rotation.rotate(p4, thetax, axis='x').z
    
    # then the points are translated into world space

After the rotation is applied, it looks like it is moving and circling the x-axis but not rotating. I want it to rotate while staying where it is. What am i doing wrong?

Complete copy-and-paste code for reference:

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 = vec3.x * mat3.matrix[2][0] + vec3.y * mat3.matrix[2][1] + vec3.z * mat3.matrix[1][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 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
while True:
    pygame.event.get()
    D.fill((255, 255, 255))


    # ROTATING THE POINTS AROUND X AXIS

    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

    p2.x = rotation.rotate(p2, thetax, axis='x').x
    p2.y = rotation.rotate(p2, thetax, axis='x').y
    p2.z = rotation.rotate(p2, thetax, axis='x').z

    p3.x = rotation.rotate(p3, thetax, axis='x').x
    p3.y = rotation.rotate(p3, thetax, axis='x').y
    p3.z = rotation.rotate(p3, thetax, axis='x').z

    p4.x = rotation.rotate(p4, thetax, axis='x').x
    p4.y = rotation.rotate(p4, thetax, axis='x').y
    p4.z = rotation.rotate(p4, thetax, axis='x').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)

    # STORING THE POINTS TO A TUPLE SO IT CAN BE DRAWN USING pygame.draw.lines
    points = ((point1.x, point1.y), (point2.x, point2.y),
              (point2.x, point2.y), (point3.x, point3.y),
              (point3.x, point3.y), (point4.x, point4.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)
    
    pygame.display.flip()

解决方案

It is not necessary to rotate each component of a vector separately. If you do

p1.x = rotation.rotate(p1, thetax, axis='x').x

then the x component of p1 has changed and the p1 which is passed to the next instruction is different

p1.y = rotation.rotate(p1, thetay, axis='x').y

It is sufficient to rotate the entire vertices once:

p1 = rotation.rotate(p1, thetax, axis='x')  
p2 = rotation.rotate(p2, thetax, axis='x')
p3 = rotation.rotate(p3, thetax, axis='x')
p4 = rotation.rotate(p4, thetax, axis='x')

When you multiply a vector by a rotation matrix, then the vector is rotated a round (0, 0, 0). You have to do the translation after the rotation.
Add a +-operator to the Vec3 class:

class Vec3:
    #  3D VECTOR
    def __init__(self, x, y, z):
        self.x = x
        self.y = y
        self.z = z
    def __add__(a, b):
        return Vec3(a.x+b.x, a.y+b.y, a.z+b.z)

Never change the original vertex coordinates p1, p2, p3 and p4. Compute the rotation and then the translation:

# TRANSLATING THE POINTS OF THE CUBE A LITTLE BIT INTO THE SCREEN
#p1.z += 6 <--- DELETE
#p2.z += 6
#p3.z += 6
#p4.z += 6
transVec = Vec3(0, 0, 6)

# [...]

while run:

    # ROTATING THE POINTS AROUND X AXIS
    point1 = rotation.rotate(p1, thetax, axis='x')  
    # [...]

    # TRANSLATING THE POINTS OF THE CUBE A LITTLE BIT INTO THE SCREEN
    point1 = point1 + transVec
    # [...]

    # TRANSLATING THE SQUARE SHEET INTO THE SCREEN SPACE
    point1 = transform.worldSpaceTransform(point1, SCREENWIDTH, SCREENHEIGHT)
    # [...]

I recommend to organize the vertex coordinates in lists:

# ASSIGNING 4 Vec3's FOR 4 SIDES OF SQUARE IN NORMALIZED SPACE
s = 1
modelPoints = [Vec3(-s, -s, -s), Vec3(s, -s, -s), Vec3(s, s, -s), Vec3(-s, s, -s)]

# TRANSLATING THE POINTS OF THE CUBE A LITTLE BIT INTO THE SCREEN
transVec = Vec3(0, 0, 6)

# ASSIGNING THE ROTATION ANGLES
thetax = 0

# APPLICATION LOOP
run = True
while run:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False

    D.fill((255, 255, 255))

    # ROTATING THE POINTS AROUND X AXIS
    points = [rotation.rotate(pt, thetax, axis='x') for pt in modelPoints]

    # TRANSLATING THE POINTS OF THE CUBE A LITTLE BIT INTO THE SCREEN
    points = [pt + transVec for pt in points]

    # TRANSLATING THE SQUARE SHEET INTO THE SCREEN SPACE
    points = [transform.worldSpaceTransform(pt, SCREENWIDTH, SCREENHEIGHT) for pt in points]

    # STORING THE POINTS TO A TUPLE SO IT CAN BE DRAWN USING pygame.draw.lines
    points = [(pt.x, pt.y) for pt in points]


See the complete example:

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 __add__(a, b):
        return Vec3(a.x+b.x, a.y+b.y, a.z+b.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 = 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 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
modelPoints = [Vec3(-s, -s, -s), Vec3(s, -s, -s), Vec3(s, s, -s), Vec3(-s, s, -s)]

# TRANSLATING THE POINTS OF THE CUBE A LITTLE BIT INTO THE SCREEN
transVec = Vec3(0, 0, 6)

# ASSIGNING THE ROTATION ANGLES
thetax = 0

# APPLICATION LOOP
run = True
while run:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False

    D.fill((255, 255, 255))

    # ROTATING THE POINTS AROUND X AXIS
    points = [rotation.rotate(pt, thetax, axis='x') for pt in modelPoints]

    # TRANSLATING THE POINTS OF THE CUBE A LITTLE BIT INTO THE SCREEN
    points = [pt + transVec for pt in points]

    # TRANSLATING THE SQUARE SHEET INTO THE SCREEN SPACE
    points = [transform.worldSpaceTransform(pt, SCREENWIDTH, SCREENHEIGHT) for pt in points]

    # STORING THE POINTS TO A TUPLE SO IT CAN BE DRAWN USING pygame.draw.lines
    points = [(pt.x, pt.y) for pt in points]
    
    keys = pygame.key.get_pressed()
    # ROTATE X ?
    if keys[pygame.K_SPACE]:
        thetax -= 0.005

    pygame.draw.lines(D, (0, 0, 0), True, points)
    
    pygame.display.flip()

这篇关于如何在 3D 空间中围绕 x 轴旋转正方形的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆