title: Three.js Updating DataTextures with a renderTarget's image data
body: <p>I am working on a motion detection program in <code>Three.js</code> which uses the difference between the current and previous frame. For now, before the subtraction, the current and the previous frame are both blurred using a <code>Three.EffectComposer</code> each. </p>
  
  <p><strong>The main problem is</strong>: Instead of having to blur the previous frame again, I want to use the previously blurred "current" frame as the texture in the subtraction process.</p>
  
  <p>The closest I have managed to do is by using the function below to update the image.data of the <code>Three.DataTexture</code>. It was used in the <code>render()</code>-function after the Blurring composer is rendered, but before the subtraction is rendered. 
  Both of them were rendered to the screen with <code>Three.CopyShader</code>.</p>
  
  <pre class="lang-js prettyprint-override"><code>function getData(image) {
      var canvas = document.createElement('canvas');
      canvas.width = image.width;
      canvas.height = image.height;
  
      var context = canvas.getContext('2d');
      context.drawImage(image, 0, 0);
      return  new Uint8Array(context.getImageData(0, 0, image.width, image.height).data);
  }
  </code></pre>
  
  <p>Where the “image” is the <code>renderer.domElement</code>. This method feels quite inefficient and I needed to render the Blur-pass to the screen, which caused the result to flicker.  </p>
  
  <p><em>Edit 1</em>  The current code is shown below, it blurs the current and previous images and then calculates the difference. The <code>animate()</code>-function is the point of interest.</p>
  
  <pre class="lang-js prettyprint-override"><code>&lt;!DOCTYPE html&gt;
  &lt;html lang="en"&gt;
  &lt;head&gt;
      &lt;title&gt;Three.js Webcam Test&lt;/title&gt;
      &lt;meta charset="utf-8"&gt;
     &lt;!-- &lt;meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"&gt;
  --&gt;
      &lt;script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"&gt;&lt;/script&gt;
      &lt;script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.10.3/jquery-ui.min.js"&gt;&lt;/script&gt;
      &lt;script src="lib/three.min.js"&gt;&lt;/script&gt;
  
      &lt;!-- Effect composer scripts --&gt;
      &lt;script src = "postprocessing/EffectComposer.js"&gt;&lt;/script&gt;
      &lt;script src = "postprocessing/MaskPass.js"&gt;&lt;/script&gt;
      &lt;script src = "postprocessing/RenderPass.js"&gt;&lt;/script&gt;
      &lt;script src = "postprocessing/TexturePass.js"&gt;&lt;/script&gt;
      &lt;script src = "postprocessing/ShaderPass.js"&gt;&lt;/script&gt;
      &lt;script src = "postprocessing/SavePass.js"&gt;&lt;/script&gt;
  
      &lt;script src = "shaders/CopyShader.js"&gt;&lt;/script&gt;
      &lt;script src = "shaders/ColorifyShader.js"&gt;&lt;/script&gt;
      &lt;script src = "shaders/DilationShader.js"&gt;&lt;/script&gt;
      &lt;script src = "shaders/ErosionShader.js"&gt;&lt;/script&gt;
      &lt;script src = "shaders/HorizontalBlurShader.js"&gt;&lt;/script&gt;
      &lt;script src = "shaders/VerticalBlurShader.js"&gt;&lt;/script&gt;
      &lt;script src = "shaders/BlendShader.js"&gt;&lt;/script&gt;
      &lt;script src = "shaders/passThroughShader.js"&gt;&lt;/script&gt;
      &lt;script src = "shaders/framediffShader.js"&gt;&lt;/script&gt;
      &lt;script src = "shaders/PawaskarPostShader.js"&gt;&lt;/script&gt;
      &lt;!-- ----------------------- --&gt;
  
      &lt;script src="lib/Projector.js"&gt;&lt;/script&gt;
      &lt;script src="lib/CanvasRenderer.js"&gt;&lt;/script&gt;
      &lt;script src="lib/webcam.js"&gt;&lt;/script&gt;
      &lt;script src="lib/perspective.js"&gt;&lt;/script&gt;
      &lt;script src="lib/stats.min.js"&gt;&lt;/script&gt;
      &lt;script src="lib/rStats.js"&gt;&lt;/script&gt;
      &lt;script src="lib/rStats.extras.js"&gt;&lt;/script&gt;
      &lt;script type="text/javascript" src="lib/dat.gui.min.js"&gt;&lt;/script&gt;
  
      &lt;link href="css/style.css" rel="stylesheet" type="text/css"&gt;
      &lt;link rel="stylesheet" href="http://bootswatch.com/lumen/bootstrap.min.css"&gt;
      &lt;link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap-theme.min.css"&gt;
      &lt;script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/js/bootstrap.min.js"&gt;&lt;/script&gt;
  
  &lt;/head&gt;
  &lt;body&gt;
  
      &lt;h1&gt;Motion Detection (in progress)&lt;/h1&gt;
          &lt;p&gt;Press P to print the current frame&lt;/p&gt;
  &lt;div id="WebGL-output"&gt;
  &lt;/div&gt;
  
  &lt;div id="camera" class="camera"&gt;
      &lt;div class="label"&gt;&lt;/div&gt;
      &lt;video id="theVideo" autoplay width="640" height="480" class="webcam"&gt;&lt;/video&gt;
      &lt;canvas id="theCanvas"  width="640" height="480" class="hidden"&gt;&lt;/canvas&gt;
  &lt;/div&gt;
  
  &lt;script&gt;
      var SCREEN_HEIGHT = 480;
      var SCREEN_WIDTH = 640;
  
      var values = {
          detectmotion: true,
          softness: 0.17,
          threshold: 0.11,
          color: "#ffae23",
          usecolor: false,
          postprocess: false,
          postprocessmethod: 0,
          preprocess: true,
          detectedges: false,
          framedifference: false,
          binarydifference: false,
          bufferlength: 1
      };
  
      var stats, container, video, renderer, currTexture, uniforms, camera, scene, prevTexture, prevTexture2, prevTexture3, prevTextureBuffer = [], bufferLenght,
          videoContext,
          prevTime;
      var rS, glS, tS;
      var postProcessFilters = [];
      var prepScene, prepRenderTarget, prepComposer, prepPrevComposer, hBlur, vBlur, temporalShader, prevTemporalShader, prevBlur;
      var modelScene, modelRenderTarget, modelComposer, passShader;
      var subtractScene, subtractRenderTarget,subtractComposer, subtractShader;
      //GUI variables
      var gui, cPostProcessMethod, doPostProcess = false;
      var frameNumber;
      /** TEST **/
      var BlurSave;
  
      function init(){
  
          frameNumber = 0;
          /* INIT */
  
          scene = new THREE.Scene();
  
          camera = new THREE.Camera();
          scene.add(camera);
  
          webcam.updateSources(function(s){
              webcam.start('theVideo',s[0]);
          });
  
          var size = SCREEN_WIDTH * SCREEN_HEIGHT;
  
          video = document.getElementById( 'theVideo' );
          videoContext = document.getElementById('theCanvas').getContext('2d');
  
          //The textures
          currTexture = new THREE.DataTexture([],SCREEN_WIDTH,SCREEN_HEIGHT);
          prevTexture = new THREE.DataTexture([],SCREEN_WIDTH,SCREEN_HEIGHT);
          prevBlur = new THREE.DataTexture([], SCREEN_WIDTH, SCREEN_HEIGHT);
          currTexture.minFilter = prevTexture.minFilter = prevBlur.minFilter=  THREE.LinearFilter;
  
          prevTime = -1;
  
          renderer = new THREE.WebGLRenderer();
          renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
          renderer.domElement.width = SCREEN_WIDTH;
          renderer.domElement.height = SCREEN_HEIGHT;
          renderer.autoClear = false;
          document.body.insertBefore(renderer.domElement, document.body.childNodes[0]);
  
          uniforms = {
              currentTexture:   { type: "t", value: currTexture },
              mirrorImage:       { type: "i", value: 0}
          }
  
          var geometry = new THREE.PlaneBufferGeometry(1, 1);
          var material = new THREE.ShaderMaterial( {
              uniforms: uniforms,
              vertexShader: THREE.passThroughShader.vertexShader,
              fragmentShader: THREE.passThroughShader.fragmentShader
  
          } );
  
          // A plane with the current video context as texture
          var mesh = new THREE.Mesh(geometry,material);
          mesh.material.depthTest = false;
          mesh.material.depthWrite = false;
          scene.add(mesh);
  
          // COPY SHADER, used to render the current context to the screen
          var effectCopy = new THREE.ShaderPass(THREE.CopyShader);
          effectCopy.renderToScreen = true;
  
          /** Preprocess stage **/
  
          prepScene = new THREE.Scene();
          prepScene.add( new THREE.AmbientLight( 0xffffff ) );
          prepScene.add(mesh) // add the current quad
  
          var renderTargetParameters = {minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBFormat, stencilBuffer: false};
  
          //blur shaders
          hBlur = new THREE.ShaderPass(THREE.HorizontalBlurShader);
          hBlur.uniforms["h"].value = 1 / SCREEN_WIDTH;
          hBlur.enabled = values['preprocess'];
  
          vBlur = new THREE.ShaderPass(THREE.VerticalBlurShader);
          vBlur.uniforms["v"].value = 1 / SCREEN_HEIGHT;
          vBlur.enabled = values['preprocess'];
  
          BlurSave = new THREE.SavePass(new THREE.WebGLRenderTarget(SCREEN_WIDTH, SCREEN_HEIGHT, renderTargetParameters));
  
          //preprocess scene render pass
          var renderModelPrep = new THREE.RenderPass(prepScene, camera);
          var prevPassShader1 = new THREE.ShaderPass(THREE.passThroughShader);
          prevPassShader1.uniforms["mirrorImage"].value = 1;
  
          //Preprocess of the current image
          //It is this prepComposer's rendertarget value I want to use in the next loop
          prepComposer = new THREE.EffectComposer(renderer, new THREE.WebGLRenderTarget(SCREEN_WIDTH, SCREEN_HEIGHT, renderTargetParameters));
          prepComposer.addPass(renderModelPrep);
          prepComposer.addPass(prevPassShader1);
          prepComposer.addPass(hBlur);
          prepComposer.addPass(vBlur);
          prepComposer.addPass(BlurSave);
  
          //
         // subtractComposer.addPass(effectCopy);
  
          //Preprocess of the previous image
          //Want to skip this stage
          var prevPassShader = new THREE.ShaderPass(THREE.passThroughShader, prevTexture);
          prevPassShader.uniforms["currentTexture"].value = prevTexture;
          prevPassShader.uniforms["mirrorImage"].value = 1;
          var prevBlurSave = new THREE.SavePass(new THREE.WebGLRenderTarget(SCREEN_WIDTH, SCREEN_HEIGHT, renderTargetParameters));
  
          prepPrevComposer = new THREE.EffectComposer(renderer, new THREE.WebGLRenderTarget(SCREEN_WIDTH, SCREEN_HEIGHT, renderTargetParameters));
          prepPrevComposer.addPass(renderModelPrep);
          prepPrevComposer.addPass(prevPassShader);
          prepPrevComposer.addPass(hBlur);
          prepPrevComposer.addPass(vBlur);
          prepPrevComposer.addPass(prevBlurSave);
  
          /**------------------**/
  
          /**---------------------------**/
  
          /** Background Subtraction stage **/
  
          subtractScene = new THREE.Scene();
          subtractScene.add( new THREE.AmbientLight( 0xffffff ) );
  
          var renderTargetParameters3 = {minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBAFormat, stencilBuffer: false};
  
          //Background Subtraction shaders
          subtractShader = new THREE.ShaderPass(THREE.framediffShader);
          subtractShader.uniforms['currentTexture'].value = BlurSave.renderTarget; // from the preprocess
          subtractShader.uniforms['previousTexture'].value = prevBlurSave.renderTarget; //modelled background
  
          //Background subtraction scene render pass
          var renderSubtract = new THREE.RenderPass(subtractScene, camera);
  
          //Background subtraction Composer
          subtractComposer = new THREE.EffectComposer(renderer, new THREE.WebGLRenderTarget(SCREEN_WIDTH, SCREEN_HEIGHT, renderTargetParameters3));
          subtractComposer.addPass(renderSubtract);
          subtractComposer.addPass(subtractShader);
  
          //subtractComposer.addPass(effectCopy2);
  
          /**------------------------------**/
  
          /** Postprocessing stage **/
  
          //Dilation
          var dilationFilter = new THREE.ShaderPass(THREE.DilationShader);
          dilationFilter.enabled = values['postprocess'];
          postProcessFilters.push(dilationFilter);
          //Erosion
          var erosionFilter = new THREE.ShaderPass(THREE.ErosionShader);
          erosionFilter.enabled = values['postprocess'];
          postProcessFilters.push(erosionFilter);
  
          //Pawaskar's postprocess filter
          var pawaskarFilter = new THREE.ShaderPass(THREE.PawaskarPostShader);
          pawaskarFilter.uniforms['threshold'].value = values['threshold'];
          pawaskarFilter.enabled = values['postprocess'];
          postProcessFilters.push(pawaskarFilter);
  
          subtractComposer.addPass(pawaskarFilter);
  
          //Opening
          subtractComposer.addPass(erosionFilter);
          subtractComposer.addPass(dilationFilter);
          //Closing
          subtractComposer.addPass(dilationFilter);
          subtractComposer.addPass(erosionFilter);
  
          //The final result rendered to the screen
          subtractComposer.addPass(effectCopy);
  
          /**----------------------**/
  
          animate();
  
      }
  
      function animate()
      {
  
          if(video.readyState === video.HAVE_ENOUGH_DATA ){
              var time = video.currentTime;
              if(time !== prevTime){
                  //Because a firefox bug when drawImage is used, need to catch NS_ERROR_NOT_AVAILABLE
                  try {
  
                      videoContext.drawImage(video, 0, 0,SCREEN_WIDTH,SCREEN_HEIGHT); //update the video
  
                      if(currTexture.image.data.length){
  
                          //var imgData = getData(renderer.domElement);
                          //var imgData = renderer.domElement.toDataURL();
  
                         // var gl = renderer.getContext();
                          //gl.readPixels( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, gl.RGBA, gl.UNSIGNED_BYTE, prevBlur.image.data );
  
                         //prevBlur.image.data = imgData;
                         // prevBlur.needsUpdate = true;
  
                          /** I want to update the prevBlur texture with the BlurSave.renderTarget! **/
  
                          prevTexture.image.data = currTexture.image.data;
                          prevTexture.needsUpdate = true; //updates the previous texture in the shader
                      }
  
                      currTexture.image.data = new Uint8Array(videoContext.getImageData(0,0,SCREEN_WIDTH, SCREEN_HEIGHT).data);
                      currTexture.needsUpdate = true; //updates the current texture in the shader
                      prevTime = time;
  
                  }catch (e) {
                      if (e.name == "NS_ERROR_NOT_AVAILABLE") {
                          console.error(e);
                      } else {
                          throw e;
                      }
                  }
              }
          }
  
  
          prepComposer.render(0.05);
          prepPrevComposer.render(0.05);
          subtractComposer.render(0.05);
  
          frameNumber++;
          requestAnimationFrame(animate);
      }
  
       function getData(image) {
          var canvas = document.createElement('canvas');
          canvas.width = image.width;
          canvas.height = image.height;
  
          var context = canvas.getContext('2d');
          context.drawImage(image, 0, 0);
  
          return new Uint8Array(context.getImageData(0, 0, image.width, image.height).data);
      }
  
  
      function copyCanvas(e) {
          var imgData, imgNode;
          if (e.which !== 80) {
              return;
          } else {
              imgData = renderer.domElement.toDataURL();
          }
          // create a new image and add to the document
          imgNode = document.createElement("img");
          imgNode.src = imgData;
          document.body.appendChild(imgNode);
      }
  
  
      window.onload = init;
  
      window.addEventListener("keyup", copyCanvas);
  
  &lt;/script&gt;
  
  &lt;/body&gt;
  &lt;/html&gt;
  </code></pre>
  
  <ul>
  <li><p>How can I update the <code>prevBlur.image.data</code> with the current image data of the <code>BlurSave.rendertarget</code>? </p></li>
  <li><p>Are there any other way to update a shader's <code>Sampler2D</code> uniform with the value from a <code>WebGLRenderTarget</code>’s image data from a previous time-step?</p></li>
  </ul>