import { HalfFloatType, ShaderMaterial, WebGLRenderTarget } from 'three'; import { FullScreenQuad, Pass } from './Pass.js'; class RenderTransitionPass extends Pass { constructor( sceneA, cameraA, sceneB, cameraB ) { super(); this.material = this.createMaterial(); this.fsQuad = new FullScreenQuad( this.material ); this.sceneA = sceneA; this.cameraA = cameraA; this.sceneB = sceneB; this.cameraB = cameraB; this.renderTargetA = new WebGLRenderTarget(); this.renderTargetA.texture.type = HalfFloatType; this.renderTargetB = new WebGLRenderTarget(); this.renderTargetB.texture.type = HalfFloatType; } setTransition( value ) { this.material.uniforms.mixRatio.value = value; } useTexture( value ) { this.material.uniforms.useTexture.value = value ? 1 : 0; } setTexture( value ) { this.material.uniforms.tMixTexture.value = value; } setTextureThreshold( value ) { this.material.uniforms.threshold.value = value; } setSize( width, height ) { this.renderTargetA.setSize( width, height ); this.renderTargetB.setSize( width, height ); } render( renderer, writeBuffer ) { const uniforms = this.fsQuad.material.uniforms; const transition = uniforms.mixRatio.value; // Prevent render both scenes when it's not necessary if ( transition === 0 ) { renderer.setRenderTarget( writeBuffer ); if ( this.clear ) renderer.clear(); renderer.render( this.sceneB, this.cameraB ); } else if ( transition === 1 ) { renderer.setRenderTarget( writeBuffer ); if ( this.clear ) renderer.clear(); renderer.render( this.sceneA, this.cameraA ); } else { // When 0 < transition < 1 render transition between two scenes renderer.setRenderTarget( this.renderTargetA ); renderer.render( this.sceneA, this.cameraA ); renderer.setRenderTarget( this.renderTargetB ); renderer.render( this.sceneB, this.cameraB ); uniforms.tDiffuse1.value = this.renderTargetA.texture; uniforms.tDiffuse2.value = this.renderTargetB.texture; if ( this.renderToScreen ) { renderer.setRenderTarget( null ); renderer.clear(); } else { renderer.setRenderTarget( writeBuffer ); if ( this.clear ) renderer.clear(); } this.fsQuad.render( renderer ); } } dispose() { this.renderTargetA.dispose(); this.renderTargetB.dispose(); this.material.dispose(); this.fsQuad.dispose(); } createMaterial() { return new ShaderMaterial( { uniforms: { tDiffuse1: { value: null }, tDiffuse2: { value: null }, mixRatio: { value: 0.0 }, threshold: { value: 0.1 }, useTexture: { value: 1 }, tMixTexture: { value: null } }, vertexShader: /* glsl */` varying vec2 vUv; void main() { vUv = vec2( uv.x, uv.y ); gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); } `, fragmentShader: /* glsl */` uniform float mixRatio; uniform sampler2D tDiffuse1; uniform sampler2D tDiffuse2; uniform sampler2D tMixTexture; uniform int useTexture; uniform float threshold; varying vec2 vUv; void main() { vec4 texel1 = texture2D( tDiffuse1, vUv ); vec4 texel2 = texture2D( tDiffuse2, vUv ); if (useTexture == 1) { vec4 transitionTexel = texture2D( tMixTexture, vUv ); float r = mixRatio * ( 1.0 + threshold * 2.0 ) - threshold; float mixf = clamp( ( transitionTexel.r - r ) * ( 1.0 / threshold ), 0.0, 1.0 ); gl_FragColor = mix( texel1, texel2, mixf ); } else { gl_FragColor = mix( texel2, texel1, mixRatio ); } #include #include } ` } ); } } export { RenderTransitionPass };