在 pygame 中向 3D 渲染器添加简单透视
Adding simple perspective to a 3D renderer in pygame
我已经在 pyGame 中创建了一个 3D 渲染器,但是我现在想添加透视图。我已经尝试了一段时间,但似乎无法弄清楚。
我读到最简单的透视形式是将 x 和 y 坐标乘以 z 坐标的倒数,这样 x 和 y 取决于 z 值。这意味着 x 和 y 的距离应该随着 z 坐标的增加而减小,而 x 和 y 的距离应该随着 z 的减小而增加。我已经设法使它稍微起作用,但是它似乎在累积,所以当我左右旋转盒子时,盒子的背面变得非常小并且似乎累积了负比例而不是保持在固定大小z 距离。
这是我的代码:
wireframe.py:
class Wireframe:
def __init__(self):
self.nodes = np.zeros((0,4))
self.edges = []
def addNodes(self, node_array):
ones_column = np.ones((len(node_array), 1))
ones_added = np.hstack((node_array, ones_column))
self.nodes = np.vstack((self.nodes, ones_added))
def addEdges(self, edgeList):
self.edges += edgeList
def outputNodes(self):
print("\n --- Nodes ---")
for i, (x, y, z, _) in enumerate(self.nodes):
print(" %d: (%.2f, %.2f, %.2f)" % (i, node.x, node.y, node.z))
def outputEdges(self):
print("\n --- Edges ---")
for i, (node1, node2) in enumerate(self.edges):
print(" %d: %d -> %d" % (i, node1, node2))
def translate(self, axis, d):
if axis in ['x', 'y', 'z']:
for node in self.nodes:
setattr(node, axis, getattr(node, axis) + d)
def scale(self, centre_x, centre_y, scale):
for node in self.nodes:
node.x = centre_x + scale * (node.x - centre_x)
node.y = centre_y + scale * (node.y - centre_y)
node.z *= scale
def findCentre(self):
num_nodes = len(self.nodes)
meanX = sum([node.x for node in self.nodes]) / num_nodes
meanY = sum([node.y for node in self.nodes]) / num_nodes
meanZ = sum([node.z for node in self.nodes]) / num_nodes
return (meanX, meanY, meanZ)
def rotateZ(self, centre, radians):
cx, cy, cz = centre
for node in self.nodes:
x = node.x - cx
y = node.y - cy
d = math.hypot(y,x)
theta = math.atan2(y,x) + radians
node.x = cx + d * math.cos(theta)
node.y = cy + d * math.sin(theta)
def rotateX(self, centre, radians):
cx, cy, cz = centre
for node in self.nodes:
y = node.y - cy
z = node.z - cz
d = math.hypot(y,z)
theta = math.atan2(y, z) + radians
node.z = cz + d * math.cos(theta)
node.y = cy + d * math.sin(theta)
def rotateY(self, centre, radians):
cx, cy, cz = centre
for node in self.nodes:
x = node.x - cx
z = node.z - cz
d = math.hypot(x, z)
theta = math.atan2(x, z) + radians
node.z = cz + d * math.cos(theta)
node.x = cx + d * math.sin(theta)
def transform(self, matrix):
self.nodes = np.dot(self.nodes, matrix)
def transform_for_perspective(self):
for node in self.nodes:
print(node[0], node[1], node[2])
if node[2] != 0:
node[0] = node[0]*(1/(1-(node[2]*0.00005)))
node[1] = node[1]*(1/(1-(node[2]*0.00005)))
node[2] = node[2]*1
def translationMatrix(self, dx=0, dy=0, dz=0):
return np.array([[1,0,0,0],
[0,1,0,0],
[0,0,1,0],
[dx,dy,dz,1]])
def scaleMatrix(self, sx=0, sy=0, sz=0):
return np.array([[sx, 0, 0, 0],
[0, sy, 0, 0],
[0, 0, sz, 0],
[0, 0, 0, 1]])
def rotateXMatrix(self, radians):
c = np.cos(radians)
s = np.sin(radians)
return np.array([[1,0,0,0],
[0,c,-s,0],
[0,s,c,0],
[0,0,0,1]])
def rotateYMatrix(self, radians):
c = np.cos(radians)
s = np.sin(radians)
return np.array([[c,0,s,0],
[0,1,0,0],
[-s,0,c,0],
[0,0,0,1]])
def rotateZMatrix(self, radians):
c = np.cos(radians)
s = np.sin(radians)
return np.array([[c,-s, 0, 0],
[s,c,0,0],
[0,0,1,0],
[0,0,0,1]])
def movCamera(self, tilt, pan):
return np.array([[1,0,0,200],
[0,1,0,0],
[pan,tilt,1,0],
[0,0,0,0]])
projectionViewer.py
from wireframe import *
import pygame
import numpy as np
class ProjectionViewer:
''' Displays 3D Objects on a Pygame Screen '''
def __init__(self, width, height):
self.width = width
self.height = height
self.screen = pygame.display.set_mode((width, height))
pygame.display.set_caption('Wireframe Display')
self.background = (10,10,50)
self.wireframes = {}
self.displayNodes = True
self.displayEdges = True
self.nodeColour = (255,255,255)
self.edgeColour = (200,200,200)
self.nodeRadius = 4
def run(self):
key_to_function = {
pygame.K_LEFT: (lambda x: x.translateAll([-10, 0, 0])),
pygame.K_RIGHT:(lambda x: x.translateAll([ 10, 0, 0])),
pygame.K_DOWN: (lambda x: x.translateAll([0, 10, 0])),
pygame.K_UP: (lambda x: x.translateAll([0, -10, 0])),
pygame.K_a: (lambda x: x.rotate_about_Center('Y', -0.08)),
pygame.K_d: (lambda x: x.rotate_about_Center('Y', 0.08)),
pygame.K_w: (lambda x: x.rotate_about_Center('X', -0.08)),
pygame.K_s: (lambda x: x.rotate_about_Center('X', 0.08)),
pygame.K_EQUALS: (lambda x: x.scale_centre([1.25,1.25,1.25])),
pygame.K_MINUS: (lambda x: x.scale_centre([0.8,0.8,0.8])),
pygame.K_q: (lambda x: x.rotateAll('X', 0.1)),
pygame.K_z: (lambda x: x.rotateAll('Z', 0.1)),
pygame.K_x: (lambda x: x.rotateAll('Z', -0.1)),
pygame.K_p: (lambda x: x.perspectiveMode()),
pygame.K_t: (lambda x: x.translate_Camera())
}
running = True
flag = False
while running:
keys = pygame.key.get_pressed()
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if keys[pygame.K_LEFT]:
key_to_function[pygame.K_LEFT](self)
if keys[pygame.K_RIGHT]:
key_to_function[pygame.K_RIGHT](self)
if keys[pygame.K_DOWN]:
key_to_function[pygame.K_DOWN](self)
if keys[pygame.K_UP]:
key_to_function[pygame.K_UP](self)
if keys[pygame.K_EQUALS]:
key_to_function[pygame.K_EQUALS](self)
if keys[pygame.K_MINUS]:
key_to_function[pygame.K_MINUS](self)
if keys[pygame.K_LEFT]:
key_to_function[pygame.K_LEFT](self)
if keys[pygame.K_q]:
key_to_function[pygame.K_q](self)
if keys[pygame.K_w]:
key_to_function[pygame.K_w](self)
if keys[pygame.K_a]:
key_to_function[pygame.K_a](self)
if keys[pygame.K_s]:
key_to_function[pygame.K_s](self)
if keys[pygame.K_z]:
key_to_function[pygame.K_z](self)
if keys[pygame.K_x]:
key_to_function[pygame.K_x](self)
if keys[pygame.K_p]:
key_to_function[pygame.K_p](self)
if keys[pygame.K_t]:
key_to_function[pygame.K_t](self)
if keys[pygame.K_d]:
key_to_function[pygame.K_d](self)
self.display()
pygame.display.flip()
def addWireframe(self, name, wireframe):
self.wireframes[name] = wireframe
#translate to center
wf = Wireframe()
matrix = wf.translationMatrix(-self.width/2,-self.height/2,0)
for wireframe in self.wireframes.values():
wireframe.transform(matrix)
wf = Wireframe()
matrix = wf.translationMatrix(self.width,self.height,0)
for wireframe in self.wireframes.values():
wireframe.transform(matrix)
def display(self):
self.screen.fill(self.background)
for wireframe in self.wireframes.values():
if self.displayEdges:
for n1, n2 in wireframe.edges:
pygame.draw.aaline(self.screen, self.edgeColour, wireframe.nodes[n1][:2], wireframe.nodes[n2][:2],1)
wireframe.transform_for_perspective()
if self.displayNodes:
for node in wireframe.nodes:
pygame.draw.circle(self.screen, self.nodeColour, (int(node[0]), int(node[1])), self.nodeRadius, 0)
def translateAll(self, vector):
''' Translate all wireframes along a given axis by d units '''
wf = Wireframe()
matrix = wf.translationMatrix(*vector)
for wireframe in self.wireframes.values():
wireframe.transform(matrix)
def scaleAll(self, vector):
wf = Wireframe()
matrix = wf.scaleMatrix(*vector)
for wireframe in self.wireframes.values():
wireframe.transform(matrix)
def rotateAll(self, axis, theta):
wf = Wireframe()
if axis == 'X':
matrix = wf.rotateXMatrix(theta)
elif axis == 'Y':
matrix = wf.rotateYMatrix(theta)
elif axis == 'Z':
matrix = wf.rotateZMatrix(theta)
for wireframe in self.wireframes.values():
wireframe.transform(matrix)
#wireframe.transform_for_perspective()
def moveCameraX(self,x,y):
wf = Wireframe()
matrix = wf.movCamera(x,y)
print("test")
for wireframe in self.wireframes.values():
wireframe.transform(matrix)
def moveCameraZ(self,x,y):
wf = Wireframe()
matrix = wf.testMat((0,0,val))
for wireframe in self.wireframes.values():
wireframe.transform(matrix)
def perspectiveMode(self):
#First translate the centre of screen to 0,0
wf = Wireframe()
matrix = wf.translationMatrix(-self.width/2,-self.height/2,0)
for wireframe in self.wireframes.values():
wireframe.transform(matrix)
#perform the perspectivecorrection
wf = Wireframe()
matrix = wf.translationMatrix(-self.width/2,-self.height/2,0)
for wireframe in self.wireframes.values():
matrix = wf.perspectiveCorrection(1.2)
wireframe.transform(matrix)
wf = Wireframe()
matrix = wf.translationMatrix(self.width/2,self.height/2,0)
for wireframe in self.wireframes.values():
wireframe.transform(matrix)
def rotate_about_Center(self, Axis, theta):
#First translate Centre of screen to 0,0
wf = Wireframe()
matrix = wf.translationMatrix(-self.width/2,-self.height/2,0)
for wireframe in self.wireframes.values():
wireframe.transform(matrix)
#Do Rotation
wf = Wireframe()
if Axis == 'X':
matrix = wf.rotateXMatrix(theta)
elif Axis == 'Y':
matrix = wf.rotateYMatrix(theta)
elif Axis == 'Z':
matrix = wf.rotateZMatrix(theta)
for wireframe in self.wireframes.values():
wireframe.transform(matrix)
#Translate back to centre of screen
wf = Wireframe()
matrix = wf.translationMatrix(self.width/2,self.height/2,0)
for wireframe in self.wireframes.values():
wireframe.transform(matrix)
#Do perspective if needed
def scale_centre(self, vector):
#Transform center of screen to origin
wf = Wireframe()
matrix = wf.translationMatrix(-self.width/2,-self.height/2,0)
for wireframe in self.wireframes.values():
wireframe.transform(matrix)
#Scale the origin by vector
wf = Wireframe()
matrix = wf.scaleMatrix(*vector)
for wireframe in self.wireframes.values():
wireframe.transform(matrix)
wf = Wireframe()
matrix = wf.translationMatrix(self.width/2,self.height/2,0)
for wireframe in self.wireframes.values():
wireframe.transform(matrix)
def add_perspective(self):
for wireframe in self.wireframes.values():
for node in wireframe.nodes:
if node[2] != 0:
print("Point ----------")
print("x node", node[0])
print("y node", node[1])
print("z node", node[2])
node[0] = node[0] + (10/node[2])
node[1] = node[1] + (10/node[2])
main.py
from projectionViewer import ProjectionViewer
import wireframe
import numpy as np
cube = wireframe.Wireframe()
cube_nodes = [(x, y, z) for x in (-100, 100) for y in (-100, 100) for z in (-100, 100)]
print(cube_nodes)
cube.addNodes(np.array(cube_nodes))
cube.addEdges([(n, n + 4) for n in range(0, 4)])
cube.addEdges([(n, n + 1) for n in range(0, 8, 2)])
cube.addEdges([(n, n + 2) for n in (0, 1, 4, 5)])
pv = ProjectionViewer(1200, 1000)
pv.addWireframe('cube', cube)
pv.run()
执行相乘的代码在线框文件和 transform_for_perspective() 函数中。
def transform_for_perspective(self):
for node in self.nodes:
print(node[0], node[1], node[2])
if node[2] != 0:
node[0] = node[0]*(1/(1-(node[2]*0.00005)))
node[1] = node[1]*(1/(1-(node[2]*0.00005)))
node[2] = node[2]*1
如果有人能告诉我我哪里出错了,并解释我需要按什么顺序调用透视矩阵,即旋转然后透视或透视然后旋转。
此外,因为 Pygame 从左上角的 (0,0) 开始,这意味着如果我想围绕屏幕中心旋转,我必须平移屏幕中心,执行旋转矩阵,然后将其平移回中心。这对透视意味着什么?我是否必须将屏幕中心平移到左上角,然后执行透视矩阵,然后再平移回来?
如有任何帮助,我们将不胜感激。
您在 transform_for_perspective
中应用的转换只能应用一次。但是,您似乎在每一帧都调用它,并且由于它将输出存储在同一个变量 (self.nodes
) 中,因此它被应用了很多次。
考虑将该转换的输出保存在新字段中(例如 self.perspective_nodes
)。
此外,转换对我不起作用,我尝试做一些变化并想出了这个:
class Wireframe:
def __init__(self):
self.nodes = np.zeros((0, 4))
self.perspective_nodes = None
self.edges = []
....
def transform_for_perspective(self, center):
self.perspective_nodes = self.nodes.copy()
for i in range(len(self.nodes)):
node = self.nodes[i]
p_node = self.perspective_nodes[i]
print(node[0], node[1], node[2])
if node[2] != 0:
p_node[0] = center[0] + (node[0]-center[0])*250/(200-(node[2]))
p_node[1] = center[1] + (node[1]-center[1])*250/(200-(node[2]))
p_node[2] = node[2] * 1
您还需要修改projectionViewer中的显示:
def display(self):
self.screen.fill(self.background)
for wireframe in self.wireframes.values():
wireframe.transform_for_perspective((self.width/2, self.height/2))
if self.displayNodes:
for node in wireframe.perspective_nodes:
pygame.draw.circle(self.screen, self.nodeColour, (int(
node[0]), int(node[1])), self.nodeRadius, 0)
if self.displayEdges:
for n1, n2 in wireframe.edges:
pygame.draw.aaline(
self.screen, self.edgeColour, wireframe.perspective_nodes[n1][:2], wireframe.perspective_nodes[n2][:2], 1)
我已经在 pyGame 中创建了一个 3D 渲染器,但是我现在想添加透视图。我已经尝试了一段时间,但似乎无法弄清楚。
我读到最简单的透视形式是将 x 和 y 坐标乘以 z 坐标的倒数,这样 x 和 y 取决于 z 值。这意味着 x 和 y 的距离应该随着 z 坐标的增加而减小,而 x 和 y 的距离应该随着 z 的减小而增加。我已经设法使它稍微起作用,但是它似乎在累积,所以当我左右旋转盒子时,盒子的背面变得非常小并且似乎累积了负比例而不是保持在固定大小z 距离。
这是我的代码:
wireframe.py:
class Wireframe:
def __init__(self):
self.nodes = np.zeros((0,4))
self.edges = []
def addNodes(self, node_array):
ones_column = np.ones((len(node_array), 1))
ones_added = np.hstack((node_array, ones_column))
self.nodes = np.vstack((self.nodes, ones_added))
def addEdges(self, edgeList):
self.edges += edgeList
def outputNodes(self):
print("\n --- Nodes ---")
for i, (x, y, z, _) in enumerate(self.nodes):
print(" %d: (%.2f, %.2f, %.2f)" % (i, node.x, node.y, node.z))
def outputEdges(self):
print("\n --- Edges ---")
for i, (node1, node2) in enumerate(self.edges):
print(" %d: %d -> %d" % (i, node1, node2))
def translate(self, axis, d):
if axis in ['x', 'y', 'z']:
for node in self.nodes:
setattr(node, axis, getattr(node, axis) + d)
def scale(self, centre_x, centre_y, scale):
for node in self.nodes:
node.x = centre_x + scale * (node.x - centre_x)
node.y = centre_y + scale * (node.y - centre_y)
node.z *= scale
def findCentre(self):
num_nodes = len(self.nodes)
meanX = sum([node.x for node in self.nodes]) / num_nodes
meanY = sum([node.y for node in self.nodes]) / num_nodes
meanZ = sum([node.z for node in self.nodes]) / num_nodes
return (meanX, meanY, meanZ)
def rotateZ(self, centre, radians):
cx, cy, cz = centre
for node in self.nodes:
x = node.x - cx
y = node.y - cy
d = math.hypot(y,x)
theta = math.atan2(y,x) + radians
node.x = cx + d * math.cos(theta)
node.y = cy + d * math.sin(theta)
def rotateX(self, centre, radians):
cx, cy, cz = centre
for node in self.nodes:
y = node.y - cy
z = node.z - cz
d = math.hypot(y,z)
theta = math.atan2(y, z) + radians
node.z = cz + d * math.cos(theta)
node.y = cy + d * math.sin(theta)
def rotateY(self, centre, radians):
cx, cy, cz = centre
for node in self.nodes:
x = node.x - cx
z = node.z - cz
d = math.hypot(x, z)
theta = math.atan2(x, z) + radians
node.z = cz + d * math.cos(theta)
node.x = cx + d * math.sin(theta)
def transform(self, matrix):
self.nodes = np.dot(self.nodes, matrix)
def transform_for_perspective(self):
for node in self.nodes:
print(node[0], node[1], node[2])
if node[2] != 0:
node[0] = node[0]*(1/(1-(node[2]*0.00005)))
node[1] = node[1]*(1/(1-(node[2]*0.00005)))
node[2] = node[2]*1
def translationMatrix(self, dx=0, dy=0, dz=0):
return np.array([[1,0,0,0],
[0,1,0,0],
[0,0,1,0],
[dx,dy,dz,1]])
def scaleMatrix(self, sx=0, sy=0, sz=0):
return np.array([[sx, 0, 0, 0],
[0, sy, 0, 0],
[0, 0, sz, 0],
[0, 0, 0, 1]])
def rotateXMatrix(self, radians):
c = np.cos(radians)
s = np.sin(radians)
return np.array([[1,0,0,0],
[0,c,-s,0],
[0,s,c,0],
[0,0,0,1]])
def rotateYMatrix(self, radians):
c = np.cos(radians)
s = np.sin(radians)
return np.array([[c,0,s,0],
[0,1,0,0],
[-s,0,c,0],
[0,0,0,1]])
def rotateZMatrix(self, radians):
c = np.cos(radians)
s = np.sin(radians)
return np.array([[c,-s, 0, 0],
[s,c,0,0],
[0,0,1,0],
[0,0,0,1]])
def movCamera(self, tilt, pan):
return np.array([[1,0,0,200],
[0,1,0,0],
[pan,tilt,1,0],
[0,0,0,0]])
projectionViewer.py
from wireframe import *
import pygame
import numpy as np
class ProjectionViewer:
''' Displays 3D Objects on a Pygame Screen '''
def __init__(self, width, height):
self.width = width
self.height = height
self.screen = pygame.display.set_mode((width, height))
pygame.display.set_caption('Wireframe Display')
self.background = (10,10,50)
self.wireframes = {}
self.displayNodes = True
self.displayEdges = True
self.nodeColour = (255,255,255)
self.edgeColour = (200,200,200)
self.nodeRadius = 4
def run(self):
key_to_function = {
pygame.K_LEFT: (lambda x: x.translateAll([-10, 0, 0])),
pygame.K_RIGHT:(lambda x: x.translateAll([ 10, 0, 0])),
pygame.K_DOWN: (lambda x: x.translateAll([0, 10, 0])),
pygame.K_UP: (lambda x: x.translateAll([0, -10, 0])),
pygame.K_a: (lambda x: x.rotate_about_Center('Y', -0.08)),
pygame.K_d: (lambda x: x.rotate_about_Center('Y', 0.08)),
pygame.K_w: (lambda x: x.rotate_about_Center('X', -0.08)),
pygame.K_s: (lambda x: x.rotate_about_Center('X', 0.08)),
pygame.K_EQUALS: (lambda x: x.scale_centre([1.25,1.25,1.25])),
pygame.K_MINUS: (lambda x: x.scale_centre([0.8,0.8,0.8])),
pygame.K_q: (lambda x: x.rotateAll('X', 0.1)),
pygame.K_z: (lambda x: x.rotateAll('Z', 0.1)),
pygame.K_x: (lambda x: x.rotateAll('Z', -0.1)),
pygame.K_p: (lambda x: x.perspectiveMode()),
pygame.K_t: (lambda x: x.translate_Camera())
}
running = True
flag = False
while running:
keys = pygame.key.get_pressed()
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if keys[pygame.K_LEFT]:
key_to_function[pygame.K_LEFT](self)
if keys[pygame.K_RIGHT]:
key_to_function[pygame.K_RIGHT](self)
if keys[pygame.K_DOWN]:
key_to_function[pygame.K_DOWN](self)
if keys[pygame.K_UP]:
key_to_function[pygame.K_UP](self)
if keys[pygame.K_EQUALS]:
key_to_function[pygame.K_EQUALS](self)
if keys[pygame.K_MINUS]:
key_to_function[pygame.K_MINUS](self)
if keys[pygame.K_LEFT]:
key_to_function[pygame.K_LEFT](self)
if keys[pygame.K_q]:
key_to_function[pygame.K_q](self)
if keys[pygame.K_w]:
key_to_function[pygame.K_w](self)
if keys[pygame.K_a]:
key_to_function[pygame.K_a](self)
if keys[pygame.K_s]:
key_to_function[pygame.K_s](self)
if keys[pygame.K_z]:
key_to_function[pygame.K_z](self)
if keys[pygame.K_x]:
key_to_function[pygame.K_x](self)
if keys[pygame.K_p]:
key_to_function[pygame.K_p](self)
if keys[pygame.K_t]:
key_to_function[pygame.K_t](self)
if keys[pygame.K_d]:
key_to_function[pygame.K_d](self)
self.display()
pygame.display.flip()
def addWireframe(self, name, wireframe):
self.wireframes[name] = wireframe
#translate to center
wf = Wireframe()
matrix = wf.translationMatrix(-self.width/2,-self.height/2,0)
for wireframe in self.wireframes.values():
wireframe.transform(matrix)
wf = Wireframe()
matrix = wf.translationMatrix(self.width,self.height,0)
for wireframe in self.wireframes.values():
wireframe.transform(matrix)
def display(self):
self.screen.fill(self.background)
for wireframe in self.wireframes.values():
if self.displayEdges:
for n1, n2 in wireframe.edges:
pygame.draw.aaline(self.screen, self.edgeColour, wireframe.nodes[n1][:2], wireframe.nodes[n2][:2],1)
wireframe.transform_for_perspective()
if self.displayNodes:
for node in wireframe.nodes:
pygame.draw.circle(self.screen, self.nodeColour, (int(node[0]), int(node[1])), self.nodeRadius, 0)
def translateAll(self, vector):
''' Translate all wireframes along a given axis by d units '''
wf = Wireframe()
matrix = wf.translationMatrix(*vector)
for wireframe in self.wireframes.values():
wireframe.transform(matrix)
def scaleAll(self, vector):
wf = Wireframe()
matrix = wf.scaleMatrix(*vector)
for wireframe in self.wireframes.values():
wireframe.transform(matrix)
def rotateAll(self, axis, theta):
wf = Wireframe()
if axis == 'X':
matrix = wf.rotateXMatrix(theta)
elif axis == 'Y':
matrix = wf.rotateYMatrix(theta)
elif axis == 'Z':
matrix = wf.rotateZMatrix(theta)
for wireframe in self.wireframes.values():
wireframe.transform(matrix)
#wireframe.transform_for_perspective()
def moveCameraX(self,x,y):
wf = Wireframe()
matrix = wf.movCamera(x,y)
print("test")
for wireframe in self.wireframes.values():
wireframe.transform(matrix)
def moveCameraZ(self,x,y):
wf = Wireframe()
matrix = wf.testMat((0,0,val))
for wireframe in self.wireframes.values():
wireframe.transform(matrix)
def perspectiveMode(self):
#First translate the centre of screen to 0,0
wf = Wireframe()
matrix = wf.translationMatrix(-self.width/2,-self.height/2,0)
for wireframe in self.wireframes.values():
wireframe.transform(matrix)
#perform the perspectivecorrection
wf = Wireframe()
matrix = wf.translationMatrix(-self.width/2,-self.height/2,0)
for wireframe in self.wireframes.values():
matrix = wf.perspectiveCorrection(1.2)
wireframe.transform(matrix)
wf = Wireframe()
matrix = wf.translationMatrix(self.width/2,self.height/2,0)
for wireframe in self.wireframes.values():
wireframe.transform(matrix)
def rotate_about_Center(self, Axis, theta):
#First translate Centre of screen to 0,0
wf = Wireframe()
matrix = wf.translationMatrix(-self.width/2,-self.height/2,0)
for wireframe in self.wireframes.values():
wireframe.transform(matrix)
#Do Rotation
wf = Wireframe()
if Axis == 'X':
matrix = wf.rotateXMatrix(theta)
elif Axis == 'Y':
matrix = wf.rotateYMatrix(theta)
elif Axis == 'Z':
matrix = wf.rotateZMatrix(theta)
for wireframe in self.wireframes.values():
wireframe.transform(matrix)
#Translate back to centre of screen
wf = Wireframe()
matrix = wf.translationMatrix(self.width/2,self.height/2,0)
for wireframe in self.wireframes.values():
wireframe.transform(matrix)
#Do perspective if needed
def scale_centre(self, vector):
#Transform center of screen to origin
wf = Wireframe()
matrix = wf.translationMatrix(-self.width/2,-self.height/2,0)
for wireframe in self.wireframes.values():
wireframe.transform(matrix)
#Scale the origin by vector
wf = Wireframe()
matrix = wf.scaleMatrix(*vector)
for wireframe in self.wireframes.values():
wireframe.transform(matrix)
wf = Wireframe()
matrix = wf.translationMatrix(self.width/2,self.height/2,0)
for wireframe in self.wireframes.values():
wireframe.transform(matrix)
def add_perspective(self):
for wireframe in self.wireframes.values():
for node in wireframe.nodes:
if node[2] != 0:
print("Point ----------")
print("x node", node[0])
print("y node", node[1])
print("z node", node[2])
node[0] = node[0] + (10/node[2])
node[1] = node[1] + (10/node[2])
main.py
from projectionViewer import ProjectionViewer
import wireframe
import numpy as np
cube = wireframe.Wireframe()
cube_nodes = [(x, y, z) for x in (-100, 100) for y in (-100, 100) for z in (-100, 100)]
print(cube_nodes)
cube.addNodes(np.array(cube_nodes))
cube.addEdges([(n, n + 4) for n in range(0, 4)])
cube.addEdges([(n, n + 1) for n in range(0, 8, 2)])
cube.addEdges([(n, n + 2) for n in (0, 1, 4, 5)])
pv = ProjectionViewer(1200, 1000)
pv.addWireframe('cube', cube)
pv.run()
执行相乘的代码在线框文件和 transform_for_perspective() 函数中。
def transform_for_perspective(self):
for node in self.nodes:
print(node[0], node[1], node[2])
if node[2] != 0:
node[0] = node[0]*(1/(1-(node[2]*0.00005)))
node[1] = node[1]*(1/(1-(node[2]*0.00005)))
node[2] = node[2]*1
如果有人能告诉我我哪里出错了,并解释我需要按什么顺序调用透视矩阵,即旋转然后透视或透视然后旋转。
此外,因为 Pygame 从左上角的 (0,0) 开始,这意味着如果我想围绕屏幕中心旋转,我必须平移屏幕中心,执行旋转矩阵,然后将其平移回中心。这对透视意味着什么?我是否必须将屏幕中心平移到左上角,然后执行透视矩阵,然后再平移回来?
如有任何帮助,我们将不胜感激。
您在 transform_for_perspective
中应用的转换只能应用一次。但是,您似乎在每一帧都调用它,并且由于它将输出存储在同一个变量 (self.nodes
) 中,因此它被应用了很多次。
考虑将该转换的输出保存在新字段中(例如 self.perspective_nodes
)。
此外,转换对我不起作用,我尝试做一些变化并想出了这个:
class Wireframe:
def __init__(self):
self.nodes = np.zeros((0, 4))
self.perspective_nodes = None
self.edges = []
....
def transform_for_perspective(self, center):
self.perspective_nodes = self.nodes.copy()
for i in range(len(self.nodes)):
node = self.nodes[i]
p_node = self.perspective_nodes[i]
print(node[0], node[1], node[2])
if node[2] != 0:
p_node[0] = center[0] + (node[0]-center[0])*250/(200-(node[2]))
p_node[1] = center[1] + (node[1]-center[1])*250/(200-(node[2]))
p_node[2] = node[2] * 1
您还需要修改projectionViewer中的显示:
def display(self):
self.screen.fill(self.background)
for wireframe in self.wireframes.values():
wireframe.transform_for_perspective((self.width/2, self.height/2))
if self.displayNodes:
for node in wireframe.perspective_nodes:
pygame.draw.circle(self.screen, self.nodeColour, (int(
node[0]), int(node[1])), self.nodeRadius, 0)
if self.displayEdges:
for n1, n2 in wireframe.edges:
pygame.draw.aaline(
self.screen, self.edgeColour, wireframe.perspective_nodes[n1][:2], wireframe.perspective_nodes[n2][:2], 1)