Header menu logo Mibo.Raylib

Layered Rendering

What and Why

Most games need multiple visual layers — a 3D world with a 2D HUD, a minimap in the corner, debug wireframes overlaid on collision boxes. Drawing everything in one renderer creates a mess of state management. Drawing in separate renderers requires knowing how they composite.

The pattern: register multiple renderers in order. The first renderer clears and draws the base layer. Subsequent renderers draw on top without clearing. Each renderer has its own camera and command buffer.

Use Cases

3D world + 2D HUD

The classic setup. A 3D renderer draws the game world. A 2D renderer draws health bars, ammo counters, and text on top.

3D world + minimap

Same as HUD, but the 2D renderer also draws a texture generated from world data — a top-down view of the map in a corner.

Game world + debug overlay

3D renderer draws the game. A second renderer draws wireframe collision boxes, pathfinding lines, and AI state text. Toggle the debug layer on/off by adding or removing the renderer.

Split-screen multiplayer

Two 3D renderers, each with a different camera and viewport. Each renders the full scene from a different perspective.

Picture-in-picture

A security camera feed, rearview mirror, or spectator view. A small 3D renderer draws into a corner of the screen after the main 3D pass.

Screen-space effects

Vignette, scanlines, film grain. A 2D renderer draws full-screen quads with post-processing shaders on top of the game world.

The Technique

Register renderers in order — first registered, first drawn:

Program.mkProgram init update
|> Program.withRenderer (fun () ->
    Renderer2D.createWith Renderer2DConfig.noClear overlayView)
|> Program.withRenderer (fun () ->
    Renderer3D.create pipeline sceneView)

The 2D overlay is registered first so it draws second (on top). The noClear flag prevents it from erasing the 3D scene.

Each renderer has its own view function:

let overlayView (ctx: GameContext) (model: GameModel) (buffer: RenderBuffer2D) =
  drawMinimap model buffer
  drawDiagnostics model buffer
  drawHealthBar model buffer

Renderer behavior

Renderer

Clears screen?

Draws to

Typical content

Renderer3D.create

Yes

3D buffer

World, models, lights, shadows

Renderer2D.createWith noClear

No

2D buffer

HUD, minimap, text, debug

Key Insight

Renderer order is execution order. The 3D renderer clears the screen and draws the world. The 2D renderer draws on top without clearing. If you reversed the order, the 3D clear would erase the 2D content. The noClear config is the critical detail — without it, overlays don't work.

You can add more than two renderers. Each one composites on top of the previous. Just make sure only the base renderer clears the screen.

When to use

See also

val overlayView: ctx: 'a -> model: 'b -> buffer: 'c -> 'd
val ctx: 'a
val model: 'b
val buffer: 'c

Type something to start searching.