retro 3d psx prototype
building a retro 3d psx prototype in godot 4
This prototype started as a tight experiment in combining 90s-era rendering with modern, physics-driven interactions. Built in Godot 4.6, it mixes retro visual quirks with systems like non‑euclidean portals, physics-based object interaction, and an AI that responds to being photographed.
project overview
Rather than just slapping a low-res filter on a modern scene, the goal was to recreate the behaviors of PS1 hardware—vertex jitter, affine texture warping, and low-resolution viewport scaling—so the visuals feel authentic, not pasted on. Underneath that is a playable template with portals, object dragging powered by JoltPhysics3D, and a creepy enemy that freezes when you look at it.
psx shader pipeline
The shader does three main things to sell the retro look:
- Vertex snapping in projection space to create the classic jitter.
- Affine texture mapping to produce texture warping on angled polygons.
- A low native viewport (455x256) with nearest-neighbor scaling for crisp pixels.
Example snippet:
float i = (1.0 - jitter) * min(VIEWPORT_SIZE.x, VIEWPORT_SIZE.y) / 2.0;
float w = (PROJECTION_MATRIX * vec4(VERTEX, 1.0)).w;
VERTEX = round(VERTEX / w * i) / i * w;
non-euclidean portals
Portals use subviewports so each opening renders the scene from the linked portal’s perspective. The linked camera’s transform is updated every frame relative to the player, keeping the view consistent while walking through.
Key idea:
var rel = global_transform.affine_inverse() * player.camera.global_transform
linked_portal.cam.global_transform = linked_portal.global_transform * rel
Portals also trigger a short post-process effect—chromatic aberration and vignette—to make the transition feel weighty.
photographic ai (enemy behaviour, mannequins)
The mannequins follow a simple rule: they stay still while visible and move when you aren’t looking. The camera/photo mechanic ties into this—if you take and keep a photo of a mannequin, it stays frozen until you remove or review that photo.
If destroyed, mannequins break into rigid fragments and the physics engine handles the resulting debris.
graph TD
A[Mannequin State] --> B{Is Player Looking?}
B -->|Yes| C[Freeze Movement]
B -->|No| D{Has Active Photo?}
D -->|Yes| C
D -->|No| E[Move Towards Player]
E --> F{Destroyed?}
F -->|Yes| G[Fracture into Rigid Fragments & Physics Debris]
gameplay systems
- Physical dragging uses Jolt joints instead of kinematic attachments so objects collide and feel heavy.
- The hotbar includes a lantern (dynamic shadows), a revolver (projectiles + recoil), and a camera (snapshots + inventory review).
- Movement includes stamina-backed sprinting, procedural camera bob, and contextual footstep sounds.
tech stack
- Godot 4.6 (Forward Plus)
- JoltPhysics3D for rigid body dynamics
- GDScript for gameplay logic
- Custom spatial shaders for retro and portal effects
why this approach
Putting retro limitations at the core of the design creates tension and helps the game feel distinct. The shaders make environments feel unstable and uncanny, which works well with portals and physics-driven enemy interactions. It’s a small, focused template that’s easy to extend into a tense, atmospheric game.
This retro 3D PSX prototype demonstrates how combining authentic classic hardware limitations with modern physics engine interactions can create a highly atmospheric and tense gameplay loop.