All guides

Porting Shadertoy Buffer A with Ping-Pong Targets

Buffer A often reads its own previous-frame result. WebGL does not allow sampling the texture currently being written, so keep separate read and write targets. Swap after each update and let the display pass read the newly completed texture.

Fix the problem and environment

Buffer A often reads its own previous-frame result. WebGL does not allow sampling the texture currently being written, so keep separate read and write targets. Swap after each update and let the display pass read the newly completed texture.

The controlling code

const readTarget=new THREE.WebGLRenderTarget(width,height);
const writeTarget=new THREE.WebGLRenderTarget(width,height);
let read=readTarget,write=writeTarget;
// Initialize both once; the simulation shader samples uniforms.previous.
renderer.setRenderTarget(read);renderer.clear();
renderer.setRenderTarget(write);renderer.clear();
function update(){
  simulationMaterial.uniforms.previous.value=read.texture;
  renderer.setRenderTarget(write);
  renderer.render(simulationScene,simulationCamera);
  [read,write]=[write,read];
  displayMaterial.uniforms.state.value=read.texture;
  renderer.setRenderTarget(null);
  renderer.render(displayScene,displayCamera);
}

Verify in order

Start with the trail example: add a new point and fade old color each frame. Then inspect the two height states of the water example. Display raw state while debugging to distinguish a broken simulation from a broken visualization.

Scope and common mistakes

Create simulation/display shaders, uniforms, scenes and cameras for their roles. Resize both state textures together and define whether resizing resets history. Dispose both targets at teardown.

Continue experimenting

Reference

Official documentation

Run the related examples