164 lines
4.5 KiB
JavaScript
164 lines
4.5 KiB
JavaScript
|
|
// 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;
|