Skip to content

GPU-accelerated parallax background layers (single-quad shader) #1403

@obiot

Description

@obiot

Summary

Render parallax background layers (ImageLayer) as a single screen-aligned quad with UV offset computed in the shader, instead of tiling multiple drawImage calls. The shader handles wrapping, scrolling, and parallax ratio — no CPU-side tile loop needed.

Current State

  • `ImageLayer` in `src/renderable/imagelayer.js` draws the background image by tiling it across the viewport
  • For repeating backgrounds, it calculates how many copies are needed and calls `drawImage()` for each
  • The parallax ratio adjusts the scroll speed, but the CPU still iterates and pushes vertices per tile

Proposed Approach

A fragment shader that:

  • Takes the background texture, scroll offset, and parallax ratio as uniforms
  • Computes wrapped UVs per pixel for seamless tiling
  • Renders the entire layer as one quad — one draw call regardless of viewport size or repeat count
uniform sampler2D uBackground;
uniform vec2 uScrollOffset;   // camera position * parallax ratio
uniform vec2 uTextureSize;    // background image size
uniform vec2 uViewportSize;   // camera viewport size

vec4 apply(vec4 color, vec2 uv) {
    vec2 pixelPos = uv * uViewportSize + uScrollOffset;
    vec2 wrappedUV = fract(pixelPos / uTextureSize);
    return texture2D(uBackground, wrappedUV);
}

Benefits

  • Single draw call per background layer (currently N draw calls for N visible tiles)
  • Smooth sub-pixel scrolling with no seams (GPU handles texture wrapping)
  • Trivially supports both horizontal and vertical repeat
  • Could extend to support scaling, rotation, or distortion effects in the shader

Considerations

  • Canvas fallback: keep existing tile-by-tile rendering for Canvas mode
  • Non-repeating backgrounds: shader can clamp UVs instead of wrapping
  • Multiple background layers: each is still a separate quad (one draw call each)
  • Integration: could be opt-in via `ImageLayer.gpuRendering = true` or automatic when WebGL is active

References

  • `ImageLayer`: `src/renderable/imagelayer.js`
  • `ShaderEffect`: `src/video/webgl/shadereffect.js`

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions