Skip to content

Distance fog for the 3D tier #1622

Description

@obiot

Summary

melonJS has no distance fog — no hits for fog anywhere in src/. For a 3D scene it is the cheapest single thing that stops geometry looking flat and pasted-on: it gives depth cues the projection alone does not, and it hides the far plane so props can pop in without a visible edge.

Every outdoor 3D scene wants it, and there is currently no supported way to get it.

What exists today

  • A custom mesh shader. mesh.addPostEffect(new GLShader(...)) replaces the built-in shading, and the mesh contract passes uProjectionMatrix / uViewMatrix / uModelMatrix with aVertex, so view-space depth → mix(color, uFogColor, f) is a few lines, dual-language GLSL + WGSL in one object. This works, but it means giving up the built-in lit/unlit shading to get fog, and writing it per project.
  • CPU-side tinting of discrete props by distance from the camera. Fine for scattered rocks and trees, wrong for large ground meshes where the gradient has to vary across a single mesh.
  • A post-effect cannot do it: toFrameTexture() is colour only, and no depth texture is exposed to ShaderEffect (WebGPU's depthTexture is internal to the pipeline). Screen-space fog needs depth, so this route is closed without also surfacing a depth attachment.

Proposal

Fog as renderer state consumed by the mesh batchers, so it composes with the existing lit and unlit paths instead of replacing them.

  • application setting and a runtime accessor: fog: { color, near, far } (or density for exponential)
  • uniforms uploaded alongside the existing light block; linear and exp² are both one line in the shader
  • applies to Mesh, InstancedMesh and Sprite3d, after lighting and emissive so a glowing object still fades with distance
  • no-op when unset, so nothing changes for existing scenes
  • Canvas has no shader path, so it ignores fog like the rest of the 3D tier

Open questions

  • Whether fog belongs to the camera (it is a viewing property, and a scene with two cameras could reasonably want two settings) or the application. Camera3d already owns the clip planes fog is defined against, which argues for the camera.
  • Whether to fog the clear colour too, or leave that to the app — a sky that does not match the fog colour is the usual way this looks wrong.
  • Whether Sprite3d billboards should fog by their centre depth or per-fragment.

Notes

Found while checking whether melonJS could build a sledding game from a screenshot — the far mountains fade toward the sky colour, which is what makes the valley read as deep. Everything else in the scene mapped onto existing features; fog and a 3D trail (#1621) were the two gaps.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions