我目前正在研究 3d 渲染,我正在尝试在不添加任何库的情况下完成它(pygame、math、sys 除外)。我做了很多研究,但仍然不能完全理解数学(我在 wikipedia 上使用方法)。它确实输出了正确的坐标,但有时会严重变形。这是结果的样子。我不太了解矩阵,所以我很难调试。我真的可以使用一些帮助来了解它为什么会变形或只是矩阵如何工作,非常感谢!
这是我的代码:
import pygame
import math
import sys
width = 600
height = 480
class Triangle:
def __init__(self, verts):
for i in range(len(verts)):
for r in range(3):
verts[i][r] = int(float(verts[i][r]))
self.verts = verts
Triangle 类是为了让代码更容易阅读
def getobj(filename):
verts = []
ind = []
triangles = []
data = None
with open(filename, 'r') as f:
data = f.readline()
while data:
data = data.rstrip("\n").split(" ")
if data[0] == 'v':
verts.append([-1 * int(float(data[1])), -1 * int(float(data[2])), -1 * int(float(data[3]))])
elif data[0] == 'f':
v = []
t = []
n = []
for i in range(3):
l = data[i + 1].split('/')
v.append(int(l[0]))
t.append(int(l[1]))
n.append(int(l[2]))
ind.append([v, t, n])
data = f.readline()
for points in ind:
v = []
for i in points[0]:
v.append(verts[i - 1])
triangles.append(Triangle(v))
return triangles
从 obj 文件中获取顶点和线
class Matrix:
def __init__(self, matrix):
self.matrix = matrix
self.height = len(matrix)
self.width = len(matrix[0])
def __add__(self, other):
result = []
for h in range(self.height):
row = []
for w in range(self.width):
row.append(self.matrix[h][w] + other.matrix[h][w])
result.append(row)
return Matrix(result)
def __sub__(self, other):
result = []
for h in range(self.height):
row = []
for w in range(self.width):
row.append(self.matrix[h][w] - other.matrix[h][w])
result.append(row)
return Matrix(result)
def __mul__(self, other):
result = []
for h in range(self.height):
SUM = 0
for w in range(self.width):
SUM += self.matrix[h][w] * other.matrix[0][w]
result.append(SUM)
return Matrix([result])
Matrix类是为了让矩阵的加减乘法更简单
class Cam:
def __init__(self):
self.pos = Matrix([[0, 0, 0]])
self.rot = [0, 0, 0]
def getcoord(self, coord):
m = Matrix([coord])
data = m - self.pos
F = Matrix([
[math.cos(self.rot[2]), math.sin(self.rot[2]), 0],
[-1 * math.sin(self.rot[2]), math.cos(self.rot[2]), 0],
[0, 0, 1]
])
S = Matrix([
[math.cos(self.rot[1]), 0, -1 * math.sin(self.rot[1])],
[0, 1, 0],
[math.sin(self.rot[1]), 0, math.cos(self.rot[1])]
])
T = Matrix([
[1, 0, 0],
[0, math.cos(self.rot[0]), math.sin(self.rot[0])],
[0, -1 * math.sin(self.rot[0]), math.cos(self.rot[0])]
])
data = F * data
data = S * data
data = T * data
x = (width / 2) * data.matrix[0][0] / data.matrix[0][2]
y = (height / 2) * data.matrix[0][1] / data.matrix[0][2]
return [x, y]
def events(self, event):
if event.type == pygame.MOUSEMOTION:
x, y = event.rel
x /= 300
y /= 300
self.rot[0] -= y
self.rot[1] += x
def update(self, dt, key):
s = dt * 2
if key[pygame.K_LSHIFT]: self.pos.matrix[0][1] -= s
if key[pygame.K_SPACE]: self.pos.matrix[0][1] += s
x, y = s*math.sin(self.rot[1]), s*math.cos(self.rot[1])
if key[pygame.K_w]: self.pos.matrix[0][0] -= x; self.pos.matrix[0][2] -= y
if key[pygame.K_s]: self.pos.matrix[0][0] += x; self.pos.matrix[0][2] += y
if key[pygame.K_a]: self.pos.matrix[0][0] += y; self.pos.matrix[0][2] -= x
if key[pygame.K_d]: self.pos.matrix[0][0] -= y; self.pos.matrix[0][2] += x
控制移动和旋转的位置,以及使用投影矩阵的位置。
cam = Cam()
pygame.init()
screen = pygame.display.set_mode((width, height))
clock = pygame.time.Clock()
pygame.event.get()
pygame.mouse.get_rel()
pygame.mouse.set_visible(0)
pygame.event.set_grab(1)
初始化
# tris = getobj("untitled.obj")
tris = [
Triangle([[1, -1, 1], [-1, -1, -1], [-1, -1, 1]]),
Triangle([[-1, -1, -1], [1, 1, -1], [-1, 1, -1]]),
Triangle([[1, -1, -1], [1, 1, 1], [1, 1, -1]]),
Triangle([[-1, 1, 1], [1, 1, -1], [1, 1, 1]]),
Triangle([[-1, -1, 1], [-1, 1, -1], [-1, 1, 1]]),
Triangle([[1, -1, 1], [-1, 1, 1], [1, 1, 1]]),
Triangle([[1, -1, 1], [1, -1, -1], [-1, -1, -1]]),
Triangle([[-1, -1, -1], [1, -1, -1], [1, 1, -1]]),
Triangle([[1, -1, -1], [1, -1, 1], [1, 1, 1]]),
Triangle([[-1, 1, 1], [-1, 1, -1], [1, 1, -1]]),
Triangle([[-1, -1, 1], [-1, -1, -1], [-1, 1, -1]]),
Triangle([[1, -1, 1], [-1, -1, 1], [-1, 1, 1]])
]
我使用的 obj 文件中的数据。
while True:
dt = clock.tick()/1000
screen.fill((255, 255, 255))
for event in pygame.event.get():
if event.type == pygame.QUIT: pygame.quit(); sys.exit()
cam.events(event)
for tri in tris:
coord1 = cam.getcoord(tri.verts[0])
coord2 = cam.getcoord(tri.verts[1])
coord3 = cam.getcoord(tri.verts[2])
pygame.draw.line(screen, (0, 0, 0), (int(coord1[0]), int(coord1[1])), (int(coord2[0]), int(coord2[1])))
pygame.draw.line(screen, (0, 0, 0), (int(coord1[0]), int(coord1[1])), (int(coord3[0]), int(coord3[1])))
pygame.draw.line(screen, (0, 0, 0), (int(coord2[0]), int(coord2[1])), (int(coord3[0]), int(coord3[1])))
pygame.display.flip()
key = pygame.key.get_pressed()
cam.update(dt, key)
在屏幕上绘制点。