2013-11-09 4 views
4

Я пытаюсь создать приложение, которое будет имитировать длительную съемку. Идея заключается в том, что я захватываю текущий кадр с веб-камеры и складываю его на холст. Со временем фото «разоблачит», станет ярче и ярче. (см. http://www.chromeexperiments.com/detail/light-paint-live-mercury/?f=)trjs фрагментарный шейдер с использованием переработанных буферов кадров

У меня есть шейдер, который отлично работает. Это похоже на режим добавления «добавить» в Photoshop. Проблема в том, что я не могу заставить его переработать предыдущий кадр.

Я думал, что это будет что-то простое, как renderer.autoClear = false;, но этот вариант, кажется, ничего не делает в этом контексте.

Вот код, который использует THREE.EffectComposer для применения шейдера.

 onWebcamInit: function() {  
      var $stream = $("#user-stream"), 
       width = $stream.width(), 
       height = $stream.height(), 
       near = .1, 
       far = 10000; 

      this.renderer = new THREE.WebGLRenderer(); 
      this.renderer.setSize(width, height); 
      this.renderer.autoClear = false; 
      this.scene = new THREE.Scene(); 

      this.camera = new THREE.OrthographicCamera(width/-2, width/2, height/2, height/-2, near, far); 
      this.scene.add(this.camera); 

      this.$el.append(this.renderer.domElement); 

      this.frameTexture = new THREE.Texture(document.querySelector("#webcam")); 
      this.compositeTexture = new THREE.Texture(this.renderer.domElement); 

      this.composer = new THREE.EffectComposer(this.renderer); 

      // same effect with or without this line 
      // this.composer.addPass(new THREE.RenderPass(this.scene, this.camera)); 

      var addEffect = new THREE.ShaderPass(addShader); 
      addEffect.uniforms[ 'exposure' ].value = .5; 
      addEffect.uniforms[ 'frameTexture' ].value = this.frameTexture; 
      addEffect.renderToScreen = true; 
      this.composer.addPass(addEffect); 

      this.plane = new THREE.Mesh(new THREE.PlaneGeometry(width, height, 1, 1), new THREE.MeshBasicMaterial({map: this.compositeTexture})); 
      this.scene.add(this.plane); 

      this.frameTexture.needsUpdate = true; 
      this.compositeTexture.needsUpdate = true; 

      new FrameImpulse(this.renderFrame); 

     }, 
     renderFrame: function() { 
      this.frameTexture.needsUpdate = true; 
      this.compositeTexture.needsUpdate = true; 
      this.composer.render(); 
     } 

Вот шейдер. Ничего особенного.

 uniforms: { 
      "tDiffuse": { type: "t", value: null }, 
      "frameTexture": { type: "t", value: null }, 
      "exposure": { type: "f", value: 1.0 } 
     }, 

     vertexShader: [ 
      "varying vec2 vUv;", 
      "void main() {", 
      "vUv = uv;", 
      "gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);", 

      "}" 
     ].join("\n"), 

     fragmentShader: [ 

      "uniform sampler2D frameTexture;", 
      "uniform sampler2D tDiffuse;", 
      "uniform float exposure;", 
      "varying vec2 vUv;", 

      "void main() {", 
      "vec4 n = texture2D(frameTexture, vUv);", 
      "vec4 o = texture2D(tDiffuse, vUv);", 
      "vec3 sum = n.rgb + o.rgb;", 
      "gl_FragColor = vec4(mix(o.rgb, sum.rgb, exposure), 1.0);", 
      "}" 

     ].join("\n") 
+0

[Это обсуждение] (https://github.com/mrdoob/three.js/issues/587), похоже, указывает, что вы должны создать WebGLRenderer следующим образом: 'new WebGLRenderer ({preserveDrawingBuffer: true})', и установите для параметра 'renderer.autoClearColor' значение false. – voithos

+0

Это будет работать для создания эффектов на геометрию (например, размытие движения), но это не влияет на фактические текстуры геометрии. Это то, что я пытаюсь настроить. –

ответ

0

Для достижения подобного эффекта обратной связи, вы должны чередовать записи на отдельные экземпляры WebGLRenderTarget. В противном случае буфер кадра будет перезаписан. Не совсем уверен, почему это происходит ... но вот решение.

INIT:

this.rt1 = new THREE.WebGLRenderTarget(512, 512, { minFilter: THREE.LinearFilter, magFilter: THREE.NearestFilter, format: THREE.RGBFormat }); 
    this.rt2 = new THREE.WebGLRenderTarget(512, 512, { minFilter: THREE.LinearFilter, magFilter: THREE.NearestFilter, format: THREE.RGBFormat }); 

оказывают:

this.renderer.render(this.scene, this.camera); 
    this.renderer.render(this.scene, this.camera, this.rt1, false); 

    // swap buffers 
    var a = this.rt2; 
    this.rt2 = this.rt1; 
    this.rt1 = a; 
    this.shaders.add.uniforms.tDiffuse.value = this.rt2; 
0

Попробуйте с этим:

this.renderer = new THREE.WebGLRenderer({ preserveDrawingBuffer: true }); 
+0

Спасибо за ответ, doob. Боюсь, что это не сработало для меня. Метод рендеринга-текстуры, который я опубликовал как ответ, работал отлично ... но я все еще не уверен, почему он это делает. –

1

Это по существу эквивалентно постулировать лаборатории ответить, но я имел успех с более рациональное решение - я создаю EffectComposer только с ShaderPass, который я хочу переработать, а затем замените renderTargets для tha т композитора с каждым рендерингом.

Инициализация:

THREE.EffectComposer.prototype.swapTargets = function() { 
    var tmp = this.renderTarget2; 
    this.renderTarget2 = this.renderTarget1; 
    this.renderTarget1 = tmp; 
}; 

... 

composer = new THREE.EffectComposer(renderer, 
    new THREE.WebGLRenderTarget(512, 512, { minFilter: THREE.LinearFilter, magFilter: THREE.NearestFilter, format: THREE.RGBFormat }) 
); 

var addEffect = new THREE.ShaderPass(addShader, 'frameTexture'); 
addEffect.renderToScreen = true; 
this.composer.addPass(addEffect); 

визуализации:

composer.render(); 
composer.swapTargets(); 

Вторичный EffectComposer затем может принимать одно из двух renderTargets и толкать его к экрану или трансформировать его дальше.

Также обратите внимание, что я объявляю «frameTexture» как textID при инициализации ShaderPass. Это позволяет ShaderPass знать, чтобы обновить форму frameTexture с результатами предыдущего Pass.

Смежные вопросы