Atlaslog
Atlaslog / luma.gl / Tutorial / Render passes and offscreen targets
luma.gl · Tutorial

Render passes and offscreen targets

Quick answer

This tutorial explains that a render pass decides where a batch of draws lands, either the canvas or a framebuffer. It builds a 256 by 256 color texture and depth texture, wraps them in a Framebuffer, renders the scene offscreen, then samples the color attachment through a compositor Model into the screen pass. It stresses attachment usage flags and readBuffer for pixel readback. Readers can add post-processing, picking or multi-pass composition.

Last verified 2026-11-03 · luma.gl 9.4 · Cite this page
Key points
Example
const SIZE = 256;

const colorTex = device.createTexture({
  width: SIZE,
  height: SIZE,
  format: 'rgba8unorm',
  usage: luma.Texture.SAMPLE | luma.Texture.RENDER | luma.Texture.COPY_DST,
  sampler: { minFilter: 'linear', magFilter: 'linear', mipmapFilter: 'none' }
});

const depthTex = device.createTexture({
  width: SIZE,
  height: SIZE,
  format: device.preferredDepthFormat,
  usage: luma.Texture.RENDER
});

const fbo = device.createFramebuffer({
  width: SIZE,
  height: SIZE,
  colorAttachments: [colorTex],
  depthStencilAttachment: depthTex
});

const compositor = new luma.Model(device, {
  vs: quadVS,
  fs: compositeFS,
  attributes: { positions: quadPositions, uvs: quadUVs },
  bufferLayout: [
    { name: 'positions', stepMode: 'vertex', format: 'float32x2' },
    { name: 'uvs', stepMode: 'vertex', format: 'float32x2' }
  ],
  bindings: { uScene: colorTex },
  vertexCount: 6,
  topology: 'triangle-list'
});

function render() {
  const offscreen = device.beginRenderPass({
    framebuffer: fbo,
    clearColor: [0.02, 0.04, 0.07, 1],
    clearDepth: 1
  });
  sceneModel.draw(offscreen);
  offscreen.end();

  const screen = device.beginRenderPass({
    clearColor: [0.04, 0.06, 0.09, 1]
  });
  compositor.draw(screen);
  screen.end();
}
const pickTex = device.createTexture({
  width: width,
  height: height,
  format: 'rgba8unorm',
  usage: luma.Texture.SAMPLE | luma.Texture.RENDER |
         luma.Texture.COPY_SRC | luma.Texture.COPY_DST,
  sampler: { minFilter: 'nearest', magFilter: 'nearest' }
});

const pickFbo = device.createFramebuffer({
  width: width,
  height: height,
  colorAttachments: [pickTex],
  depthStencilAttachment: device.createTexture({
    width: width,
    height: height,
    format: device.preferredDepthFormat,
    usage: luma.Texture.RENDER
  })
});
Notes
Runnable examplesChinese originalAll Tutorials
English edition written for Atlaslog. Code is taken from the original Chinese page and runs unchanged.
Cite this page

Atlaslog (2026). Render passes and offscreen targets. https://www.jingweizhi.com/en/lumagl/tutorials/render-passes.html (accessed 2026-11-03).