I copied a perspective projection matrix from here (https://jsantell.com/3d-projection/) and applied it to my vertices. It looks ok but without depth. Rotation matrices also work.
When I try to divide all components by the w-component after applying the projection matrix I get some weird geometry. This is what I have understand should be done to apply depth projection.
Below is my code, I'm using python and pygame for drawing the polygons of a cube.
What the cube looks like if I don't divide by w:
With division:
import pygame
import math
import copy
pygame.init()
screen_width = 960
screen_height = 800
screen = pygame.display.set_mode([screen_width, screen_height])
clock = pygame.time.Clock()
FPS = 60
run = True
class Renderer():
def __init__(self):
self.cube = [
[[0, 0, 0, 1], [0, 1, 0, 1], [1, 1, 0, 1]],
[[0, 0, 0, 1], [1, 1, 0, 1], [1, 0, 0, 1]],
[[1, 0, 0, 1], [1, 1, 0, 1], [1, 1, 1, 1]],
[[1, 0, 0, 1], [1, 1, 1, 1], [1, 0, 1, 1]],
[[1, 0, 1, 1], [1, 1, 1, 1], [0, 1, 1, 1]],
[[1, 0, 1, 1], [0, 1, 1, 1], [0, 0, 1, 1]],
[[0, 0, 1, 1], [0, 1, 1, 1], [0, 1, 0, 1]],
[[0, 0, 1, 1], [0, 1, 0, 1], [0, 0, 0, 1]],
[[0, 1, 0, 1], [0, 1, 1, 1], [1, 1, 1, 1]],
[[0, 1, 0, 1], [1, 1, 1, 1], [1, 1, 0, 1]],
[[1, 0, 1, 1], [0, 0, 1, 1], [0, 0, 0, 1]],
[[1, 0, 1, 1], [0, 0, 0, 1], [1, 0, 0, 1]]]
for tri in self.cube:
for point in tri:
point[0] -= 0.5; point[1] -= 0.5; point[2] -= 0.5
a = screen_height/screen_height
n = 1
f = 1000
t = n * math.tan(math.radians(90/2))
b = -t
r = a * t
l = -r
self.projection = [
[(2*n)/(r-l), 0, (r+l)/(r-l), 0],
[0, (2*n)/(t-b), (t+b)/(t-b), 0],
[0, 0, (f+n)/(n-f), (2*f*n)/(n-f)],
[0, 0, -1, 0]
]
self.rot = 0
self.update_roty()
def update_roty(self):
self.rotation_y = [
[math.cos(self.rot), 0, -math.sin(self.rot), 0],
[0, 1, 0, 0],
[math.sin(self.rot), 0, math.cos(self.rot), 0],
[0, 0, 0, 1]]
def Matrix_MultiplyVector(self, i, m):
vx = i[0] * m[0][0] + i[1] * m[0][1] + i[2] * m[0][2] + m[0][3]
vy = i[0] * m[1][0] + i[1] * m[1][1] + i[2] * m[1][2] + m[1][3]
vz = i[0] * m[2][0] + i[1] * m[2][1] + i[2] * m[2][2] + m[2][3]
vw = i[0] * m[3][0] + i[1] * m[3][1] + i[2] * m[3][2] + m[3][3]
vx /= vw
vy /= vw
vz /= vw
vw /= vw
return [vx,vy,vz,vw]
def draw(self):
self.rot += 0.0175
self.update_rotx()
self.update_roty()
self.update_rotz()
a = screen_height/2
for tri in self.cube:
tricopy = copy.deepcopy(tri)
proj_points = []
for i, point in enumerate(tricopy):
tricopy[i] = self.Matrix_MultiplyVector(tricopy[i], self.rotation_y)
tricopy[i] = self.Matrix_MultiplyVector(tricopy[i], self.projection)
tricopy[i][0] *= 50; tricopy[i][0] += a
tricopy[i][1] *= 50; tricopy[i][1] += a
proj_points.append((tricopy[i][0], tricopy[i][1]))
pygame.draw.polygon(screen, [230,168,50], proj_points, 1)
renderer = Renderer()
while run:
screen.fill([47,79,83])
mx, my = pygame.mouse.get_pos()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
pygame.quit()
keys = pygame.key.get_pressed()
renderer.user_input(keys)
renderer.draw()
clock.tick(FPS)
pygame.display.flip()
pygame.quit()```