如何在 3D 中围绕 x 轴旋转正方形 space
How to rotate a square around x-axis in a 3D space
所以我一直在努力学习 3D 渲染的工作原理。我尝试编写一个脚本,目标是在 3D space 中旋转一个平面 (2D) 正方形。我首先在标准化的 space (-1, 1) 中定义了一个正方形。请注意,只有 x 和 y 是标准化的。
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)
然后将点翻译成屏幕:
p1.z += 6
p2.z += 6
p3.z += 6
p4.z += 6
此后的一切都在应用程序循环内完成。我使用以下函数将点缩放到屏幕上并应用投影:
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))
像这样:
# 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))
pygame.draw.lines(D, (0, 0, 0), False, points)
到目前为止一切正常(我认为),因为它按预期绘制了一个正方形。
现在轮换。我尝试了所有轴的旋转,其中 none 有效,但为了具体起见,我将讨论 x 轴。下面是轮换class。我从维基百科复制了旋转矩阵。我不完全确定它们是如何工作的,所以我也不知道它是否与我上面描述的系统兼容。
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))
在将屏幕填充为白色之后,在将点从标准化 space 缩放到屏幕 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
应用旋转后,看起来是在绕x轴运动,但没有旋转。我希望它在原地旋转的同时旋转。我做错了什么?
完整的复制粘贴代码以供参考:
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()
没有必要分别旋转矢量的每个分量。如果你这样做
p1.x = rotation.rotate(p1, thetax, axis='x').x
然后 p1
的 x
组件发生了变化,传递给下一条指令的 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)。你必须在旋转后进行翻译。
将 +
运算符添加到 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)
切勿更改原始顶点坐标 p1
、p2
、p3
和 p4
。计算旋转,然后计算平移:
# 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)
# [...]
我建议在列表中组织顶点坐标:
# 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]
查看完整示例:
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 渲染的工作原理。我尝试编写一个脚本,目标是在 3D space 中旋转一个平面 (2D) 正方形。我首先在标准化的 space (-1, 1) 中定义了一个正方形。请注意,只有 x 和 y 是标准化的。
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)
然后将点翻译成屏幕:
p1.z += 6
p2.z += 6
p3.z += 6
p4.z += 6
此后的一切都在应用程序循环内完成。我使用以下函数将点缩放到屏幕上并应用投影:
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))
像这样:
# 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))
pygame.draw.lines(D, (0, 0, 0), False, points)
到目前为止一切正常(我认为),因为它按预期绘制了一个正方形。
现在轮换。我尝试了所有轴的旋转,其中 none 有效,但为了具体起见,我将讨论 x 轴。下面是轮换class。我从维基百科复制了旋转矩阵。我不完全确定它们是如何工作的,所以我也不知道它是否与我上面描述的系统兼容。
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))
在将屏幕填充为白色之后,在将点从标准化 space 缩放到屏幕 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
应用旋转后,看起来是在绕x轴运动,但没有旋转。我希望它在原地旋转的同时旋转。我做错了什么?
完整的复制粘贴代码以供参考:
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()
没有必要分别旋转矢量的每个分量。如果你这样做
p1.x = rotation.rotate(p1, thetax, axis='x').x
然后 p1
的 x
组件发生了变化,传递给下一条指令的 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)。你必须在旋转后进行翻译。
将 +
运算符添加到 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)
切勿更改原始顶点坐标 p1
、p2
、p3
和 p4
。计算旋转,然后计算平移:
# 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)
# [...]
我建议在列表中组织顶点坐标:
# 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]
查看完整示例:
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()