// Temporal Super Sample for static Scene import halton from './halton'; import Pass from 'claygl/src/compositor/Pass'; import FrameBuffer from 'claygl/src/FrameBuffer'; import Texture2D from 'claygl/src/Texture2D'; import Shader from 'claygl/src/Shader'; import Matrix4 from 'claygl/src/math/Matrix4'; function TemporalSuperSampling(frames) { var haltonSequence = []; for (var i = 0; i < 30; i++) { haltonSequence.push([halton(i, 2), halton(i, 3)]); } this._haltonSequence = haltonSequence; this._frame = 0; this._sourceTex = new Texture2D(); this._sourceFb = new FrameBuffer(); this._sourceFb.attach(this._sourceTex); // Frame texture before temporal supersampling this._prevFrameTex = new Texture2D(); this._outputTex = new Texture2D(); var blendPass = this._blendPass = new Pass({ fragment: Shader.source('clay.compositor.blend') }); blendPass.material.disableTexturesAll(); blendPass.material.enableTexture(['texture1', 'texture2']); this._blendFb = new FrameBuffer({ depthBuffer: false }); this._outputPass = new Pass({ fragment: Shader.source('clay.compositor.output'), // TODO, alpha is premultiplied? blendWithPrevious: true }); this._outputPass.material.define('fragment', 'OUTPUT_ALPHA'); this._outputPass.material.blend = function (_gl) { // FIXME. // Output is premultiplied alpha when BLEND is enabled ? // http://stackoverflow.com/questions/2171085/opengl-blending-with-previous-contents-of-framebuffer _gl.blendEquationSeparate(_gl.FUNC_ADD, _gl.FUNC_ADD); _gl.blendFuncSeparate(_gl.ONE, _gl.ONE_MINUS_SRC_ALPHA, _gl.ONE, _gl.ONE_MINUS_SRC_ALPHA); }; } TemporalSuperSampling.prototype = { constructor: TemporalSuperSampling, /** * Jitter camera projectionMatrix * @parma {clay.Renderer} renderer * @param {clay.Camera} camera */ jitterProjection: function (renderer, camera) { var viewport = renderer.viewport; var dpr = viewport.devicePixelRatio || renderer.getDevicePixelRatio(); var width = viewport.width * dpr; var height = viewport.height * dpr; var offset = this._haltonSequence[this._frame % this._haltonSequence.length]; var translationMat = new Matrix4(); translationMat.array[12] = (offset[0] * 2.0 - 1.0) / width; translationMat.array[13] = (offset[1] * 2.0 - 1.0) / height; Matrix4.mul(camera.projectionMatrix, translationMat, camera.projectionMatrix); Matrix4.invert(camera.invProjectionMatrix, camera.projectionMatrix); }, /** * Reset accumulating frame */ resetFrame: function () { this._frame = 0; }, /** * Return current frame */ getFrame: function () { return this._frame; }, /** * Get source framebuffer for usage */ getSourceFrameBuffer: function () { return this._sourceFb; }, getOutputTexture: function () { return this._outputTex; }, resize: function (width, height) { this._prevFrameTex.width = width; this._prevFrameTex.height = height; this._outputTex.width = width; this._outputTex.height = height; this._sourceTex.width = width; this._sourceTex.height = height; this._prevFrameTex.dirty(); this._outputTex.dirty(); this._sourceTex.dirty(); }, isFinished: function () { return this._frame >= this._haltonSequence.length; }, render: function (renderer, sourceTex, notOutput) { var blendPass = this._blendPass; if (this._frame === 0) { // Direct output blendPass.setUniform('weight1', 0); blendPass.setUniform('weight2', 1); } else { blendPass.setUniform('weight1', 0.9); blendPass.setUniform('weight2', 0.1); } blendPass.setUniform('texture1', this._prevFrameTex); blendPass.setUniform('texture2', sourceTex || this._sourceTex); this._blendFb.attach(this._outputTex); this._blendFb.bind(renderer); blendPass.render(renderer); this._blendFb.unbind(renderer); if (!notOutput) { this._outputPass.setUniform('texture', this._outputTex); this._outputPass.render(renderer); } // Swap texture var tmp = this._prevFrameTex; this._prevFrameTex = this._outputTex; this._outputTex = tmp; this._frame++; }, dispose: function (renderer) { this._sourceFb.dispose(renderer); this._blendFb.dispose(renderer); this._prevFrameTex.dispose(renderer); this._outputTex.dispose(renderer); this._sourceTex.dispose(renderer); this._outputPass.dispose(renderer); this._blendPass.dispose(renderer); } }; export default TemporalSuperSampling;