Log inGet started
World

Builtin Library

Branchmain
Branchesmainpublish
Commit65083fe8f2

README.md

ps1

A full PS1-style retro shader and a worked example of a self-shading fragment() shader — one that returns the final pixel itself instead of describing a PBR surface. It emulates the three signature traits of original PlayStation hardware, each mapped onto a generic engine capability:

  1. Vertex snapping (vertex_clip) — the integer-grid vertex wobble, snapping NDC xy to a low-res grid set by snap_resolution (low = chunkier).
  2. Per-vertex (Gouraud) lighting (vertex_clip writes ClipVertex.color) — the PS1 lit at vertices and the rasterizer interpolated the colour; there was no per-pixel lighting. The shader computes Lambert sun + ambient from the world normal at each vertex and writes it into the vertex colour, which the engine forwards into the interpolated vertex_color the fragment reads. Flat, gradient-banded shading — never a per-pixel highlight.
  3. Affine texture mapping (FragmentData.affine_uv) — the PS1 had no perspective-correct UV interpolation, so textures "swim" / warp across triangles. We sample with the engine's non-perspective-correct affine_uv channel instead of uv, reproducing the warp exactly.

On top of those: the texture is quantized to a tex_resolution×tex_resolution texel grid (the PS1 256×256 texture page) and the final colour is run through a 4×4 Bayer ordered dither + colour-level quantization (the PS1 banded look). No shadow-map sampling — the PS1 had none.

Properties

PropertyTypeDefaultMeaning
base_colorcolor[1,1,1,1]Tint multiplied into the texture.
snap_resolutionfloat160Vertex-snap grid resolution (lower = chunkier wobble).
tex_resolutionfloat256Texture sampled at tex_resolution×tex_resolution texels (PS1 256×256 page).
dither_amountfloat0.06Strength of the Bayer dither applied before quantization.
color_levelsfloat16Number of quantization levels per channel (lower = more banding).
base_color_texturetexture (white)—Albedo texture, sampled affinely in fragment().

Framework capabilities this exercises

  • ClipVertex is a post-transform vertex-shading hook: it carries world-space geometry (world_normal, world_position) and the sun/ambient light context, and writing its color field performs per-vertex (Gouraud) shading — the engine forwards it to the interpolated vertex_color. Leave color untouched and the mesh's per-vertex colour passes through.
  • FragmentData.affine_uv is the primary UV interpolated without perspective correction — always available alongside the perspective-correct uv. Sample with it for the PS1 swim; sample with uv for normal mapping.