Created
December 28, 2025 19:19
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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