1

I would like to store frames from a webcam, and then display these (delayed) frames on a THREE.PlaneGeometry texture.

I already have a webcam frame encapsulated in a THREE.Texture subclass, and can apply this to a material. Now I would like to buffer these frames and delay them:

**** pseudocode within tick() function ****
for (let i = 0; i < bufferedFrames.length - 1; i++) {
  bufferedFrames[i] = bufferedFrames[i+1];
}
bufferedFrames[bufferedFrames.length - 1] = currentWebCamTexture;

// Now display delayed texture.
plane.material.map = bufferedFrames[0]; // Use a frame from the past.
plane.material.map.needsUpdate = true;

renderer.render(scene, camera)

Unfortunately it doesn't seem like it's possible to store old frames. Even THREE.Texture's clone() method states that a clone does not create a "deep copy" of the image. And hence, I am not really buffering up an array of unique frames.

Currently I can solve this if I employ WebGLRenderTargets, i.e. keep an array of WebGLRenderTargets and render a scene that only contains my centered webcam frame. This seems wasteful to re-render a scene just to recover a texture. Is this the right way of doing it? Is there a cleaner way?

CaliCoder
  • 23
  • 2

1 Answers1

2

RenderTargets are the only way to store procedurally-generated textures to use later. You could technically copy it to a 2D canvas, and store that elsewhere, but reading the color data from WebGL in this method is pretty slow and inefficient if you want to do it continuously at 60FPS.

You don't need to render an entire scene. Since you just want to copy the frame without any 3D transformations, you could just render a simple plane mesh directly with a very basic camera:

// Build a plane mesh
const planeGeom = new THREE.PlaneGeometry(2, 2);
const planeMat = new THREE.MeshBasicMaterial({map: yourTexture});
const simplePlane = new THREE.Mesh(planeGeom, planeMat);

// Create simplest camera
const simpleCam = new THREE.Camera();

// Save texture to your renderTarget
renderer.setRenderTarget(yourRenderTarget);
renderer.render(simplePlane, simpleCam);
M -
  • 26,908
  • 11
  • 49
  • 81
  • 1
    I'm not sure, but I always use plane of size 2x2, not 1x1, with `THREE.Camera`, as it renders quad in NDC [-1, 1] on both x and y, thus 2 x 2. Sizing 1x1 gives a centered rect of halves on width and height. – prisoner849 Apr 25 '22 at 20:13
  • 1
    @prisoner849 Good catch! Fixed. – M - Apr 25 '22 at 20:18