Repository navigation
Why transparency is sorted
Blending is not commutative, so Renderer.prepare decides the draw order. Opaque meshes come first, nearest first. Translucent meshes follow, furthest first.
A translucent surface tests depth and writes it, as in three.js. depth_write is on by default for a transparent material too. The furthest pane draws first, so a pane behind is already there when a nearer pane mixes over it. A pane drawn first can hide a pane drawn after it, as in three.js. Set depth_write=False on a material to let every surface behind it show through.
Opaque surfaces draw first because they write the depth that stops a pane behind a wall from showing through. Among themselves they draw nearest first. The image does not depend on that order, but the cost does: a fragment that fails the depth test is skipped before it is shaded.
Whether a surface composites decides two things together: how its color combines, and where it sorts. It has to be one answer. It was three: the mesh sorter asked the material, and each rasterizer asked a vertex color. A material with an opaque opacity but a translucent base color sorted as opaque and rasterized as blended. It mixed with the pixel before the surfaces behind it were drawn.
Material.blending is now that one answer. It is inferred from the opacity and the color where it can be. It is stated where it cannot be, because a texture's own alpha is invisible from the material. It travels to both rasterizers as per-triangle state.
The sort is per mesh, by the depth of its node's origin, as three.js sorts. A translucent mesh that overlaps itself is still approximate. The alternative is sorting every triangle every frame.
The kernel accumulates every fragment of a pixel in one pass, in draw order. A fragment that writes depth updates the depth that the next fragments test against, opaque or translucent. The host target writes depth at the same step, so the two backends agree.

examples/glass.mojo renders three translucent panes turning through each other over a solid cube. The sort decides the order, so submitting them in any order gives the same image, and a test asserts it.
Tutorials
How-to
Reference
- Scene graph
- Rotations
- Cameras
- Geometry
- Geometry addons
- Lofts and clipping groups
- Sculptor
- Procedural generators
- Meshes and assets
- Lines
- Points and sprites
- Helpers
- Scene objects
- Animated objects
- Raycasting
- Curves and paths
- Animation
- Skinning
- Materials
- Node materials
- TSL functions
- Lights
- Lighting addons
- Fog
- Textures
- Renderer
- Renderer hooks and material flags
- Rasterization
- GPU backend
- Render target
- Post-processing
- GPU computation
- Compute nodes
- Progressive light map
- Gaussian splats
- Voxel global illumination
- SVG renderer
- Image files
- Image decode queue
- Model files
- More model files
- Exporters
- Scene JSON
- Windowing and controls
- Math
- Norm consumers
- Math addons
- Units
- Coverage tool
- Commands
- Examples
- Benchmarks
- Extensions
- Shared physics
- Physics query snapshots
- Continuous collision
- Tick force restoration
- Water
- Animals
- Buildings
- CARLA
- Femur
- Tibia
- Fibula
- Patella
- Knee
- Muscles
- Vessels
- Lymph
- Nerves
- Integument
- Leg
- Foot
- Pelvis
- Torso
- Arm
- Hand
- Head
- Genome
- Game humanoid
- Humanoid fidelity
- Audio-aligned game faces
- Converted assets
- Mesh quality
- Segment inertia
Explanation
- Software rasterizer
- Scene graph as an array
- Linear color
- Normals are not colors
- Perspective correction
- Fixed-point coverage
- Per-fragment shading
- Mipmaps
- Sorted transparency
- Types and checks
- Render hooks as a trait
- Shared CPU and GPU code
- Bands and threads
- PNG structure
- Extensions beside the port
- CUDA teardown hang
- Mojo compiler hang