import Matrix4 from 'claygl/src/math/Matrix4'; import Vector3 from 'claygl/src/math/Vector3'; import Texture2D from 'claygl/src/Texture2D'; import Texture from 'claygl/src/Texture'; import Pass from 'claygl/src/compositor/Pass'; import Shader from 'claygl/src/Shader'; import FrameBuffer from 'claygl/src/FrameBuffer'; import halton from './halton'; import cubemapUtil from 'claygl/src/util/cubemap'; import SSRGLSLCode from './SSR.glsl.js'; Shader.import(SSRGLSLCode); function SSRPass(opt) { opt = opt || {}; this._ssrPass = new Pass({ fragment: Shader.source('ecgl.ssr.main'), clearColor: [0, 0, 0, 0] }); this._blurPass1 = new Pass({ fragment: Shader.source('ecgl.ssr.blur'), clearColor: [0, 0, 0, 0] }); this._blurPass2 = new Pass({ fragment: Shader.source('ecgl.ssr.blur'), clearColor: [0, 0, 0, 0] }); this._blendPass = new Pass({ fragment: Shader.source('clay.compositor.blend') }); this._blendPass.material.disableTexturesAll(); this._blendPass.material.enableTexture(['texture1', 'texture2']); this._ssrPass.setUniform('gBufferTexture1', opt.normalTexture); this._ssrPass.setUniform('gBufferTexture2', opt.depthTexture); // this._ssrPass.setUniform('gBufferTexture3', opt.albedoTexture); this._blurPass1.setUniform('gBufferTexture1', opt.normalTexture); this._blurPass1.setUniform('gBufferTexture2', opt.depthTexture); this._blurPass2.setUniform('gBufferTexture1', opt.normalTexture); this._blurPass2.setUniform('gBufferTexture2', opt.depthTexture); this._blurPass2.material.define('fragment', 'VERTICAL'); this._blurPass2.material.define('fragment', 'BLEND'); this._ssrTexture = new Texture2D({ type: Texture.HALF_FLOAT }); this._texture2 = new Texture2D({ type: Texture.HALF_FLOAT }); this._texture3 = new Texture2D({ type: Texture.HALF_FLOAT }); this._prevTexture = new Texture2D({ type: Texture.HALF_FLOAT }); this._currentTexture = new Texture2D({ type: Texture.HALF_FLOAT }); this._frameBuffer = new FrameBuffer({ depthBuffer: false }); this._normalDistribution = null; this._totalSamples = 256; this._samplePerFrame = 4; this._ssrPass.material.define('fragment', 'SAMPLE_PER_FRAME', this._samplePerFrame); this._ssrPass.material.define('fragment', 'TOTAL_SAMPLES', this._totalSamples); this._downScale = 1; } SSRPass.prototype.setAmbientCubemap = function (specularCubemap, specularIntensity) { this._ssrPass.material.set('specularCubemap', specularCubemap); this._ssrPass.material.set('specularIntensity', specularIntensity); var enableSpecularMap = specularCubemap && specularIntensity; this._ssrPass.material[enableSpecularMap ? 'enableTexture' : 'disableTexture']('specularCubemap'); }; SSRPass.prototype.update = function (renderer, camera, sourceTexture, frame) { var width = renderer.getWidth(); var height = renderer.getHeight(); var ssrTexture = this._ssrTexture; var texture2 = this._texture2; var texture3 = this._texture3; ssrTexture.width = this._prevTexture.width = this._currentTexture.width = width / this._downScale; ssrTexture.height = this._prevTexture.height = this._currentTexture.height = height / this._downScale; texture2.width = texture3.width = width; texture2.height = texture3.height = height; var frameBuffer = this._frameBuffer; var ssrPass = this._ssrPass; var blurPass1 = this._blurPass1; var blurPass2 = this._blurPass2; var blendPass = this._blendPass; var toViewSpace = new Matrix4(); var toWorldSpace = new Matrix4(); Matrix4.transpose(toViewSpace, camera.worldTransform); Matrix4.transpose(toWorldSpace, camera.viewMatrix); ssrPass.setUniform('sourceTexture', sourceTexture); ssrPass.setUniform('projection', camera.projectionMatrix.array); ssrPass.setUniform('projectionInv', camera.invProjectionMatrix.array); ssrPass.setUniform('toViewSpace', toViewSpace.array); ssrPass.setUniform('toWorldSpace', toWorldSpace.array); ssrPass.setUniform('nearZ', camera.near); var percent = frame / this._totalSamples * this._samplePerFrame; ssrPass.setUniform('jitterOffset', percent); ssrPass.setUniform('sampleOffset', frame * this._samplePerFrame); blurPass1.setUniform('textureSize', [ssrTexture.width, ssrTexture.height]); blurPass2.setUniform('textureSize', [width, height]); blurPass2.setUniform('sourceTexture', sourceTexture); blurPass1.setUniform('projection', camera.projectionMatrix.array); blurPass2.setUniform('projection', camera.projectionMatrix.array); frameBuffer.attach(ssrTexture); frameBuffer.bind(renderer); ssrPass.render(renderer); if (this._physicallyCorrect) { frameBuffer.attach(this._currentTexture); blendPass.setUniform('texture1', this._prevTexture); blendPass.setUniform('texture2', ssrTexture); blendPass.material.set({ 'weight1': frame >= 1 ? 0.95 : 0, 'weight2': frame >= 1 ? 0.05 : 1 // weight1: frame >= 1 ? 1 : 0, // weight2: 1 }); blendPass.render(renderer); } frameBuffer.attach(texture2); blurPass1.setUniform('texture', this._physicallyCorrect ? this._currentTexture : ssrTexture); blurPass1.render(renderer); frameBuffer.attach(texture3); blurPass2.setUniform('texture', texture2); blurPass2.render(renderer); frameBuffer.unbind(renderer); if (this._physicallyCorrect) { var tmp = this._prevTexture; this._prevTexture = this._currentTexture; this._currentTexture = tmp; } }; SSRPass.prototype.getTargetTexture = function () { return this._texture3; }; SSRPass.prototype.setParameter = function (name, val) { if (name === 'maxIteration') { this._ssrPass.material.define('fragment', 'MAX_ITERATION', val); } else { this._ssrPass.setUniform(name, val); } }; SSRPass.prototype.setPhysicallyCorrect = function (isPhysicallyCorrect) { if (isPhysicallyCorrect) { if (!this._normalDistribution) { this._normalDistribution = cubemapUtil.generateNormalDistribution(64, this._totalSamples); } this._ssrPass.material.define('fragment', 'PHYSICALLY_CORRECT'); this._ssrPass.material.set('normalDistribution', this._normalDistribution); this._ssrPass.material.set('normalDistributionSize', [64, this._totalSamples]); } else { this._ssrPass.material.undefine('fragment', 'PHYSICALLY_CORRECT'); } this._physicallyCorrect = isPhysicallyCorrect; }; SSRPass.prototype.setSSAOTexture = function (texture) { var blendPass = this._blurPass2; if (texture) { blendPass.material.enableTexture('ssaoTex'); blendPass.material.set('ssaoTex', texture); } else { blendPass.material.disableTexture('ssaoTex'); } }; SSRPass.prototype.isFinished = function (frame) { if (this._physicallyCorrect) { return frame > (this._totalSamples / this._samplePerFrame); } else { return true; } }; SSRPass.prototype.dispose = function (renderer) { this._ssrTexture.dispose(renderer); this._texture2.dispose(renderer); this._texture3.dispose(renderer); this._prevTexture.dispose(renderer); this._currentTexture.dispose(renderer); this._frameBuffer.dispose(renderer); }; export default SSRPass;