Skip to content

Instantly share code, notes, and snippets.

@nitori
Created December 28, 2025 19:19
Show Gist options
  • Select an option

  • Save nitori/0a4d73950a64a3b8e08bc7d44601a9ff to your computer and use it in GitHub Desktop.

Select an option

Save nitori/0a4d73950a64a3b8e08bc7d44601a9ff to your computer and use it in GitHub Desktop.
simple projection example in pure python. no gpu. uses pyglm for some vector math
import pygame
from pyglm.glm import vec2, vec3, vec4
from pyglm import glm
def to_screen(point: vec2, screen_v: vec2) -> vec2:
"""
ndc (-1, 1) to actual screen coords (0, width), (0, height)
"""
n = (point + 1) / 2 # (0, 1)
return vec2(n.x, 1 - n.y) * screen_v
def main():
pygame.init()
win = pygame.Window(size=(800, 800))
screen = win.get_surface()
screen_v = vec2(screen.width, screen.height)
clock = pygame.Clock()
points = [
vec3(1, 1, 1),
vec3(1, 1, -1),
vec3(1, -1, -1),
vec3(1, -1, 1),
vec3(-1, 1, 1),
vec3(-1, 1, -1),
vec3(-1, -1, -1),
vec3(-1, -1, 1),
# vec3(.5, 2, 1),
# vec3(-.5, 2, 1),
]
faces: list[list[int]] = [
[0, 1, 2, 3],
[4, 5, 6, 7],
[0, 4],
[1, 5],
[2, 6],
[3, 7],
# [8, 9],
]
angle = 0.0
axis = glm.normalize(vec3(0, 1, 0))
while True:
delta = clock.tick(60) / 1000
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
return
screen.fill('black')
angle += 2 * delta
rotation = glm.rotate(angle, glm.normalize(axis))
translation = glm.translate(vec3(0, 0, 10))
scale = glm.scale(vec3(1, 1, 1))
model = translation * scale * rotation
view = glm.lookAt(vec3(0, 0, 0), vec3(0, 0, 1), vec3(0, 1, 0))
proj = glm.perspective(glm.radians(60), screen.width / screen.height, 0.1, 100.0)
mvp = proj * view * model
transformed_points = []
for point in points:
clip = mvp * vec4(point, 1)
ndc = vec3(clip) / clip.w
transformed_points.append(to_screen(ndc.xy, screen_v))
for face in faces:
for i, points_index in enumerate(face):
screen_point = transformed_points[points_index]
next_point = transformed_points[face[(i + 1) % len(face)]]
pygame.draw.line(screen, 'red', screen_point, next_point, 2)
win.flip()
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment