Creative Coding

Four-Point Perspective for Godot's 2D Sprites, Without Touching 3D

BunkWire2X8's Perspective Quads adds nodes that pin a texture to four arbitrary corners — the oldest trick in fake-3D, finally as an engine primitive.

Godot’s 2D renderer gives you affine transforms: translate, rotate, scale, skew. What it does not give you is a projective transform — the one where a rectangle becomes an arbitrary quadrilateral and the texture inside it converges toward a vanishing point.

That single missing operation is why fake-3D in 2D engines is fiddly. Perspective Quads, by BunkWire2X8, adds it as a set of nodes.

What’s in it

  • PerspectiveQuad2D — a Node2D that draws a texture across four freely positioned corner points
  • PerspectiveQuadShape — a Resource holding a shape and modulation, so you can blend between several
  • Carousel2D and CarouselAnchor2D — shared view positions for building pseudo-3D carousels
  • PerspectiveViewportContainer2D — like SubViewportContainer, but perspective-warped, with input forwarding

It requires Godot 4.6 or later and is on the Godot Asset Store.

Why the last one is the sleeper feature

PerspectiveViewportContainer2D warps an entire viewport — a whole running scene, UI and all — onto a four-point quad, and keeps input working.

That is the hard part. Warping pixels is a shader. Making a button inside the warped surface still respond to a click means inverting the transform on every input event and mapping the cursor back into the unwarped space. Anyone who has tried this by hand knows it is where the afternoon goes.

With it working, you get things that are otherwise a real effort in a 2D engine: a diegetic screen in a game world — a monitor, an arcade cabinet, a tablet held at an angle — that is a live, interactive scene rather than a texture. A UI that folds. A menu on a surface that isn’t facing you.

The technique underneath

A projective (homography) transform maps four points to four points. It is what a graphics API does per-triangle with perspective-correct interpolation, and it is what 2D renderers deliberately omit because affine transforms are cheaper and sufficient for almost everything.

The lineage is long and worth knowing if you write this kind of code:

Mode 7 on the SNES did it in hardware, per scanline, on a background layer — which is why F-Zero and Mario Kart look the way they do. Ray-cast renderers like Wolfenstein 3D did a restricted version by drawing vertical strips at computed heights. The PlayStation 1’s famous texture warble is the same transform done without perspective correction, interpolating affinely across a triangle, which is why textures swim on large polygons in PS1 games.

Which is the thing to notice about doing this with a modern addon: you get to choose. Perspective-correct for a clean result, or deliberately affine for the PS1 wobble, which is now a legitimate aesthetic rather than a limitation.

What you’d actually build with it

  • Card and board games — cards that tilt and flip in a plane, tables seen at an angle, the whole genre of 2D games that want depth without a 3D pipeline
  • Fake-3D corridors and rooms — walls as quads whose far edges converge, which with a handful of quads gets you a surprisingly convincing space
  • Carousels and cover flow, which is what the dedicated nodes are for
  • Diegetic screens and in-world UI, per above
  • Deliberate retro rendering — Mode 7 pastiche, PS1 warble, anything that wants the texture of early 3D without the cost of real 3D

The broader reason a node beats a shader here: a shader warps pixels, but the engine still thinks the sprite is an axis-aligned rectangle. Hit detection, editor handles, child positioning and input all disagree with what the player sees. A node that owns the transform keeps the editor and the runtime telling the same story, and that is most of the value.