添加关照、全局等高线、修改图层问题
This commit is contained in:
104
static/sdk/three/jsm/postprocessing/AfterimagePass.js
Normal file
104
static/sdk/three/jsm/postprocessing/AfterimagePass.js
Normal file
@ -0,0 +1,104 @@
|
||||
import {
|
||||
HalfFloatType,
|
||||
MeshBasicMaterial,
|
||||
NearestFilter,
|
||||
ShaderMaterial,
|
||||
UniformsUtils,
|
||||
WebGLRenderTarget
|
||||
} from 'three';
|
||||
import { Pass, FullScreenQuad } from './Pass.js';
|
||||
import { AfterimageShader } from '../shaders/AfterimageShader.js';
|
||||
|
||||
class AfterimagePass extends Pass {
|
||||
|
||||
constructor( damp = 0.96 ) {
|
||||
|
||||
super();
|
||||
|
||||
this.shader = AfterimageShader;
|
||||
|
||||
this.uniforms = UniformsUtils.clone( this.shader.uniforms );
|
||||
|
||||
this.uniforms[ 'damp' ].value = damp;
|
||||
|
||||
this.textureComp = new WebGLRenderTarget( window.innerWidth, window.innerHeight, {
|
||||
magFilter: NearestFilter,
|
||||
type: HalfFloatType
|
||||
} );
|
||||
|
||||
this.textureOld = new WebGLRenderTarget( window.innerWidth, window.innerHeight, {
|
||||
magFilter: NearestFilter,
|
||||
type: HalfFloatType
|
||||
} );
|
||||
|
||||
this.compFsMaterial = new ShaderMaterial( {
|
||||
|
||||
uniforms: this.uniforms,
|
||||
vertexShader: this.shader.vertexShader,
|
||||
fragmentShader: this.shader.fragmentShader
|
||||
|
||||
} );
|
||||
|
||||
this.compFsQuad = new FullScreenQuad( this.compFsMaterial );
|
||||
|
||||
this.copyFsMaterial = new MeshBasicMaterial();
|
||||
this.copyFsQuad = new FullScreenQuad( this.copyFsMaterial );
|
||||
|
||||
}
|
||||
|
||||
render( renderer, writeBuffer, readBuffer/*, deltaTime, maskActive*/ ) {
|
||||
|
||||
this.uniforms[ 'tOld' ].value = this.textureOld.texture;
|
||||
this.uniforms[ 'tNew' ].value = readBuffer.texture;
|
||||
|
||||
renderer.setRenderTarget( this.textureComp );
|
||||
this.compFsQuad.render( renderer );
|
||||
|
||||
this.copyFsQuad.material.map = this.textureComp.texture;
|
||||
|
||||
if ( this.renderToScreen ) {
|
||||
|
||||
renderer.setRenderTarget( null );
|
||||
this.copyFsQuad.render( renderer );
|
||||
|
||||
} else {
|
||||
|
||||
renderer.setRenderTarget( writeBuffer );
|
||||
|
||||
if ( this.clear ) renderer.clear();
|
||||
|
||||
this.copyFsQuad.render( renderer );
|
||||
|
||||
}
|
||||
|
||||
// Swap buffers.
|
||||
const temp = this.textureOld;
|
||||
this.textureOld = this.textureComp;
|
||||
this.textureComp = temp;
|
||||
// Now textureOld contains the latest image, ready for the next frame.
|
||||
|
||||
}
|
||||
|
||||
setSize( width, height ) {
|
||||
|
||||
this.textureComp.setSize( width, height );
|
||||
this.textureOld.setSize( width, height );
|
||||
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
||||
this.textureComp.dispose();
|
||||
this.textureOld.dispose();
|
||||
|
||||
this.compFsMaterial.dispose();
|
||||
this.copyFsMaterial.dispose();
|
||||
|
||||
this.compFsQuad.dispose();
|
||||
this.copyFsQuad.dispose();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { AfterimagePass };
|
172
static/sdk/three/jsm/postprocessing/BloomPass.js
Normal file
172
static/sdk/three/jsm/postprocessing/BloomPass.js
Normal file
@ -0,0 +1,172 @@
|
||||
import {
|
||||
AdditiveBlending,
|
||||
HalfFloatType,
|
||||
ShaderMaterial,
|
||||
UniformsUtils,
|
||||
Vector2,
|
||||
WebGLRenderTarget
|
||||
} from 'three';
|
||||
import { Pass, FullScreenQuad } from './Pass.js';
|
||||
import { ConvolutionShader } from '../shaders/ConvolutionShader.js';
|
||||
|
||||
class BloomPass extends Pass {
|
||||
|
||||
constructor( strength = 1, kernelSize = 25, sigma = 4 ) {
|
||||
|
||||
super();
|
||||
|
||||
// render targets
|
||||
|
||||
this.renderTargetX = new WebGLRenderTarget( 1, 1, { type: HalfFloatType } ); // will be resized later
|
||||
this.renderTargetX.texture.name = 'BloomPass.x';
|
||||
this.renderTargetY = new WebGLRenderTarget( 1, 1, { type: HalfFloatType } ); // will be resized later
|
||||
this.renderTargetY.texture.name = 'BloomPass.y';
|
||||
|
||||
// combine material
|
||||
|
||||
this.combineUniforms = UniformsUtils.clone( CombineShader.uniforms );
|
||||
|
||||
this.combineUniforms[ 'strength' ].value = strength;
|
||||
|
||||
this.materialCombine = new ShaderMaterial( {
|
||||
|
||||
name: CombineShader.name,
|
||||
uniforms: this.combineUniforms,
|
||||
vertexShader: CombineShader.vertexShader,
|
||||
fragmentShader: CombineShader.fragmentShader,
|
||||
blending: AdditiveBlending,
|
||||
transparent: true
|
||||
|
||||
} );
|
||||
|
||||
// convolution material
|
||||
|
||||
const convolutionShader = ConvolutionShader;
|
||||
|
||||
this.convolutionUniforms = UniformsUtils.clone( convolutionShader.uniforms );
|
||||
|
||||
this.convolutionUniforms[ 'uImageIncrement' ].value = BloomPass.blurX;
|
||||
this.convolutionUniforms[ 'cKernel' ].value = ConvolutionShader.buildKernel( sigma );
|
||||
|
||||
this.materialConvolution = new ShaderMaterial( {
|
||||
|
||||
name: convolutionShader.name,
|
||||
uniforms: this.convolutionUniforms,
|
||||
vertexShader: convolutionShader.vertexShader,
|
||||
fragmentShader: convolutionShader.fragmentShader,
|
||||
defines: {
|
||||
'KERNEL_SIZE_FLOAT': kernelSize.toFixed( 1 ),
|
||||
'KERNEL_SIZE_INT': kernelSize.toFixed( 0 )
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
this.needsSwap = false;
|
||||
|
||||
this.fsQuad = new FullScreenQuad( null );
|
||||
|
||||
}
|
||||
|
||||
render( renderer, writeBuffer, readBuffer, deltaTime, maskActive ) {
|
||||
|
||||
if ( maskActive ) renderer.state.buffers.stencil.setTest( false );
|
||||
|
||||
// Render quad with blured scene into texture (convolution pass 1)
|
||||
|
||||
this.fsQuad.material = this.materialConvolution;
|
||||
|
||||
this.convolutionUniforms[ 'tDiffuse' ].value = readBuffer.texture;
|
||||
this.convolutionUniforms[ 'uImageIncrement' ].value = BloomPass.blurX;
|
||||
|
||||
renderer.setRenderTarget( this.renderTargetX );
|
||||
renderer.clear();
|
||||
this.fsQuad.render( renderer );
|
||||
|
||||
|
||||
// Render quad with blured scene into texture (convolution pass 2)
|
||||
|
||||
this.convolutionUniforms[ 'tDiffuse' ].value = this.renderTargetX.texture;
|
||||
this.convolutionUniforms[ 'uImageIncrement' ].value = BloomPass.blurY;
|
||||
|
||||
renderer.setRenderTarget( this.renderTargetY );
|
||||
renderer.clear();
|
||||
this.fsQuad.render( renderer );
|
||||
|
||||
// Render original scene with superimposed blur to texture
|
||||
|
||||
this.fsQuad.material = this.materialCombine;
|
||||
|
||||
this.combineUniforms[ 'tDiffuse' ].value = this.renderTargetY.texture;
|
||||
|
||||
if ( maskActive ) renderer.state.buffers.stencil.setTest( true );
|
||||
|
||||
renderer.setRenderTarget( readBuffer );
|
||||
if ( this.clear ) renderer.clear();
|
||||
this.fsQuad.render( renderer );
|
||||
|
||||
}
|
||||
|
||||
setSize( width, height ) {
|
||||
|
||||
this.renderTargetX.setSize( width, height );
|
||||
this.renderTargetY.setSize( width, height );
|
||||
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
||||
this.renderTargetX.dispose();
|
||||
this.renderTargetY.dispose();
|
||||
|
||||
this.materialCombine.dispose();
|
||||
this.materialConvolution.dispose();
|
||||
|
||||
this.fsQuad.dispose();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const CombineShader = {
|
||||
|
||||
name: 'CombineShader',
|
||||
|
||||
uniforms: {
|
||||
|
||||
'tDiffuse': { value: null },
|
||||
'strength': { value: 1.0 }
|
||||
|
||||
},
|
||||
|
||||
vertexShader: /* glsl */`
|
||||
|
||||
varying vec2 vUv;
|
||||
|
||||
void main() {
|
||||
|
||||
vUv = uv;
|
||||
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
|
||||
|
||||
}`,
|
||||
|
||||
fragmentShader: /* glsl */`
|
||||
|
||||
uniform float strength;
|
||||
|
||||
uniform sampler2D tDiffuse;
|
||||
|
||||
varying vec2 vUv;
|
||||
|
||||
void main() {
|
||||
|
||||
vec4 texel = texture2D( tDiffuse, vUv );
|
||||
gl_FragColor = strength * texel;
|
||||
|
||||
}`
|
||||
|
||||
};
|
||||
|
||||
BloomPass.blurX = new Vector2( 0.001953125, 0.0 );
|
||||
BloomPass.blurY = new Vector2( 0.0, 0.001953125 );
|
||||
|
||||
export { BloomPass };
|
141
static/sdk/three/jsm/postprocessing/BokehPass.js
Normal file
141
static/sdk/three/jsm/postprocessing/BokehPass.js
Normal file
@ -0,0 +1,141 @@
|
||||
import {
|
||||
Color,
|
||||
HalfFloatType,
|
||||
MeshDepthMaterial,
|
||||
NearestFilter,
|
||||
NoBlending,
|
||||
RGBADepthPacking,
|
||||
ShaderMaterial,
|
||||
UniformsUtils,
|
||||
WebGLRenderTarget
|
||||
} from 'three';
|
||||
import { Pass, FullScreenQuad } from './Pass.js';
|
||||
import { BokehShader } from '../shaders/BokehShader.js';
|
||||
|
||||
/**
|
||||
* Depth-of-field post-process with bokeh shader
|
||||
*/
|
||||
|
||||
class BokehPass extends Pass {
|
||||
|
||||
constructor( scene, camera, params ) {
|
||||
|
||||
super();
|
||||
|
||||
this.scene = scene;
|
||||
this.camera = camera;
|
||||
|
||||
const focus = ( params.focus !== undefined ) ? params.focus : 1.0;
|
||||
const aperture = ( params.aperture !== undefined ) ? params.aperture : 0.025;
|
||||
const maxblur = ( params.maxblur !== undefined ) ? params.maxblur : 1.0;
|
||||
|
||||
// render targets
|
||||
|
||||
this.renderTargetDepth = new WebGLRenderTarget( 1, 1, { // will be resized later
|
||||
minFilter: NearestFilter,
|
||||
magFilter: NearestFilter,
|
||||
type: HalfFloatType
|
||||
} );
|
||||
|
||||
this.renderTargetDepth.texture.name = 'BokehPass.depth';
|
||||
|
||||
// depth material
|
||||
|
||||
this.materialDepth = new MeshDepthMaterial();
|
||||
this.materialDepth.depthPacking = RGBADepthPacking;
|
||||
this.materialDepth.blending = NoBlending;
|
||||
|
||||
// bokeh material
|
||||
|
||||
const bokehShader = BokehShader;
|
||||
const bokehUniforms = UniformsUtils.clone( bokehShader.uniforms );
|
||||
|
||||
bokehUniforms[ 'tDepth' ].value = this.renderTargetDepth.texture;
|
||||
|
||||
bokehUniforms[ 'focus' ].value = focus;
|
||||
bokehUniforms[ 'aspect' ].value = camera.aspect;
|
||||
bokehUniforms[ 'aperture' ].value = aperture;
|
||||
bokehUniforms[ 'maxblur' ].value = maxblur;
|
||||
bokehUniforms[ 'nearClip' ].value = camera.near;
|
||||
bokehUniforms[ 'farClip' ].value = camera.far;
|
||||
|
||||
this.materialBokeh = new ShaderMaterial( {
|
||||
defines: Object.assign( {}, bokehShader.defines ),
|
||||
uniforms: bokehUniforms,
|
||||
vertexShader: bokehShader.vertexShader,
|
||||
fragmentShader: bokehShader.fragmentShader
|
||||
} );
|
||||
|
||||
this.uniforms = bokehUniforms;
|
||||
|
||||
this.fsQuad = new FullScreenQuad( this.materialBokeh );
|
||||
|
||||
this._oldClearColor = new Color();
|
||||
|
||||
}
|
||||
|
||||
render( renderer, writeBuffer, readBuffer/*, deltaTime, maskActive*/ ) {
|
||||
|
||||
// Render depth into texture
|
||||
|
||||
this.scene.overrideMaterial = this.materialDepth;
|
||||
|
||||
renderer.getClearColor( this._oldClearColor );
|
||||
const oldClearAlpha = renderer.getClearAlpha();
|
||||
const oldAutoClear = renderer.autoClear;
|
||||
renderer.autoClear = false;
|
||||
|
||||
renderer.setClearColor( 0xffffff );
|
||||
renderer.setClearAlpha( 1.0 );
|
||||
renderer.setRenderTarget( this.renderTargetDepth );
|
||||
renderer.clear();
|
||||
renderer.render( this.scene, this.camera );
|
||||
|
||||
// Render bokeh composite
|
||||
|
||||
this.uniforms[ 'tColor' ].value = readBuffer.texture;
|
||||
this.uniforms[ 'nearClip' ].value = this.camera.near;
|
||||
this.uniforms[ 'farClip' ].value = this.camera.far;
|
||||
|
||||
if ( this.renderToScreen ) {
|
||||
|
||||
renderer.setRenderTarget( null );
|
||||
this.fsQuad.render( renderer );
|
||||
|
||||
} else {
|
||||
|
||||
renderer.setRenderTarget( writeBuffer );
|
||||
renderer.clear();
|
||||
this.fsQuad.render( renderer );
|
||||
|
||||
}
|
||||
|
||||
this.scene.overrideMaterial = null;
|
||||
renderer.setClearColor( this._oldClearColor );
|
||||
renderer.setClearAlpha( oldClearAlpha );
|
||||
renderer.autoClear = oldAutoClear;
|
||||
|
||||
}
|
||||
|
||||
setSize( width, height ) {
|
||||
|
||||
this.materialBokeh.uniforms[ 'aspect' ].value = width / height;
|
||||
|
||||
this.renderTargetDepth.setSize( width, height );
|
||||
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
||||
this.renderTargetDepth.dispose();
|
||||
|
||||
this.materialDepth.dispose();
|
||||
this.materialBokeh.dispose();
|
||||
|
||||
this.fsQuad.dispose();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { BokehPass };
|
46
static/sdk/three/jsm/postprocessing/ClearPass.js
Normal file
46
static/sdk/three/jsm/postprocessing/ClearPass.js
Normal file
@ -0,0 +1,46 @@
|
||||
import {
|
||||
Color
|
||||
} from 'three';
|
||||
import { Pass } from './Pass.js';
|
||||
|
||||
class ClearPass extends Pass {
|
||||
|
||||
constructor( clearColor, clearAlpha ) {
|
||||
|
||||
super();
|
||||
|
||||
this.needsSwap = false;
|
||||
|
||||
this.clearColor = ( clearColor !== undefined ) ? clearColor : 0x000000;
|
||||
this.clearAlpha = ( clearAlpha !== undefined ) ? clearAlpha : 0;
|
||||
this._oldClearColor = new Color();
|
||||
|
||||
}
|
||||
|
||||
render( renderer, writeBuffer, readBuffer /*, deltaTime, maskActive */ ) {
|
||||
|
||||
let oldClearAlpha;
|
||||
|
||||
if ( this.clearColor ) {
|
||||
|
||||
renderer.getClearColor( this._oldClearColor );
|
||||
oldClearAlpha = renderer.getClearAlpha();
|
||||
|
||||
renderer.setClearColor( this.clearColor, this.clearAlpha );
|
||||
|
||||
}
|
||||
|
||||
renderer.setRenderTarget( this.renderToScreen ? null : readBuffer );
|
||||
renderer.clear();
|
||||
|
||||
if ( this.clearColor ) {
|
||||
|
||||
renderer.setClearColor( this._oldClearColor, oldClearAlpha );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { ClearPass };
|
85
static/sdk/three/jsm/postprocessing/CubeTexturePass.js
Normal file
85
static/sdk/three/jsm/postprocessing/CubeTexturePass.js
Normal file
@ -0,0 +1,85 @@
|
||||
import {
|
||||
BackSide,
|
||||
BoxGeometry,
|
||||
Mesh,
|
||||
PerspectiveCamera,
|
||||
Scene,
|
||||
ShaderLib,
|
||||
ShaderMaterial,
|
||||
UniformsUtils
|
||||
} from 'three';
|
||||
import { Pass } from './Pass.js';
|
||||
|
||||
class CubeTexturePass extends Pass {
|
||||
|
||||
constructor( camera, tCube, opacity = 1 ) {
|
||||
|
||||
super();
|
||||
|
||||
this.camera = camera;
|
||||
|
||||
this.needsSwap = false;
|
||||
|
||||
this.cubeShader = ShaderLib[ 'cube' ];
|
||||
this.cubeMesh = new Mesh(
|
||||
new BoxGeometry( 10, 10, 10 ),
|
||||
new ShaderMaterial( {
|
||||
uniforms: UniformsUtils.clone( this.cubeShader.uniforms ),
|
||||
vertexShader: this.cubeShader.vertexShader,
|
||||
fragmentShader: this.cubeShader.fragmentShader,
|
||||
depthTest: false,
|
||||
depthWrite: false,
|
||||
side: BackSide
|
||||
} )
|
||||
);
|
||||
|
||||
Object.defineProperty( this.cubeMesh.material, 'envMap', {
|
||||
|
||||
get: function () {
|
||||
|
||||
return this.uniforms.tCube.value;
|
||||
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
this.tCube = tCube;
|
||||
this.opacity = opacity;
|
||||
|
||||
this.cubeScene = new Scene();
|
||||
this.cubeCamera = new PerspectiveCamera();
|
||||
this.cubeScene.add( this.cubeMesh );
|
||||
|
||||
}
|
||||
|
||||
render( renderer, writeBuffer, readBuffer/*, deltaTime, maskActive*/ ) {
|
||||
|
||||
const oldAutoClear = renderer.autoClear;
|
||||
renderer.autoClear = false;
|
||||
|
||||
this.cubeCamera.projectionMatrix.copy( this.camera.projectionMatrix );
|
||||
this.cubeCamera.quaternion.setFromRotationMatrix( this.camera.matrixWorld );
|
||||
|
||||
this.cubeMesh.material.uniforms.tCube.value = this.tCube;
|
||||
this.cubeMesh.material.uniforms.tFlip.value = ( this.tCube.isCubeTexture && this.tCube.isRenderTargetTexture === false ) ? - 1 : 1;
|
||||
this.cubeMesh.material.uniforms.opacity.value = this.opacity;
|
||||
this.cubeMesh.material.transparent = ( this.opacity < 1.0 );
|
||||
|
||||
renderer.setRenderTarget( this.renderToScreen ? null : readBuffer );
|
||||
if ( this.clear ) renderer.clear();
|
||||
renderer.render( this.cubeScene, this.cubeCamera );
|
||||
|
||||
renderer.autoClear = oldAutoClear;
|
||||
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
||||
this.cubeMesh.geometry.dispose();
|
||||
this.cubeMesh.material.dispose();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { CubeTexturePass };
|
65
static/sdk/three/jsm/postprocessing/DotScreenPass.js
Normal file
65
static/sdk/three/jsm/postprocessing/DotScreenPass.js
Normal file
@ -0,0 +1,65 @@
|
||||
import {
|
||||
ShaderMaterial,
|
||||
UniformsUtils
|
||||
} from 'three';
|
||||
import { Pass, FullScreenQuad } from './Pass.js';
|
||||
import { DotScreenShader } from '../shaders/DotScreenShader.js';
|
||||
|
||||
class DotScreenPass extends Pass {
|
||||
|
||||
constructor( center, angle, scale ) {
|
||||
|
||||
super();
|
||||
|
||||
const shader = DotScreenShader;
|
||||
|
||||
this.uniforms = UniformsUtils.clone( shader.uniforms );
|
||||
|
||||
if ( center !== undefined ) this.uniforms[ 'center' ].value.copy( center );
|
||||
if ( angle !== undefined ) this.uniforms[ 'angle' ].value = angle;
|
||||
if ( scale !== undefined ) this.uniforms[ 'scale' ].value = scale;
|
||||
|
||||
this.material = new ShaderMaterial( {
|
||||
|
||||
name: shader.name,
|
||||
uniforms: this.uniforms,
|
||||
vertexShader: shader.vertexShader,
|
||||
fragmentShader: shader.fragmentShader
|
||||
|
||||
} );
|
||||
|
||||
this.fsQuad = new FullScreenQuad( this.material );
|
||||
|
||||
}
|
||||
|
||||
render( renderer, writeBuffer, readBuffer /*, deltaTime, maskActive */ ) {
|
||||
|
||||
this.uniforms[ 'tDiffuse' ].value = readBuffer.texture;
|
||||
this.uniforms[ 'tSize' ].value.set( readBuffer.width, readBuffer.height );
|
||||
|
||||
if ( this.renderToScreen ) {
|
||||
|
||||
renderer.setRenderTarget( null );
|
||||
this.fsQuad.render( renderer );
|
||||
|
||||
} else {
|
||||
|
||||
renderer.setRenderTarget( writeBuffer );
|
||||
if ( this.clear ) renderer.clear();
|
||||
this.fsQuad.render( renderer );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
||||
this.material.dispose();
|
||||
|
||||
this.fsQuad.dispose();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { DotScreenPass };
|
231
static/sdk/three/jsm/postprocessing/EffectComposer.js
Normal file
231
static/sdk/three/jsm/postprocessing/EffectComposer.js
Normal file
@ -0,0 +1,231 @@
|
||||
import {
|
||||
Clock,
|
||||
HalfFloatType,
|
||||
NoBlending,
|
||||
Vector2,
|
||||
WebGLRenderTarget
|
||||
} from 'three';
|
||||
import { CopyShader } from '../shaders/CopyShader.js';
|
||||
import { ShaderPass } from './ShaderPass.js';
|
||||
import { MaskPass } from './MaskPass.js';
|
||||
import { ClearMaskPass } from './MaskPass.js';
|
||||
|
||||
class EffectComposer {
|
||||
|
||||
constructor( renderer, renderTarget ) {
|
||||
|
||||
this.renderer = renderer;
|
||||
|
||||
this._pixelRatio = renderer.getPixelRatio();
|
||||
|
||||
if ( renderTarget === undefined ) {
|
||||
|
||||
const size = renderer.getSize( new Vector2() );
|
||||
this._width = size.width;
|
||||
this._height = size.height;
|
||||
|
||||
renderTarget = new WebGLRenderTarget( this._width * this._pixelRatio, this._height * this._pixelRatio, { type: HalfFloatType } );
|
||||
renderTarget.texture.name = 'EffectComposer.rt1';
|
||||
|
||||
} else {
|
||||
|
||||
this._width = renderTarget.width;
|
||||
this._height = renderTarget.height;
|
||||
|
||||
}
|
||||
|
||||
this.renderTarget1 = renderTarget;
|
||||
this.renderTarget2 = renderTarget.clone();
|
||||
this.renderTarget2.texture.name = 'EffectComposer.rt2';
|
||||
|
||||
this.writeBuffer = this.renderTarget1;
|
||||
this.readBuffer = this.renderTarget2;
|
||||
|
||||
this.renderToScreen = true;
|
||||
|
||||
this.passes = [];
|
||||
|
||||
this.copyPass = new ShaderPass( CopyShader );
|
||||
this.copyPass.material.blending = NoBlending;
|
||||
|
||||
this.clock = new Clock();
|
||||
|
||||
}
|
||||
|
||||
swapBuffers() {
|
||||
|
||||
const tmp = this.readBuffer;
|
||||
this.readBuffer = this.writeBuffer;
|
||||
this.writeBuffer = tmp;
|
||||
|
||||
}
|
||||
|
||||
addPass( pass ) {
|
||||
|
||||
this.passes.push( pass );
|
||||
pass.setSize( this._width * this._pixelRatio, this._height * this._pixelRatio );
|
||||
|
||||
}
|
||||
|
||||
insertPass( pass, index ) {
|
||||
|
||||
this.passes.splice( index, 0, pass );
|
||||
pass.setSize( this._width * this._pixelRatio, this._height * this._pixelRatio );
|
||||
|
||||
}
|
||||
|
||||
removePass( pass ) {
|
||||
|
||||
const index = this.passes.indexOf( pass );
|
||||
|
||||
if ( index !== - 1 ) {
|
||||
|
||||
this.passes.splice( index, 1 );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
isLastEnabledPass( passIndex ) {
|
||||
|
||||
for ( let i = passIndex + 1; i < this.passes.length; i ++ ) {
|
||||
|
||||
if ( this.passes[ i ].enabled ) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
render( deltaTime ) {
|
||||
|
||||
// deltaTime value is in seconds
|
||||
|
||||
if ( deltaTime === undefined ) {
|
||||
|
||||
deltaTime = this.clock.getDelta();
|
||||
|
||||
}
|
||||
|
||||
const currentRenderTarget = this.renderer.getRenderTarget();
|
||||
|
||||
let maskActive = false;
|
||||
|
||||
for ( let i = 0, il = this.passes.length; i < il; i ++ ) {
|
||||
|
||||
const pass = this.passes[ i ];
|
||||
|
||||
if ( pass.enabled === false ) continue;
|
||||
|
||||
pass.renderToScreen = ( this.renderToScreen && this.isLastEnabledPass( i ) );
|
||||
pass.render( this.renderer, this.writeBuffer, this.readBuffer, deltaTime, maskActive );
|
||||
|
||||
if ( pass.needsSwap ) {
|
||||
|
||||
if ( maskActive ) {
|
||||
|
||||
const context = this.renderer.getContext();
|
||||
const stencil = this.renderer.state.buffers.stencil;
|
||||
|
||||
//context.stencilFunc( context.NOTEQUAL, 1, 0xffffffff );
|
||||
stencil.setFunc( context.NOTEQUAL, 1, 0xffffffff );
|
||||
|
||||
this.copyPass.render( this.renderer, this.writeBuffer, this.readBuffer, deltaTime );
|
||||
|
||||
//context.stencilFunc( context.EQUAL, 1, 0xffffffff );
|
||||
stencil.setFunc( context.EQUAL, 1, 0xffffffff );
|
||||
|
||||
}
|
||||
|
||||
this.swapBuffers();
|
||||
|
||||
}
|
||||
|
||||
if ( MaskPass !== undefined ) {
|
||||
|
||||
if ( pass instanceof MaskPass ) {
|
||||
|
||||
maskActive = true;
|
||||
|
||||
} else if ( pass instanceof ClearMaskPass ) {
|
||||
|
||||
maskActive = false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.renderer.setRenderTarget( currentRenderTarget );
|
||||
|
||||
}
|
||||
|
||||
reset( renderTarget ) {
|
||||
|
||||
if ( renderTarget === undefined ) {
|
||||
|
||||
const size = this.renderer.getSize( new Vector2() );
|
||||
this._pixelRatio = this.renderer.getPixelRatio();
|
||||
this._width = size.width;
|
||||
this._height = size.height;
|
||||
|
||||
renderTarget = this.renderTarget1.clone();
|
||||
renderTarget.setSize( this._width * this._pixelRatio, this._height * this._pixelRatio );
|
||||
|
||||
}
|
||||
|
||||
this.renderTarget1.dispose();
|
||||
this.renderTarget2.dispose();
|
||||
this.renderTarget1 = renderTarget;
|
||||
this.renderTarget2 = renderTarget.clone();
|
||||
|
||||
this.writeBuffer = this.renderTarget1;
|
||||
this.readBuffer = this.renderTarget2;
|
||||
|
||||
}
|
||||
|
||||
setSize( width, height ) {
|
||||
|
||||
this._width = width;
|
||||
this._height = height;
|
||||
|
||||
const effectiveWidth = this._width * this._pixelRatio;
|
||||
const effectiveHeight = this._height * this._pixelRatio;
|
||||
|
||||
this.renderTarget1.setSize( effectiveWidth, effectiveHeight );
|
||||
this.renderTarget2.setSize( effectiveWidth, effectiveHeight );
|
||||
|
||||
for ( let i = 0; i < this.passes.length; i ++ ) {
|
||||
|
||||
this.passes[ i ].setSize( effectiveWidth, effectiveHeight );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
setPixelRatio( pixelRatio ) {
|
||||
|
||||
this._pixelRatio = pixelRatio;
|
||||
|
||||
this.setSize( this._width, this._height );
|
||||
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
||||
this.renderTarget1.dispose();
|
||||
this.renderTarget2.dispose();
|
||||
|
||||
this.copyPass.dispose();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { EffectComposer };
|
64
static/sdk/three/jsm/postprocessing/FilmPass.js
Normal file
64
static/sdk/three/jsm/postprocessing/FilmPass.js
Normal file
@ -0,0 +1,64 @@
|
||||
import {
|
||||
ShaderMaterial,
|
||||
UniformsUtils
|
||||
} from 'three';
|
||||
import { Pass, FullScreenQuad } from './Pass.js';
|
||||
import { FilmShader } from '../shaders/FilmShader.js';
|
||||
|
||||
class FilmPass extends Pass {
|
||||
|
||||
constructor( intensity = 0.5, grayscale = false ) {
|
||||
|
||||
super();
|
||||
|
||||
const shader = FilmShader;
|
||||
|
||||
this.uniforms = UniformsUtils.clone( shader.uniforms );
|
||||
|
||||
this.material = new ShaderMaterial( {
|
||||
|
||||
name: shader.name,
|
||||
uniforms: this.uniforms,
|
||||
vertexShader: shader.vertexShader,
|
||||
fragmentShader: shader.fragmentShader
|
||||
|
||||
} );
|
||||
|
||||
this.uniforms.intensity.value = intensity; // (0 = no effect, 1 = full effect)
|
||||
this.uniforms.grayscale.value = grayscale;
|
||||
|
||||
this.fsQuad = new FullScreenQuad( this.material );
|
||||
|
||||
}
|
||||
|
||||
render( renderer, writeBuffer, readBuffer, deltaTime /*, maskActive */ ) {
|
||||
|
||||
this.uniforms[ 'tDiffuse' ].value = readBuffer.texture;
|
||||
this.uniforms[ 'time' ].value += deltaTime;
|
||||
|
||||
if ( this.renderToScreen ) {
|
||||
|
||||
renderer.setRenderTarget( null );
|
||||
this.fsQuad.render( renderer );
|
||||
|
||||
} else {
|
||||
|
||||
renderer.setRenderTarget( writeBuffer );
|
||||
if ( this.clear ) renderer.clear();
|
||||
this.fsQuad.render( renderer );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
||||
this.material.dispose();
|
||||
|
||||
this.fsQuad.dispose();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { FilmPass };
|
582
static/sdk/three/jsm/postprocessing/GTAOPass.js
Normal file
582
static/sdk/three/jsm/postprocessing/GTAOPass.js
Normal file
@ -0,0 +1,582 @@
|
||||
import {
|
||||
AddEquation,
|
||||
Color,
|
||||
CustomBlending,
|
||||
DataTexture,
|
||||
DepthTexture,
|
||||
DepthStencilFormat,
|
||||
DstAlphaFactor,
|
||||
DstColorFactor,
|
||||
HalfFloatType,
|
||||
MeshNormalMaterial,
|
||||
NearestFilter,
|
||||
NoBlending,
|
||||
RepeatWrapping,
|
||||
RGBAFormat,
|
||||
ShaderMaterial,
|
||||
UniformsUtils,
|
||||
UnsignedByteType,
|
||||
UnsignedInt248Type,
|
||||
WebGLRenderTarget,
|
||||
ZeroFactor
|
||||
} from 'three';
|
||||
import { Pass, FullScreenQuad } from './Pass.js';
|
||||
import { generateMagicSquareNoise, GTAOShader, GTAODepthShader, GTAOBlendShader } from '../shaders/GTAOShader.js';
|
||||
import { generatePdSamplePointInitializer, PoissonDenoiseShader } from '../shaders/PoissonDenoiseShader.js';
|
||||
import { CopyShader } from '../shaders/CopyShader.js';
|
||||
import { SimplexNoise } from '../math/SimplexNoise.js';
|
||||
|
||||
class GTAOPass extends Pass {
|
||||
|
||||
constructor( scene, camera, width, height, parameters, aoParameters, pdParameters ) {
|
||||
|
||||
super();
|
||||
|
||||
this.width = ( width !== undefined ) ? width : 512;
|
||||
this.height = ( height !== undefined ) ? height : 512;
|
||||
this.clear = true;
|
||||
this.camera = camera;
|
||||
this.scene = scene;
|
||||
this.output = 0;
|
||||
this._renderGBuffer = true;
|
||||
this._visibilityCache = new Map();
|
||||
this.blendIntensity = 1.;
|
||||
|
||||
this.pdRings = 2.;
|
||||
this.pdRadiusExponent = 2.;
|
||||
this.pdSamples = 16;
|
||||
|
||||
this.gtaoNoiseTexture = generateMagicSquareNoise();
|
||||
this.pdNoiseTexture = this.generateNoise();
|
||||
|
||||
this.gtaoRenderTarget = new WebGLRenderTarget( this.width, this.height, { type: HalfFloatType } );
|
||||
this.pdRenderTarget = this.gtaoRenderTarget.clone();
|
||||
|
||||
this.gtaoMaterial = new ShaderMaterial( {
|
||||
defines: Object.assign( {}, GTAOShader.defines ),
|
||||
uniforms: UniformsUtils.clone( GTAOShader.uniforms ),
|
||||
vertexShader: GTAOShader.vertexShader,
|
||||
fragmentShader: GTAOShader.fragmentShader,
|
||||
blending: NoBlending,
|
||||
depthTest: false,
|
||||
depthWrite: false,
|
||||
} );
|
||||
this.gtaoMaterial.defines.PERSPECTIVE_CAMERA = this.camera.isPerspectiveCamera ? 1 : 0;
|
||||
this.gtaoMaterial.uniforms.tNoise.value = this.gtaoNoiseTexture;
|
||||
this.gtaoMaterial.uniforms.resolution.value.set( this.width, this.height );
|
||||
this.gtaoMaterial.uniforms.cameraNear.value = this.camera.near;
|
||||
this.gtaoMaterial.uniforms.cameraFar.value = this.camera.far;
|
||||
|
||||
this.normalMaterial = new MeshNormalMaterial();
|
||||
this.normalMaterial.blending = NoBlending;
|
||||
|
||||
this.pdMaterial = new ShaderMaterial( {
|
||||
defines: Object.assign( {}, PoissonDenoiseShader.defines ),
|
||||
uniforms: UniformsUtils.clone( PoissonDenoiseShader.uniforms ),
|
||||
vertexShader: PoissonDenoiseShader.vertexShader,
|
||||
fragmentShader: PoissonDenoiseShader.fragmentShader,
|
||||
depthTest: false,
|
||||
depthWrite: false,
|
||||
} );
|
||||
this.pdMaterial.uniforms.tDiffuse.value = this.gtaoRenderTarget.texture;
|
||||
this.pdMaterial.uniforms.tNoise.value = this.pdNoiseTexture;
|
||||
this.pdMaterial.uniforms.resolution.value.set( this.width, this.height );
|
||||
this.pdMaterial.uniforms.lumaPhi.value = 10;
|
||||
this.pdMaterial.uniforms.depthPhi.value = 2;
|
||||
this.pdMaterial.uniforms.normalPhi.value = 3;
|
||||
this.pdMaterial.uniforms.radius.value = 8;
|
||||
|
||||
this.depthRenderMaterial = new ShaderMaterial( {
|
||||
defines: Object.assign( {}, GTAODepthShader.defines ),
|
||||
uniforms: UniformsUtils.clone( GTAODepthShader.uniforms ),
|
||||
vertexShader: GTAODepthShader.vertexShader,
|
||||
fragmentShader: GTAODepthShader.fragmentShader,
|
||||
blending: NoBlending
|
||||
} );
|
||||
this.depthRenderMaterial.uniforms.cameraNear.value = this.camera.near;
|
||||
this.depthRenderMaterial.uniforms.cameraFar.value = this.camera.far;
|
||||
|
||||
this.copyMaterial = new ShaderMaterial( {
|
||||
uniforms: UniformsUtils.clone( CopyShader.uniforms ),
|
||||
vertexShader: CopyShader.vertexShader,
|
||||
fragmentShader: CopyShader.fragmentShader,
|
||||
transparent: true,
|
||||
depthTest: false,
|
||||
depthWrite: false,
|
||||
blendSrc: DstColorFactor,
|
||||
blendDst: ZeroFactor,
|
||||
blendEquation: AddEquation,
|
||||
blendSrcAlpha: DstAlphaFactor,
|
||||
blendDstAlpha: ZeroFactor,
|
||||
blendEquationAlpha: AddEquation
|
||||
} );
|
||||
|
||||
this.blendMaterial = new ShaderMaterial( {
|
||||
uniforms: UniformsUtils.clone( GTAOBlendShader.uniforms ),
|
||||
vertexShader: GTAOBlendShader.vertexShader,
|
||||
fragmentShader: GTAOBlendShader.fragmentShader,
|
||||
transparent: true,
|
||||
depthTest: false,
|
||||
depthWrite: false,
|
||||
blending: CustomBlending,
|
||||
blendSrc: DstColorFactor,
|
||||
blendDst: ZeroFactor,
|
||||
blendEquation: AddEquation,
|
||||
blendSrcAlpha: DstAlphaFactor,
|
||||
blendDstAlpha: ZeroFactor,
|
||||
blendEquationAlpha: AddEquation
|
||||
} );
|
||||
|
||||
this.fsQuad = new FullScreenQuad( null );
|
||||
|
||||
this.originalClearColor = new Color();
|
||||
|
||||
this.setGBuffer( parameters ? parameters.depthTexture : undefined, parameters ? parameters.normalTexture : undefined );
|
||||
|
||||
if ( aoParameters !== undefined ) {
|
||||
|
||||
this.updateGtaoMaterial( aoParameters );
|
||||
|
||||
}
|
||||
|
||||
if ( pdParameters !== undefined ) {
|
||||
|
||||
this.updatePdMaterial( pdParameters );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
||||
this.gtaoNoiseTexture.dispose();
|
||||
this.pdNoiseTexture.dispose();
|
||||
this.normalRenderTarget.dispose();
|
||||
this.gtaoRenderTarget.dispose();
|
||||
this.pdRenderTarget.dispose();
|
||||
this.normalMaterial.dispose();
|
||||
this.pdMaterial.dispose();
|
||||
this.copyMaterial.dispose();
|
||||
this.depthRenderMaterial.dispose();
|
||||
this.fsQuad.dispose();
|
||||
|
||||
}
|
||||
|
||||
get gtaoMap() {
|
||||
|
||||
return this.pdRenderTarget.texture;
|
||||
|
||||
}
|
||||
|
||||
setGBuffer( depthTexture, normalTexture ) {
|
||||
|
||||
if ( depthTexture !== undefined ) {
|
||||
|
||||
this.depthTexture = depthTexture;
|
||||
this.normalTexture = normalTexture;
|
||||
this._renderGBuffer = false;
|
||||
|
||||
} else {
|
||||
|
||||
this.depthTexture = new DepthTexture();
|
||||
this.depthTexture.format = DepthStencilFormat;
|
||||
this.depthTexture.type = UnsignedInt248Type;
|
||||
this.normalRenderTarget = new WebGLRenderTarget( this.width, this.height, {
|
||||
minFilter: NearestFilter,
|
||||
magFilter: NearestFilter,
|
||||
type: HalfFloatType,
|
||||
depthTexture: this.depthTexture
|
||||
} );
|
||||
this.normalTexture = this.normalRenderTarget.texture;
|
||||
this._renderGBuffer = true;
|
||||
|
||||
}
|
||||
|
||||
const normalVectorType = ( this.normalTexture ) ? 1 : 0;
|
||||
const depthValueSource = ( this.depthTexture === this.normalTexture ) ? 'w' : 'x';
|
||||
|
||||
this.gtaoMaterial.defines.NORMAL_VECTOR_TYPE = normalVectorType;
|
||||
this.gtaoMaterial.defines.DEPTH_SWIZZLING = depthValueSource;
|
||||
this.gtaoMaterial.uniforms.tNormal.value = this.normalTexture;
|
||||
this.gtaoMaterial.uniforms.tDepth.value = this.depthTexture;
|
||||
|
||||
this.pdMaterial.defines.NORMAL_VECTOR_TYPE = normalVectorType;
|
||||
this.pdMaterial.defines.DEPTH_SWIZZLING = depthValueSource;
|
||||
this.pdMaterial.uniforms.tNormal.value = this.normalTexture;
|
||||
this.pdMaterial.uniforms.tDepth.value = this.depthTexture;
|
||||
|
||||
this.depthRenderMaterial.uniforms.tDepth.value = this.normalRenderTarget.depthTexture;
|
||||
|
||||
}
|
||||
|
||||
setSceneClipBox( box ) {
|
||||
|
||||
if ( box ) {
|
||||
|
||||
this.gtaoMaterial.needsUpdate = this.gtaoMaterial.defines.SCENE_CLIP_BOX !== 1;
|
||||
this.gtaoMaterial.defines.SCENE_CLIP_BOX = 1;
|
||||
this.gtaoMaterial.uniforms.sceneBoxMin.value.copy( box.min );
|
||||
this.gtaoMaterial.uniforms.sceneBoxMax.value.copy( box.max );
|
||||
|
||||
} else {
|
||||
|
||||
this.gtaoMaterial.needsUpdate = this.gtaoMaterial.defines.SCENE_CLIP_BOX === 0;
|
||||
this.gtaoMaterial.defines.SCENE_CLIP_BOX = 0;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
updateGtaoMaterial( parameters ) {
|
||||
|
||||
if ( parameters.radius !== undefined ) {
|
||||
|
||||
this.gtaoMaterial.uniforms.radius.value = parameters.radius;
|
||||
|
||||
}
|
||||
|
||||
if ( parameters.distanceExponent !== undefined ) {
|
||||
|
||||
this.gtaoMaterial.uniforms.distanceExponent.value = parameters.distanceExponent;
|
||||
|
||||
}
|
||||
|
||||
if ( parameters.thickness !== undefined ) {
|
||||
|
||||
this.gtaoMaterial.uniforms.thickness.value = parameters.thickness;
|
||||
|
||||
}
|
||||
|
||||
if ( parameters.distanceFallOff !== undefined ) {
|
||||
|
||||
this.gtaoMaterial.uniforms.distanceFallOff.value = parameters.distanceFallOff;
|
||||
this.gtaoMaterial.needsUpdate = true;
|
||||
|
||||
}
|
||||
|
||||
if ( parameters.scale !== undefined ) {
|
||||
|
||||
this.gtaoMaterial.uniforms.scale.value = parameters.scale;
|
||||
|
||||
}
|
||||
|
||||
if ( parameters.samples !== undefined && parameters.samples !== this.gtaoMaterial.defines.SAMPLES ) {
|
||||
|
||||
this.gtaoMaterial.defines.SAMPLES = parameters.samples;
|
||||
this.gtaoMaterial.needsUpdate = true;
|
||||
|
||||
}
|
||||
|
||||
if ( parameters.screenSpaceRadius !== undefined && ( parameters.screenSpaceRadius ? 1 : 0 ) !== this.gtaoMaterial.defines.SCREEN_SPACE_RADIUS ) {
|
||||
|
||||
this.gtaoMaterial.defines.SCREEN_SPACE_RADIUS = parameters.screenSpaceRadius ? 1 : 0;
|
||||
this.gtaoMaterial.needsUpdate = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
updatePdMaterial( parameters ) {
|
||||
|
||||
let updateShader = false;
|
||||
|
||||
if ( parameters.lumaPhi !== undefined ) {
|
||||
|
||||
this.pdMaterial.uniforms.lumaPhi.value = parameters.lumaPhi;
|
||||
|
||||
}
|
||||
|
||||
if ( parameters.depthPhi !== undefined ) {
|
||||
|
||||
this.pdMaterial.uniforms.depthPhi.value = parameters.depthPhi;
|
||||
|
||||
}
|
||||
|
||||
if ( parameters.normalPhi !== undefined ) {
|
||||
|
||||
this.pdMaterial.uniforms.normalPhi.value = parameters.normalPhi;
|
||||
|
||||
}
|
||||
|
||||
if ( parameters.radius !== undefined && parameters.radius !== this.radius ) {
|
||||
|
||||
this.pdMaterial.uniforms.radius.value = parameters.radius;
|
||||
|
||||
}
|
||||
|
||||
if ( parameters.radiusExponent !== undefined && parameters.radiusExponent !== this.pdRadiusExponent ) {
|
||||
|
||||
this.pdRadiusExponent = parameters.radiusExponent;
|
||||
updateShader = true;
|
||||
|
||||
}
|
||||
|
||||
if ( parameters.rings !== undefined && parameters.rings !== this.pdRings ) {
|
||||
|
||||
this.pdRings = parameters.rings;
|
||||
updateShader = true;
|
||||
|
||||
}
|
||||
|
||||
if ( parameters.samples !== undefined && parameters.samples !== this.pdSamples ) {
|
||||
|
||||
this.pdSamples = parameters.samples;
|
||||
updateShader = true;
|
||||
|
||||
}
|
||||
|
||||
if ( updateShader ) {
|
||||
|
||||
this.pdMaterial.defines.SAMPLES = this.pdSamples;
|
||||
this.pdMaterial.defines.SAMPLE_VECTORS = generatePdSamplePointInitializer( this.pdSamples, this.pdRings, this.pdRadiusExponent );
|
||||
this.pdMaterial.needsUpdate = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
render( renderer, writeBuffer, readBuffer /*, deltaTime, maskActive */ ) {
|
||||
|
||||
// render normals and depth (honor only meshes, points and lines do not contribute to AO)
|
||||
|
||||
if ( this._renderGBuffer ) {
|
||||
|
||||
this.overrideVisibility();
|
||||
this.renderOverride( renderer, this.normalMaterial, this.normalRenderTarget, 0x7777ff, 1.0 );
|
||||
this.restoreVisibility();
|
||||
|
||||
}
|
||||
|
||||
// render AO
|
||||
|
||||
this.gtaoMaterial.uniforms.cameraNear.value = this.camera.near;
|
||||
this.gtaoMaterial.uniforms.cameraFar.value = this.camera.far;
|
||||
this.gtaoMaterial.uniforms.cameraProjectionMatrix.value.copy( this.camera.projectionMatrix );
|
||||
this.gtaoMaterial.uniforms.cameraProjectionMatrixInverse.value.copy( this.camera.projectionMatrixInverse );
|
||||
this.gtaoMaterial.uniforms.cameraWorldMatrix.value.copy( this.camera.matrixWorld );
|
||||
this.renderPass( renderer, this.gtaoMaterial, this.gtaoRenderTarget, 0xffffff, 1.0 );
|
||||
|
||||
// render poisson denoise
|
||||
|
||||
this.pdMaterial.uniforms.cameraProjectionMatrixInverse.value.copy( this.camera.projectionMatrixInverse );
|
||||
this.renderPass( renderer, this.pdMaterial, this.pdRenderTarget, 0xffffff, 1.0 );
|
||||
|
||||
// output result to screen
|
||||
|
||||
switch ( this.output ) {
|
||||
|
||||
case GTAOPass.OUTPUT.Off:
|
||||
break;
|
||||
|
||||
case GTAOPass.OUTPUT.Diffuse:
|
||||
|
||||
this.copyMaterial.uniforms.tDiffuse.value = readBuffer.texture;
|
||||
this.copyMaterial.blending = NoBlending;
|
||||
this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer );
|
||||
|
||||
break;
|
||||
|
||||
case GTAOPass.OUTPUT.AO:
|
||||
|
||||
this.copyMaterial.uniforms.tDiffuse.value = this.gtaoRenderTarget.texture;
|
||||
this.copyMaterial.blending = NoBlending;
|
||||
this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer );
|
||||
|
||||
break;
|
||||
|
||||
case GTAOPass.OUTPUT.Denoise:
|
||||
|
||||
this.copyMaterial.uniforms.tDiffuse.value = this.pdRenderTarget.texture;
|
||||
this.copyMaterial.blending = NoBlending;
|
||||
this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer );
|
||||
|
||||
break;
|
||||
|
||||
case GTAOPass.OUTPUT.Depth:
|
||||
|
||||
this.depthRenderMaterial.uniforms.cameraNear.value = this.camera.near;
|
||||
this.depthRenderMaterial.uniforms.cameraFar.value = this.camera.far;
|
||||
this.renderPass( renderer, this.depthRenderMaterial, this.renderToScreen ? null : writeBuffer );
|
||||
|
||||
break;
|
||||
|
||||
case GTAOPass.OUTPUT.Normal:
|
||||
|
||||
this.copyMaterial.uniforms.tDiffuse.value = this.normalRenderTarget.texture;
|
||||
this.copyMaterial.blending = NoBlending;
|
||||
this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer );
|
||||
|
||||
break;
|
||||
|
||||
case GTAOPass.OUTPUT.Default:
|
||||
|
||||
this.copyMaterial.uniforms.tDiffuse.value = readBuffer.texture;
|
||||
this.copyMaterial.blending = NoBlending;
|
||||
this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer );
|
||||
|
||||
this.blendMaterial.uniforms.intensity.value = this.blendIntensity;
|
||||
this.blendMaterial.uniforms.tDiffuse.value = this.pdRenderTarget.texture;
|
||||
this.renderPass( renderer, this.blendMaterial, this.renderToScreen ? null : writeBuffer );
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
console.warn( 'THREE.GTAOPass: Unknown output type.' );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
renderPass( renderer, passMaterial, renderTarget, clearColor, clearAlpha ) {
|
||||
|
||||
// save original state
|
||||
renderer.getClearColor( this.originalClearColor );
|
||||
const originalClearAlpha = renderer.getClearAlpha();
|
||||
const originalAutoClear = renderer.autoClear;
|
||||
|
||||
renderer.setRenderTarget( renderTarget );
|
||||
|
||||
// setup pass state
|
||||
renderer.autoClear = false;
|
||||
if ( ( clearColor !== undefined ) && ( clearColor !== null ) ) {
|
||||
|
||||
renderer.setClearColor( clearColor );
|
||||
renderer.setClearAlpha( clearAlpha || 0.0 );
|
||||
renderer.clear();
|
||||
|
||||
}
|
||||
|
||||
this.fsQuad.material = passMaterial;
|
||||
this.fsQuad.render( renderer );
|
||||
|
||||
// restore original state
|
||||
renderer.autoClear = originalAutoClear;
|
||||
renderer.setClearColor( this.originalClearColor );
|
||||
renderer.setClearAlpha( originalClearAlpha );
|
||||
|
||||
}
|
||||
|
||||
renderOverride( renderer, overrideMaterial, renderTarget, clearColor, clearAlpha ) {
|
||||
|
||||
renderer.getClearColor( this.originalClearColor );
|
||||
const originalClearAlpha = renderer.getClearAlpha();
|
||||
const originalAutoClear = renderer.autoClear;
|
||||
|
||||
renderer.setRenderTarget( renderTarget );
|
||||
renderer.autoClear = false;
|
||||
|
||||
clearColor = overrideMaterial.clearColor || clearColor;
|
||||
clearAlpha = overrideMaterial.clearAlpha || clearAlpha;
|
||||
|
||||
if ( ( clearColor !== undefined ) && ( clearColor !== null ) ) {
|
||||
|
||||
renderer.setClearColor( clearColor );
|
||||
renderer.setClearAlpha( clearAlpha || 0.0 );
|
||||
renderer.clear();
|
||||
|
||||
}
|
||||
|
||||
this.scene.overrideMaterial = overrideMaterial;
|
||||
renderer.render( this.scene, this.camera );
|
||||
this.scene.overrideMaterial = null;
|
||||
|
||||
renderer.autoClear = originalAutoClear;
|
||||
renderer.setClearColor( this.originalClearColor );
|
||||
renderer.setClearAlpha( originalClearAlpha );
|
||||
|
||||
}
|
||||
|
||||
setSize( width, height ) {
|
||||
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
|
||||
this.gtaoRenderTarget.setSize( width, height );
|
||||
this.normalRenderTarget.setSize( width, height );
|
||||
this.pdRenderTarget.setSize( width, height );
|
||||
|
||||
this.gtaoMaterial.uniforms.resolution.value.set( width, height );
|
||||
this.gtaoMaterial.uniforms.cameraProjectionMatrix.value.copy( this.camera.projectionMatrix );
|
||||
this.gtaoMaterial.uniforms.cameraProjectionMatrixInverse.value.copy( this.camera.projectionMatrixInverse );
|
||||
|
||||
this.pdMaterial.uniforms.resolution.value.set( width, height );
|
||||
this.pdMaterial.uniforms.cameraProjectionMatrixInverse.value.copy( this.camera.projectionMatrixInverse );
|
||||
|
||||
}
|
||||
|
||||
overrideVisibility() {
|
||||
|
||||
const scene = this.scene;
|
||||
const cache = this._visibilityCache;
|
||||
|
||||
scene.traverse( function ( object ) {
|
||||
|
||||
cache.set( object, object.visible );
|
||||
|
||||
if ( object.isPoints || object.isLine ) object.visible = false;
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
restoreVisibility() {
|
||||
|
||||
const scene = this.scene;
|
||||
const cache = this._visibilityCache;
|
||||
|
||||
scene.traverse( function ( object ) {
|
||||
|
||||
const visible = cache.get( object );
|
||||
object.visible = visible;
|
||||
|
||||
} );
|
||||
|
||||
cache.clear();
|
||||
|
||||
}
|
||||
|
||||
generateNoise( size = 64 ) {
|
||||
|
||||
const simplex = new SimplexNoise();
|
||||
|
||||
const arraySize = size * size * 4;
|
||||
const data = new Uint8Array( arraySize );
|
||||
|
||||
for ( let i = 0; i < size; i ++ ) {
|
||||
|
||||
for ( let j = 0; j < size; j ++ ) {
|
||||
|
||||
const x = i;
|
||||
const y = j;
|
||||
|
||||
data[ ( i * size + j ) * 4 ] = ( simplex.noise( x, y ) * 0.5 + 0.5 ) * 255;
|
||||
data[ ( i * size + j ) * 4 + 1 ] = ( simplex.noise( x + size, y ) * 0.5 + 0.5 ) * 255;
|
||||
data[ ( i * size + j ) * 4 + 2 ] = ( simplex.noise( x, y + size ) * 0.5 + 0.5 ) * 255;
|
||||
data[ ( i * size + j ) * 4 + 3 ] = ( simplex.noise( x + size, y + size ) * 0.5 + 0.5 ) * 255;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const noiseTexture = new DataTexture( data, size, size, RGBAFormat, UnsignedByteType );
|
||||
noiseTexture.wrapS = RepeatWrapping;
|
||||
noiseTexture.wrapT = RepeatWrapping;
|
||||
noiseTexture.needsUpdate = true;
|
||||
|
||||
return noiseTexture;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
GTAOPass.OUTPUT = {
|
||||
'Off': - 1,
|
||||
'Default': 0,
|
||||
'Diffuse': 1,
|
||||
'Depth': 2,
|
||||
'Normal': 3,
|
||||
'AO': 4,
|
||||
'Denoise': 5,
|
||||
};
|
||||
|
||||
export { GTAOPass };
|
125
static/sdk/three/jsm/postprocessing/GlitchPass.js
Normal file
125
static/sdk/three/jsm/postprocessing/GlitchPass.js
Normal file
@ -0,0 +1,125 @@
|
||||
import {
|
||||
DataTexture,
|
||||
FloatType,
|
||||
MathUtils,
|
||||
RedFormat,
|
||||
ShaderMaterial,
|
||||
UniformsUtils
|
||||
} from 'three';
|
||||
import { Pass, FullScreenQuad } from './Pass.js';
|
||||
import { DigitalGlitch } from '../shaders/DigitalGlitch.js';
|
||||
|
||||
class GlitchPass extends Pass {
|
||||
|
||||
constructor( dt_size = 64 ) {
|
||||
|
||||
super();
|
||||
|
||||
const shader = DigitalGlitch;
|
||||
|
||||
this.uniforms = UniformsUtils.clone( shader.uniforms );
|
||||
|
||||
this.heightMap = this.generateHeightmap( dt_size );
|
||||
|
||||
this.uniforms[ 'tDisp' ].value = this.heightMap;
|
||||
|
||||
this.material = new ShaderMaterial( {
|
||||
uniforms: this.uniforms,
|
||||
vertexShader: shader.vertexShader,
|
||||
fragmentShader: shader.fragmentShader
|
||||
} );
|
||||
|
||||
this.fsQuad = new FullScreenQuad( this.material );
|
||||
|
||||
this.goWild = false;
|
||||
this.curF = 0;
|
||||
this.generateTrigger();
|
||||
|
||||
}
|
||||
|
||||
render( renderer, writeBuffer, readBuffer /*, deltaTime, maskActive */ ) {
|
||||
|
||||
this.uniforms[ 'tDiffuse' ].value = readBuffer.texture;
|
||||
this.uniforms[ 'seed' ].value = Math.random();//default seeding
|
||||
this.uniforms[ 'byp' ].value = 0;
|
||||
|
||||
if ( this.curF % this.randX == 0 || this.goWild == true ) {
|
||||
|
||||
this.uniforms[ 'amount' ].value = Math.random() / 30;
|
||||
this.uniforms[ 'angle' ].value = MathUtils.randFloat( - Math.PI, Math.PI );
|
||||
this.uniforms[ 'seed_x' ].value = MathUtils.randFloat( - 1, 1 );
|
||||
this.uniforms[ 'seed_y' ].value = MathUtils.randFloat( - 1, 1 );
|
||||
this.uniforms[ 'distortion_x' ].value = MathUtils.randFloat( 0, 1 );
|
||||
this.uniforms[ 'distortion_y' ].value = MathUtils.randFloat( 0, 1 );
|
||||
this.curF = 0;
|
||||
this.generateTrigger();
|
||||
|
||||
} else if ( this.curF % this.randX < this.randX / 5 ) {
|
||||
|
||||
this.uniforms[ 'amount' ].value = Math.random() / 90;
|
||||
this.uniforms[ 'angle' ].value = MathUtils.randFloat( - Math.PI, Math.PI );
|
||||
this.uniforms[ 'distortion_x' ].value = MathUtils.randFloat( 0, 1 );
|
||||
this.uniforms[ 'distortion_y' ].value = MathUtils.randFloat( 0, 1 );
|
||||
this.uniforms[ 'seed_x' ].value = MathUtils.randFloat( - 0.3, 0.3 );
|
||||
this.uniforms[ 'seed_y' ].value = MathUtils.randFloat( - 0.3, 0.3 );
|
||||
|
||||
} else if ( this.goWild == false ) {
|
||||
|
||||
this.uniforms[ 'byp' ].value = 1;
|
||||
|
||||
}
|
||||
|
||||
this.curF ++;
|
||||
|
||||
if ( this.renderToScreen ) {
|
||||
|
||||
renderer.setRenderTarget( null );
|
||||
this.fsQuad.render( renderer );
|
||||
|
||||
} else {
|
||||
|
||||
renderer.setRenderTarget( writeBuffer );
|
||||
if ( this.clear ) renderer.clear();
|
||||
this.fsQuad.render( renderer );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
generateTrigger() {
|
||||
|
||||
this.randX = MathUtils.randInt( 120, 240 );
|
||||
|
||||
}
|
||||
|
||||
generateHeightmap( dt_size ) {
|
||||
|
||||
const data_arr = new Float32Array( dt_size * dt_size );
|
||||
const length = dt_size * dt_size;
|
||||
|
||||
for ( let i = 0; i < length; i ++ ) {
|
||||
|
||||
const val = MathUtils.randFloat( 0, 1 );
|
||||
data_arr[ i ] = val;
|
||||
|
||||
}
|
||||
|
||||
const texture = new DataTexture( data_arr, dt_size, dt_size, RedFormat, FloatType );
|
||||
texture.needsUpdate = true;
|
||||
return texture;
|
||||
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
||||
this.material.dispose();
|
||||
|
||||
this.heightMap.dispose();
|
||||
|
||||
this.fsQuad.dispose();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { GlitchPass };
|
79
static/sdk/three/jsm/postprocessing/HalftonePass.js
Normal file
79
static/sdk/three/jsm/postprocessing/HalftonePass.js
Normal file
@ -0,0 +1,79 @@
|
||||
import {
|
||||
ShaderMaterial,
|
||||
UniformsUtils
|
||||
} from 'three';
|
||||
import { Pass, FullScreenQuad } from './Pass.js';
|
||||
import { HalftoneShader } from '../shaders/HalftoneShader.js';
|
||||
|
||||
/**
|
||||
* RGB Halftone pass for three.js effects composer. Requires HalftoneShader.
|
||||
*/
|
||||
|
||||
class HalftonePass extends Pass {
|
||||
|
||||
constructor( width, height, params ) {
|
||||
|
||||
super();
|
||||
|
||||
this.uniforms = UniformsUtils.clone( HalftoneShader.uniforms );
|
||||
this.material = new ShaderMaterial( {
|
||||
uniforms: this.uniforms,
|
||||
fragmentShader: HalftoneShader.fragmentShader,
|
||||
vertexShader: HalftoneShader.vertexShader
|
||||
} );
|
||||
|
||||
// set params
|
||||
this.uniforms.width.value = width;
|
||||
this.uniforms.height.value = height;
|
||||
|
||||
for ( const key in params ) {
|
||||
|
||||
if ( params.hasOwnProperty( key ) && this.uniforms.hasOwnProperty( key ) ) {
|
||||
|
||||
this.uniforms[ key ].value = params[ key ];
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.fsQuad = new FullScreenQuad( this.material );
|
||||
|
||||
}
|
||||
|
||||
render( renderer, writeBuffer, readBuffer/*, deltaTime, maskActive*/ ) {
|
||||
|
||||
this.material.uniforms[ 'tDiffuse' ].value = readBuffer.texture;
|
||||
|
||||
if ( this.renderToScreen ) {
|
||||
|
||||
renderer.setRenderTarget( null );
|
||||
this.fsQuad.render( renderer );
|
||||
|
||||
} else {
|
||||
|
||||
renderer.setRenderTarget( writeBuffer );
|
||||
if ( this.clear ) renderer.clear();
|
||||
this.fsQuad.render( renderer );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
setSize( width, height ) {
|
||||
|
||||
this.uniforms.width.value = width;
|
||||
this.uniforms.height.value = height;
|
||||
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
||||
this.material.dispose();
|
||||
|
||||
this.fsQuad.dispose();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { HalftonePass };
|
108
static/sdk/three/jsm/postprocessing/LUTPass.js
Normal file
108
static/sdk/three/jsm/postprocessing/LUTPass.js
Normal file
@ -0,0 +1,108 @@
|
||||
import { ShaderPass } from './ShaderPass.js';
|
||||
|
||||
const LUTShader = {
|
||||
|
||||
name: 'LUTShader',
|
||||
|
||||
uniforms: {
|
||||
|
||||
lut: { value: null },
|
||||
lutSize: { value: 0 },
|
||||
|
||||
tDiffuse: { value: null },
|
||||
intensity: { value: 1.0 },
|
||||
},
|
||||
|
||||
vertexShader: /* glsl */`
|
||||
|
||||
varying vec2 vUv;
|
||||
|
||||
void main() {
|
||||
|
||||
vUv = uv;
|
||||
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
|
||||
|
||||
}
|
||||
|
||||
`,
|
||||
|
||||
fragmentShader: /* glsl */`
|
||||
|
||||
uniform float lutSize;
|
||||
uniform sampler3D lut;
|
||||
|
||||
varying vec2 vUv;
|
||||
uniform float intensity;
|
||||
uniform sampler2D tDiffuse;
|
||||
void main() {
|
||||
|
||||
vec4 val = texture2D( tDiffuse, vUv );
|
||||
vec4 lutVal;
|
||||
|
||||
// pull the sample in by half a pixel so the sample begins
|
||||
// at the center of the edge pixels.
|
||||
float pixelWidth = 1.0 / lutSize;
|
||||
float halfPixelWidth = 0.5 / lutSize;
|
||||
vec3 uvw = vec3( halfPixelWidth ) + val.rgb * ( 1.0 - pixelWidth );
|
||||
|
||||
|
||||
lutVal = vec4( texture( lut, uvw ).rgb, val.a );
|
||||
|
||||
gl_FragColor = vec4( mix( val, lutVal, intensity ) );
|
||||
|
||||
}
|
||||
|
||||
`,
|
||||
|
||||
};
|
||||
|
||||
class LUTPass extends ShaderPass {
|
||||
|
||||
set lut( v ) {
|
||||
|
||||
const material = this.material;
|
||||
|
||||
if ( v !== this.lut ) {
|
||||
|
||||
material.uniforms.lut.value = null;
|
||||
|
||||
if ( v ) {
|
||||
|
||||
material.uniforms.lutSize.value = v.image.width;
|
||||
material.uniforms.lut.value = v;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
get lut() {
|
||||
|
||||
return this.material.uniforms.lut.value;
|
||||
|
||||
}
|
||||
|
||||
set intensity( v ) {
|
||||
|
||||
this.material.uniforms.intensity.value = v;
|
||||
|
||||
}
|
||||
|
||||
get intensity() {
|
||||
|
||||
return this.material.uniforms.intensity.value;
|
||||
|
||||
}
|
||||
|
||||
constructor( options = {} ) {
|
||||
|
||||
super( LUTShader );
|
||||
this.lut = options.lut || null;
|
||||
this.intensity = 'intensity' in options ? options.intensity : 1;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { LUTPass };
|
104
static/sdk/three/jsm/postprocessing/MaskPass.js
Normal file
104
static/sdk/three/jsm/postprocessing/MaskPass.js
Normal file
@ -0,0 +1,104 @@
|
||||
import { Pass } from './Pass.js';
|
||||
|
||||
class MaskPass extends Pass {
|
||||
|
||||
constructor( scene, camera ) {
|
||||
|
||||
super();
|
||||
|
||||
this.scene = scene;
|
||||
this.camera = camera;
|
||||
|
||||
this.clear = true;
|
||||
this.needsSwap = false;
|
||||
|
||||
this.inverse = false;
|
||||
|
||||
}
|
||||
|
||||
render( renderer, writeBuffer, readBuffer /*, deltaTime, maskActive */ ) {
|
||||
|
||||
const context = renderer.getContext();
|
||||
const state = renderer.state;
|
||||
|
||||
// don't update color or depth
|
||||
|
||||
state.buffers.color.setMask( false );
|
||||
state.buffers.depth.setMask( false );
|
||||
|
||||
// lock buffers
|
||||
|
||||
state.buffers.color.setLocked( true );
|
||||
state.buffers.depth.setLocked( true );
|
||||
|
||||
// set up stencil
|
||||
|
||||
let writeValue, clearValue;
|
||||
|
||||
if ( this.inverse ) {
|
||||
|
||||
writeValue = 0;
|
||||
clearValue = 1;
|
||||
|
||||
} else {
|
||||
|
||||
writeValue = 1;
|
||||
clearValue = 0;
|
||||
|
||||
}
|
||||
|
||||
state.buffers.stencil.setTest( true );
|
||||
state.buffers.stencil.setOp( context.REPLACE, context.REPLACE, context.REPLACE );
|
||||
state.buffers.stencil.setFunc( context.ALWAYS, writeValue, 0xffffffff );
|
||||
state.buffers.stencil.setClear( clearValue );
|
||||
state.buffers.stencil.setLocked( true );
|
||||
|
||||
// draw into the stencil buffer
|
||||
|
||||
renderer.setRenderTarget( readBuffer );
|
||||
if ( this.clear ) renderer.clear();
|
||||
renderer.render( this.scene, this.camera );
|
||||
|
||||
renderer.setRenderTarget( writeBuffer );
|
||||
if ( this.clear ) renderer.clear();
|
||||
renderer.render( this.scene, this.camera );
|
||||
|
||||
// unlock color and depth buffer and make them writable for subsequent rendering/clearing
|
||||
|
||||
state.buffers.color.setLocked( false );
|
||||
state.buffers.depth.setLocked( false );
|
||||
|
||||
state.buffers.color.setMask( true );
|
||||
state.buffers.depth.setMask( true );
|
||||
|
||||
// only render where stencil is set to 1
|
||||
|
||||
state.buffers.stencil.setLocked( false );
|
||||
state.buffers.stencil.setFunc( context.EQUAL, 1, 0xffffffff ); // draw if == 1
|
||||
state.buffers.stencil.setOp( context.KEEP, context.KEEP, context.KEEP );
|
||||
state.buffers.stencil.setLocked( true );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class ClearMaskPass extends Pass {
|
||||
|
||||
constructor() {
|
||||
|
||||
super();
|
||||
|
||||
this.needsSwap = false;
|
||||
|
||||
}
|
||||
|
||||
render( renderer /*, writeBuffer, readBuffer, deltaTime, maskActive */ ) {
|
||||
|
||||
renderer.state.buffers.stencil.setLocked( false );
|
||||
renderer.state.buffers.stencil.setTest( false );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { MaskPass, ClearMaskPass };
|
654
static/sdk/three/jsm/postprocessing/OutlinePass.js
Normal file
654
static/sdk/three/jsm/postprocessing/OutlinePass.js
Normal file
@ -0,0 +1,654 @@
|
||||
import {
|
||||
AdditiveBlending,
|
||||
Color,
|
||||
DoubleSide,
|
||||
HalfFloatType,
|
||||
Matrix4,
|
||||
MeshDepthMaterial,
|
||||
NoBlending,
|
||||
RGBADepthPacking,
|
||||
ShaderMaterial,
|
||||
UniformsUtils,
|
||||
Vector2,
|
||||
Vector3,
|
||||
WebGLRenderTarget
|
||||
} from 'three';
|
||||
import { Pass, FullScreenQuad } from './Pass.js';
|
||||
import { CopyShader } from '../shaders/CopyShader.js';
|
||||
|
||||
class OutlinePass extends Pass {
|
||||
|
||||
constructor( resolution, scene, camera, selectedObjects ) {
|
||||
|
||||
super();
|
||||
|
||||
this.renderScene = scene;
|
||||
this.renderCamera = camera;
|
||||
this.selectedObjects = selectedObjects !== undefined ? selectedObjects : [];
|
||||
this.visibleEdgeColor = new Color( 1, 1, 1 );
|
||||
this.hiddenEdgeColor = new Color( 0.1, 0.04, 0.02 );
|
||||
this.edgeGlow = 0.0;
|
||||
this.usePatternTexture = false;
|
||||
this.edgeThickness = 1.0;
|
||||
this.edgeStrength = 3.0;
|
||||
this.downSampleRatio = 2;
|
||||
this.pulsePeriod = 0;
|
||||
|
||||
this._visibilityCache = new Map();
|
||||
|
||||
|
||||
this.resolution = ( resolution !== undefined ) ? new Vector2( resolution.x, resolution.y ) : new Vector2( 256, 256 );
|
||||
|
||||
const resx = Math.round( this.resolution.x / this.downSampleRatio );
|
||||
const resy = Math.round( this.resolution.y / this.downSampleRatio );
|
||||
|
||||
this.renderTargetMaskBuffer = new WebGLRenderTarget( this.resolution.x, this.resolution.y );
|
||||
this.renderTargetMaskBuffer.texture.name = 'OutlinePass.mask';
|
||||
this.renderTargetMaskBuffer.texture.generateMipmaps = false;
|
||||
|
||||
this.depthMaterial = new MeshDepthMaterial();
|
||||
this.depthMaterial.side = DoubleSide;
|
||||
this.depthMaterial.depthPacking = RGBADepthPacking;
|
||||
this.depthMaterial.blending = NoBlending;
|
||||
|
||||
this.prepareMaskMaterial = this.getPrepareMaskMaterial();
|
||||
this.prepareMaskMaterial.side = DoubleSide;
|
||||
this.prepareMaskMaterial.fragmentShader = replaceDepthToViewZ( this.prepareMaskMaterial.fragmentShader, this.renderCamera );
|
||||
|
||||
this.renderTargetDepthBuffer = new WebGLRenderTarget( this.resolution.x, this.resolution.y, { type: HalfFloatType } );
|
||||
this.renderTargetDepthBuffer.texture.name = 'OutlinePass.depth';
|
||||
this.renderTargetDepthBuffer.texture.generateMipmaps = false;
|
||||
|
||||
this.renderTargetMaskDownSampleBuffer = new WebGLRenderTarget( resx, resy, { type: HalfFloatType } );
|
||||
this.renderTargetMaskDownSampleBuffer.texture.name = 'OutlinePass.depthDownSample';
|
||||
this.renderTargetMaskDownSampleBuffer.texture.generateMipmaps = false;
|
||||
|
||||
this.renderTargetBlurBuffer1 = new WebGLRenderTarget( resx, resy, { type: HalfFloatType } );
|
||||
this.renderTargetBlurBuffer1.texture.name = 'OutlinePass.blur1';
|
||||
this.renderTargetBlurBuffer1.texture.generateMipmaps = false;
|
||||
this.renderTargetBlurBuffer2 = new WebGLRenderTarget( Math.round( resx / 2 ), Math.round( resy / 2 ), { type: HalfFloatType } );
|
||||
this.renderTargetBlurBuffer2.texture.name = 'OutlinePass.blur2';
|
||||
this.renderTargetBlurBuffer2.texture.generateMipmaps = false;
|
||||
|
||||
this.edgeDetectionMaterial = this.getEdgeDetectionMaterial();
|
||||
this.renderTargetEdgeBuffer1 = new WebGLRenderTarget( resx, resy, { type: HalfFloatType } );
|
||||
this.renderTargetEdgeBuffer1.texture.name = 'OutlinePass.edge1';
|
||||
this.renderTargetEdgeBuffer1.texture.generateMipmaps = false;
|
||||
this.renderTargetEdgeBuffer2 = new WebGLRenderTarget( Math.round( resx / 2 ), Math.round( resy / 2 ), { type: HalfFloatType } );
|
||||
this.renderTargetEdgeBuffer2.texture.name = 'OutlinePass.edge2';
|
||||
this.renderTargetEdgeBuffer2.texture.generateMipmaps = false;
|
||||
|
||||
const MAX_EDGE_THICKNESS = 4;
|
||||
const MAX_EDGE_GLOW = 4;
|
||||
|
||||
this.separableBlurMaterial1 = this.getSeperableBlurMaterial( MAX_EDGE_THICKNESS );
|
||||
this.separableBlurMaterial1.uniforms[ 'texSize' ].value.set( resx, resy );
|
||||
this.separableBlurMaterial1.uniforms[ 'kernelRadius' ].value = 1;
|
||||
this.separableBlurMaterial2 = this.getSeperableBlurMaterial( MAX_EDGE_GLOW );
|
||||
this.separableBlurMaterial2.uniforms[ 'texSize' ].value.set( Math.round( resx / 2 ), Math.round( resy / 2 ) );
|
||||
this.separableBlurMaterial2.uniforms[ 'kernelRadius' ].value = MAX_EDGE_GLOW;
|
||||
|
||||
// Overlay material
|
||||
this.overlayMaterial = this.getOverlayMaterial();
|
||||
|
||||
// copy material
|
||||
|
||||
const copyShader = CopyShader;
|
||||
|
||||
this.copyUniforms = UniformsUtils.clone( copyShader.uniforms );
|
||||
|
||||
this.materialCopy = new ShaderMaterial( {
|
||||
uniforms: this.copyUniforms,
|
||||
vertexShader: copyShader.vertexShader,
|
||||
fragmentShader: copyShader.fragmentShader,
|
||||
blending: NoBlending,
|
||||
depthTest: false,
|
||||
depthWrite: false
|
||||
} );
|
||||
|
||||
this.enabled = true;
|
||||
this.needsSwap = false;
|
||||
|
||||
this._oldClearColor = new Color();
|
||||
this.oldClearAlpha = 1;
|
||||
|
||||
this.fsQuad = new FullScreenQuad( null );
|
||||
|
||||
this.tempPulseColor1 = new Color();
|
||||
this.tempPulseColor2 = new Color();
|
||||
this.textureMatrix = new Matrix4();
|
||||
|
||||
function replaceDepthToViewZ( string, camera ) {
|
||||
|
||||
const type = camera.isPerspectiveCamera ? 'perspective' : 'orthographic';
|
||||
|
||||
return string.replace( /DEPTH_TO_VIEW_Z/g, type + 'DepthToViewZ' );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
||||
this.renderTargetMaskBuffer.dispose();
|
||||
this.renderTargetDepthBuffer.dispose();
|
||||
this.renderTargetMaskDownSampleBuffer.dispose();
|
||||
this.renderTargetBlurBuffer1.dispose();
|
||||
this.renderTargetBlurBuffer2.dispose();
|
||||
this.renderTargetEdgeBuffer1.dispose();
|
||||
this.renderTargetEdgeBuffer2.dispose();
|
||||
|
||||
this.depthMaterial.dispose();
|
||||
this.prepareMaskMaterial.dispose();
|
||||
this.edgeDetectionMaterial.dispose();
|
||||
this.separableBlurMaterial1.dispose();
|
||||
this.separableBlurMaterial2.dispose();
|
||||
this.overlayMaterial.dispose();
|
||||
this.materialCopy.dispose();
|
||||
|
||||
this.fsQuad.dispose();
|
||||
|
||||
}
|
||||
|
||||
setSize( width, height ) {
|
||||
|
||||
this.renderTargetMaskBuffer.setSize( width, height );
|
||||
this.renderTargetDepthBuffer.setSize( width, height );
|
||||
|
||||
let resx = Math.round( width / this.downSampleRatio );
|
||||
let resy = Math.round( height / this.downSampleRatio );
|
||||
this.renderTargetMaskDownSampleBuffer.setSize( resx, resy );
|
||||
this.renderTargetBlurBuffer1.setSize( resx, resy );
|
||||
this.renderTargetEdgeBuffer1.setSize( resx, resy );
|
||||
this.separableBlurMaterial1.uniforms[ 'texSize' ].value.set( resx, resy );
|
||||
|
||||
resx = Math.round( resx / 2 );
|
||||
resy = Math.round( resy / 2 );
|
||||
|
||||
this.renderTargetBlurBuffer2.setSize( resx, resy );
|
||||
this.renderTargetEdgeBuffer2.setSize( resx, resy );
|
||||
|
||||
this.separableBlurMaterial2.uniforms[ 'texSize' ].value.set( resx, resy );
|
||||
|
||||
}
|
||||
|
||||
changeVisibilityOfSelectedObjects( bVisible ) {
|
||||
|
||||
const cache = this._visibilityCache;
|
||||
|
||||
function gatherSelectedMeshesCallBack( object ) {
|
||||
|
||||
if ( object.isMesh ) {
|
||||
|
||||
if ( bVisible === true ) {
|
||||
|
||||
object.visible = cache.get( object );
|
||||
|
||||
} else {
|
||||
|
||||
cache.set( object, object.visible );
|
||||
object.visible = bVisible;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for ( let i = 0; i < this.selectedObjects.length; i ++ ) {
|
||||
|
||||
const selectedObject = this.selectedObjects[ i ];
|
||||
selectedObject.traverse( gatherSelectedMeshesCallBack );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
changeVisibilityOfNonSelectedObjects( bVisible ) {
|
||||
|
||||
const cache = this._visibilityCache;
|
||||
const selectedMeshes = [];
|
||||
|
||||
function gatherSelectedMeshesCallBack( object ) {
|
||||
|
||||
if ( object.isMesh ) selectedMeshes.push( object );
|
||||
|
||||
}
|
||||
|
||||
for ( let i = 0; i < this.selectedObjects.length; i ++ ) {
|
||||
|
||||
const selectedObject = this.selectedObjects[ i ];
|
||||
selectedObject.traverse( gatherSelectedMeshesCallBack );
|
||||
|
||||
}
|
||||
|
||||
function VisibilityChangeCallBack( object ) {
|
||||
|
||||
if ( object.isMesh || object.isSprite ) {
|
||||
|
||||
// only meshes and sprites are supported by OutlinePass
|
||||
|
||||
let bFound = false;
|
||||
|
||||
for ( let i = 0; i < selectedMeshes.length; i ++ ) {
|
||||
|
||||
const selectedObjectId = selectedMeshes[ i ].id;
|
||||
|
||||
if ( selectedObjectId === object.id ) {
|
||||
|
||||
bFound = true;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( bFound === false ) {
|
||||
|
||||
const visibility = object.visible;
|
||||
|
||||
if ( bVisible === false || cache.get( object ) === true ) {
|
||||
|
||||
object.visible = bVisible;
|
||||
|
||||
}
|
||||
|
||||
cache.set( object, visibility );
|
||||
|
||||
}
|
||||
|
||||
} else if ( object.isPoints || object.isLine ) {
|
||||
|
||||
// the visibilty of points and lines is always set to false in order to
|
||||
// not affect the outline computation
|
||||
|
||||
if ( bVisible === true ) {
|
||||
|
||||
object.visible = cache.get( object ); // restore
|
||||
|
||||
} else {
|
||||
|
||||
cache.set( object, object.visible );
|
||||
object.visible = bVisible;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.renderScene.traverse( VisibilityChangeCallBack );
|
||||
|
||||
}
|
||||
|
||||
updateTextureMatrix() {
|
||||
|
||||
this.textureMatrix.set( 0.5, 0.0, 0.0, 0.5,
|
||||
0.0, 0.5, 0.0, 0.5,
|
||||
0.0, 0.0, 0.5, 0.5,
|
||||
0.0, 0.0, 0.0, 1.0 );
|
||||
this.textureMatrix.multiply( this.renderCamera.projectionMatrix );
|
||||
this.textureMatrix.multiply( this.renderCamera.matrixWorldInverse );
|
||||
|
||||
}
|
||||
|
||||
render( renderer, writeBuffer, readBuffer, deltaTime, maskActive ) {
|
||||
|
||||
if ( this.selectedObjects.length > 0 ) {
|
||||
|
||||
renderer.getClearColor( this._oldClearColor );
|
||||
this.oldClearAlpha = renderer.getClearAlpha();
|
||||
const oldAutoClear = renderer.autoClear;
|
||||
|
||||
renderer.autoClear = false;
|
||||
|
||||
if ( maskActive ) renderer.state.buffers.stencil.setTest( false );
|
||||
|
||||
renderer.setClearColor( 0xffffff, 1 );
|
||||
|
||||
// Make selected objects invisible
|
||||
this.changeVisibilityOfSelectedObjects( false );
|
||||
|
||||
const currentBackground = this.renderScene.background;
|
||||
this.renderScene.background = null;
|
||||
|
||||
// 1. Draw Non Selected objects in the depth buffer
|
||||
this.renderScene.overrideMaterial = this.depthMaterial;
|
||||
renderer.setRenderTarget( this.renderTargetDepthBuffer );
|
||||
renderer.clear();
|
||||
renderer.render( this.renderScene, this.renderCamera );
|
||||
|
||||
// Make selected objects visible
|
||||
this.changeVisibilityOfSelectedObjects( true );
|
||||
this._visibilityCache.clear();
|
||||
|
||||
// Update Texture Matrix for Depth compare
|
||||
this.updateTextureMatrix();
|
||||
|
||||
// Make non selected objects invisible, and draw only the selected objects, by comparing the depth buffer of non selected objects
|
||||
this.changeVisibilityOfNonSelectedObjects( false );
|
||||
this.renderScene.overrideMaterial = this.prepareMaskMaterial;
|
||||
this.prepareMaskMaterial.uniforms[ 'cameraNearFar' ].value.set( this.renderCamera.near, this.renderCamera.far );
|
||||
this.prepareMaskMaterial.uniforms[ 'depthTexture' ].value = this.renderTargetDepthBuffer.texture;
|
||||
this.prepareMaskMaterial.uniforms[ 'textureMatrix' ].value = this.textureMatrix;
|
||||
renderer.setRenderTarget( this.renderTargetMaskBuffer );
|
||||
renderer.clear();
|
||||
renderer.render( this.renderScene, this.renderCamera );
|
||||
this.renderScene.overrideMaterial = null;
|
||||
this.changeVisibilityOfNonSelectedObjects( true );
|
||||
this._visibilityCache.clear();
|
||||
|
||||
this.renderScene.background = currentBackground;
|
||||
|
||||
// 2. Downsample to Half resolution
|
||||
this.fsQuad.material = this.materialCopy;
|
||||
this.copyUniforms[ 'tDiffuse' ].value = this.renderTargetMaskBuffer.texture;
|
||||
renderer.setRenderTarget( this.renderTargetMaskDownSampleBuffer );
|
||||
renderer.clear();
|
||||
this.fsQuad.render( renderer );
|
||||
|
||||
this.tempPulseColor1.copy( this.visibleEdgeColor );
|
||||
this.tempPulseColor2.copy( this.hiddenEdgeColor );
|
||||
|
||||
if ( this.pulsePeriod > 0 ) {
|
||||
|
||||
const scalar = ( 1 + 0.25 ) / 2 + Math.cos( performance.now() * 0.01 / this.pulsePeriod ) * ( 1.0 - 0.25 ) / 2;
|
||||
this.tempPulseColor1.multiplyScalar( scalar );
|
||||
this.tempPulseColor2.multiplyScalar( scalar );
|
||||
|
||||
}
|
||||
|
||||
// 3. Apply Edge Detection Pass
|
||||
this.fsQuad.material = this.edgeDetectionMaterial;
|
||||
this.edgeDetectionMaterial.uniforms[ 'maskTexture' ].value = this.renderTargetMaskDownSampleBuffer.texture;
|
||||
this.edgeDetectionMaterial.uniforms[ 'texSize' ].value.set( this.renderTargetMaskDownSampleBuffer.width, this.renderTargetMaskDownSampleBuffer.height );
|
||||
this.edgeDetectionMaterial.uniforms[ 'visibleEdgeColor' ].value = this.tempPulseColor1;
|
||||
this.edgeDetectionMaterial.uniforms[ 'hiddenEdgeColor' ].value = this.tempPulseColor2;
|
||||
renderer.setRenderTarget( this.renderTargetEdgeBuffer1 );
|
||||
renderer.clear();
|
||||
this.fsQuad.render( renderer );
|
||||
|
||||
// 4. Apply Blur on Half res
|
||||
this.fsQuad.material = this.separableBlurMaterial1;
|
||||
this.separableBlurMaterial1.uniforms[ 'colorTexture' ].value = this.renderTargetEdgeBuffer1.texture;
|
||||
this.separableBlurMaterial1.uniforms[ 'direction' ].value = OutlinePass.BlurDirectionX;
|
||||
this.separableBlurMaterial1.uniforms[ 'kernelRadius' ].value = this.edgeThickness;
|
||||
renderer.setRenderTarget( this.renderTargetBlurBuffer1 );
|
||||
renderer.clear();
|
||||
this.fsQuad.render( renderer );
|
||||
this.separableBlurMaterial1.uniforms[ 'colorTexture' ].value = this.renderTargetBlurBuffer1.texture;
|
||||
this.separableBlurMaterial1.uniforms[ 'direction' ].value = OutlinePass.BlurDirectionY;
|
||||
renderer.setRenderTarget( this.renderTargetEdgeBuffer1 );
|
||||
renderer.clear();
|
||||
this.fsQuad.render( renderer );
|
||||
|
||||
// Apply Blur on quarter res
|
||||
this.fsQuad.material = this.separableBlurMaterial2;
|
||||
this.separableBlurMaterial2.uniforms[ 'colorTexture' ].value = this.renderTargetEdgeBuffer1.texture;
|
||||
this.separableBlurMaterial2.uniforms[ 'direction' ].value = OutlinePass.BlurDirectionX;
|
||||
renderer.setRenderTarget( this.renderTargetBlurBuffer2 );
|
||||
renderer.clear();
|
||||
this.fsQuad.render( renderer );
|
||||
this.separableBlurMaterial2.uniforms[ 'colorTexture' ].value = this.renderTargetBlurBuffer2.texture;
|
||||
this.separableBlurMaterial2.uniforms[ 'direction' ].value = OutlinePass.BlurDirectionY;
|
||||
renderer.setRenderTarget( this.renderTargetEdgeBuffer2 );
|
||||
renderer.clear();
|
||||
this.fsQuad.render( renderer );
|
||||
|
||||
// Blend it additively over the input texture
|
||||
this.fsQuad.material = this.overlayMaterial;
|
||||
this.overlayMaterial.uniforms[ 'maskTexture' ].value = this.renderTargetMaskBuffer.texture;
|
||||
this.overlayMaterial.uniforms[ 'edgeTexture1' ].value = this.renderTargetEdgeBuffer1.texture;
|
||||
this.overlayMaterial.uniforms[ 'edgeTexture2' ].value = this.renderTargetEdgeBuffer2.texture;
|
||||
this.overlayMaterial.uniforms[ 'patternTexture' ].value = this.patternTexture;
|
||||
this.overlayMaterial.uniforms[ 'edgeStrength' ].value = this.edgeStrength;
|
||||
this.overlayMaterial.uniforms[ 'edgeGlow' ].value = this.edgeGlow;
|
||||
this.overlayMaterial.uniforms[ 'usePatternTexture' ].value = this.usePatternTexture;
|
||||
|
||||
|
||||
if ( maskActive ) renderer.state.buffers.stencil.setTest( true );
|
||||
|
||||
renderer.setRenderTarget( readBuffer );
|
||||
this.fsQuad.render( renderer );
|
||||
|
||||
renderer.setClearColor( this._oldClearColor, this.oldClearAlpha );
|
||||
renderer.autoClear = oldAutoClear;
|
||||
|
||||
}
|
||||
|
||||
if ( this.renderToScreen ) {
|
||||
|
||||
this.fsQuad.material = this.materialCopy;
|
||||
this.copyUniforms[ 'tDiffuse' ].value = readBuffer.texture;
|
||||
renderer.setRenderTarget( null );
|
||||
this.fsQuad.render( renderer );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
getPrepareMaskMaterial() {
|
||||
|
||||
return new ShaderMaterial( {
|
||||
|
||||
uniforms: {
|
||||
'depthTexture': { value: null },
|
||||
'cameraNearFar': { value: new Vector2( 0.5, 0.5 ) },
|
||||
'textureMatrix': { value: null }
|
||||
},
|
||||
|
||||
vertexShader:
|
||||
`#include <morphtarget_pars_vertex>
|
||||
#include <skinning_pars_vertex>
|
||||
|
||||
varying vec4 projTexCoord;
|
||||
varying vec4 vPosition;
|
||||
uniform mat4 textureMatrix;
|
||||
|
||||
void main() {
|
||||
|
||||
#include <skinbase_vertex>
|
||||
#include <begin_vertex>
|
||||
#include <morphtarget_vertex>
|
||||
#include <skinning_vertex>
|
||||
#include <project_vertex>
|
||||
|
||||
vPosition = mvPosition;
|
||||
|
||||
vec4 worldPosition = vec4( transformed, 1.0 );
|
||||
|
||||
#ifdef USE_INSTANCING
|
||||
|
||||
worldPosition = instanceMatrix * worldPosition;
|
||||
|
||||
#endif
|
||||
|
||||
worldPosition = modelMatrix * worldPosition;
|
||||
|
||||
projTexCoord = textureMatrix * worldPosition;
|
||||
|
||||
}`,
|
||||
|
||||
fragmentShader:
|
||||
`#include <packing>
|
||||
varying vec4 vPosition;
|
||||
varying vec4 projTexCoord;
|
||||
uniform sampler2D depthTexture;
|
||||
uniform vec2 cameraNearFar;
|
||||
|
||||
void main() {
|
||||
|
||||
float depth = unpackRGBAToDepth(texture2DProj( depthTexture, projTexCoord ));
|
||||
float viewZ = - DEPTH_TO_VIEW_Z( depth, cameraNearFar.x, cameraNearFar.y );
|
||||
float depthTest = (-vPosition.z > viewZ) ? 1.0 : 0.0;
|
||||
gl_FragColor = vec4(0.0, depthTest, 1.0, 1.0);
|
||||
|
||||
}`
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
getEdgeDetectionMaterial() {
|
||||
|
||||
return new ShaderMaterial( {
|
||||
|
||||
uniforms: {
|
||||
'maskTexture': { value: null },
|
||||
'texSize': { value: new Vector2( 0.5, 0.5 ) },
|
||||
'visibleEdgeColor': { value: new Vector3( 1.0, 1.0, 1.0 ) },
|
||||
'hiddenEdgeColor': { value: new Vector3( 1.0, 1.0, 1.0 ) },
|
||||
},
|
||||
|
||||
vertexShader:
|
||||
`varying vec2 vUv;
|
||||
|
||||
void main() {
|
||||
vUv = uv;
|
||||
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
|
||||
}`,
|
||||
|
||||
fragmentShader:
|
||||
`varying vec2 vUv;
|
||||
|
||||
uniform sampler2D maskTexture;
|
||||
uniform vec2 texSize;
|
||||
uniform vec3 visibleEdgeColor;
|
||||
uniform vec3 hiddenEdgeColor;
|
||||
|
||||
void main() {
|
||||
vec2 invSize = 1.0 / texSize;
|
||||
vec4 uvOffset = vec4(1.0, 0.0, 0.0, 1.0) * vec4(invSize, invSize);
|
||||
vec4 c1 = texture2D( maskTexture, vUv + uvOffset.xy);
|
||||
vec4 c2 = texture2D( maskTexture, vUv - uvOffset.xy);
|
||||
vec4 c3 = texture2D( maskTexture, vUv + uvOffset.yw);
|
||||
vec4 c4 = texture2D( maskTexture, vUv - uvOffset.yw);
|
||||
float diff1 = (c1.r - c2.r)*0.5;
|
||||
float diff2 = (c3.r - c4.r)*0.5;
|
||||
float d = length( vec2(diff1, diff2) );
|
||||
float a1 = min(c1.g, c2.g);
|
||||
float a2 = min(c3.g, c4.g);
|
||||
float visibilityFactor = min(a1, a2);
|
||||
vec3 edgeColor = 1.0 - visibilityFactor > 0.001 ? visibleEdgeColor : hiddenEdgeColor;
|
||||
gl_FragColor = vec4(edgeColor, 1.0) * vec4(d);
|
||||
}`
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
getSeperableBlurMaterial( maxRadius ) {
|
||||
|
||||
return new ShaderMaterial( {
|
||||
|
||||
defines: {
|
||||
'MAX_RADIUS': maxRadius,
|
||||
},
|
||||
|
||||
uniforms: {
|
||||
'colorTexture': { value: null },
|
||||
'texSize': { value: new Vector2( 0.5, 0.5 ) },
|
||||
'direction': { value: new Vector2( 0.5, 0.5 ) },
|
||||
'kernelRadius': { value: 1.0 }
|
||||
},
|
||||
|
||||
vertexShader:
|
||||
`varying vec2 vUv;
|
||||
|
||||
void main() {
|
||||
vUv = uv;
|
||||
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
|
||||
}`,
|
||||
|
||||
fragmentShader:
|
||||
`#include <common>
|
||||
varying vec2 vUv;
|
||||
uniform sampler2D colorTexture;
|
||||
uniform vec2 texSize;
|
||||
uniform vec2 direction;
|
||||
uniform float kernelRadius;
|
||||
|
||||
float gaussianPdf(in float x, in float sigma) {
|
||||
return 0.39894 * exp( -0.5 * x * x/( sigma * sigma))/sigma;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 invSize = 1.0 / texSize;
|
||||
float sigma = kernelRadius/2.0;
|
||||
float weightSum = gaussianPdf(0.0, sigma);
|
||||
vec4 diffuseSum = texture2D( colorTexture, vUv) * weightSum;
|
||||
vec2 delta = direction * invSize * kernelRadius/float(MAX_RADIUS);
|
||||
vec2 uvOffset = delta;
|
||||
for( int i = 1; i <= MAX_RADIUS; i ++ ) {
|
||||
float x = kernelRadius * float(i) / float(MAX_RADIUS);
|
||||
float w = gaussianPdf(x, sigma);
|
||||
vec4 sample1 = texture2D( colorTexture, vUv + uvOffset);
|
||||
vec4 sample2 = texture2D( colorTexture, vUv - uvOffset);
|
||||
diffuseSum += ((sample1 + sample2) * w);
|
||||
weightSum += (2.0 * w);
|
||||
uvOffset += delta;
|
||||
}
|
||||
gl_FragColor = diffuseSum/weightSum;
|
||||
}`
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
getOverlayMaterial() {
|
||||
|
||||
return new ShaderMaterial( {
|
||||
|
||||
uniforms: {
|
||||
'maskTexture': { value: null },
|
||||
'edgeTexture1': { value: null },
|
||||
'edgeTexture2': { value: null },
|
||||
'patternTexture': { value: null },
|
||||
'edgeStrength': { value: 1.0 },
|
||||
'edgeGlow': { value: 1.0 },
|
||||
'usePatternTexture': { value: 0.0 }
|
||||
},
|
||||
|
||||
vertexShader:
|
||||
`varying vec2 vUv;
|
||||
|
||||
void main() {
|
||||
vUv = uv;
|
||||
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
|
||||
}`,
|
||||
|
||||
fragmentShader:
|
||||
`varying vec2 vUv;
|
||||
|
||||
uniform sampler2D maskTexture;
|
||||
uniform sampler2D edgeTexture1;
|
||||
uniform sampler2D edgeTexture2;
|
||||
uniform sampler2D patternTexture;
|
||||
uniform float edgeStrength;
|
||||
uniform float edgeGlow;
|
||||
uniform bool usePatternTexture;
|
||||
|
||||
void main() {
|
||||
vec4 edgeValue1 = texture2D(edgeTexture1, vUv);
|
||||
vec4 edgeValue2 = texture2D(edgeTexture2, vUv);
|
||||
vec4 maskColor = texture2D(maskTexture, vUv);
|
||||
vec4 patternColor = texture2D(patternTexture, 6.0 * vUv);
|
||||
float visibilityFactor = 1.0 - maskColor.g > 0.0 ? 1.0 : 0.5;
|
||||
vec4 edgeValue = edgeValue1 + edgeValue2 * edgeGlow;
|
||||
vec4 finalColor = edgeStrength * maskColor.r * edgeValue;
|
||||
if(usePatternTexture)
|
||||
finalColor += + visibilityFactor * (1.0 - maskColor.r) * (1.0 - patternColor.r);
|
||||
gl_FragColor = finalColor;
|
||||
}`,
|
||||
blending: AdditiveBlending,
|
||||
depthTest: false,
|
||||
depthWrite: false,
|
||||
transparent: true
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
OutlinePass.BlurDirectionX = new Vector2( 1.0, 0.0 );
|
||||
OutlinePass.BlurDirectionY = new Vector2( 0.0, 1.0 );
|
||||
|
||||
export { OutlinePass };
|
97
static/sdk/three/jsm/postprocessing/OutputPass.js
Normal file
97
static/sdk/three/jsm/postprocessing/OutputPass.js
Normal file
@ -0,0 +1,97 @@
|
||||
import {
|
||||
ColorManagement,
|
||||
RawShaderMaterial,
|
||||
UniformsUtils,
|
||||
LinearToneMapping,
|
||||
ReinhardToneMapping,
|
||||
CineonToneMapping,
|
||||
AgXToneMapping,
|
||||
ACESFilmicToneMapping,
|
||||
NeutralToneMapping,
|
||||
SRGBTransfer
|
||||
} from 'three';
|
||||
import { Pass, FullScreenQuad } from './Pass.js';
|
||||
import { OutputShader } from '../shaders/OutputShader.js';
|
||||
|
||||
class OutputPass extends Pass {
|
||||
|
||||
constructor() {
|
||||
|
||||
super();
|
||||
|
||||
//
|
||||
|
||||
const shader = OutputShader;
|
||||
|
||||
this.uniforms = UniformsUtils.clone( shader.uniforms );
|
||||
|
||||
this.material = new RawShaderMaterial( {
|
||||
name: shader.name,
|
||||
uniforms: this.uniforms,
|
||||
vertexShader: shader.vertexShader,
|
||||
fragmentShader: shader.fragmentShader
|
||||
} );
|
||||
|
||||
this.fsQuad = new FullScreenQuad( this.material );
|
||||
|
||||
// internal cache
|
||||
|
||||
this._outputColorSpace = null;
|
||||
this._toneMapping = null;
|
||||
|
||||
}
|
||||
|
||||
render( renderer, writeBuffer, readBuffer/*, deltaTime, maskActive */ ) {
|
||||
|
||||
this.uniforms[ 'tDiffuse' ].value = readBuffer.texture;
|
||||
this.uniforms[ 'toneMappingExposure' ].value = renderer.toneMappingExposure;
|
||||
|
||||
// rebuild defines if required
|
||||
|
||||
if ( this._outputColorSpace !== renderer.outputColorSpace || this._toneMapping !== renderer.toneMapping ) {
|
||||
|
||||
this._outputColorSpace = renderer.outputColorSpace;
|
||||
this._toneMapping = renderer.toneMapping;
|
||||
|
||||
this.material.defines = {};
|
||||
|
||||
if ( ColorManagement.getTransfer( this._outputColorSpace ) === SRGBTransfer ) this.material.defines.SRGB_TRANSFER = '';
|
||||
|
||||
if ( this._toneMapping === LinearToneMapping ) this.material.defines.LINEAR_TONE_MAPPING = '';
|
||||
else if ( this._toneMapping === ReinhardToneMapping ) this.material.defines.REINHARD_TONE_MAPPING = '';
|
||||
else if ( this._toneMapping === CineonToneMapping ) this.material.defines.CINEON_TONE_MAPPING = '';
|
||||
else if ( this._toneMapping === ACESFilmicToneMapping ) this.material.defines.ACES_FILMIC_TONE_MAPPING = '';
|
||||
else if ( this._toneMapping === AgXToneMapping ) this.material.defines.AGX_TONE_MAPPING = '';
|
||||
else if ( this._toneMapping === NeutralToneMapping ) this.material.defines.NEUTRAL_TONE_MAPPING = '';
|
||||
|
||||
this.material.needsUpdate = true;
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
if ( this.renderToScreen === true ) {
|
||||
|
||||
renderer.setRenderTarget( null );
|
||||
this.fsQuad.render( renderer );
|
||||
|
||||
} else {
|
||||
|
||||
renderer.setRenderTarget( writeBuffer );
|
||||
if ( this.clear ) renderer.clear( renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil );
|
||||
this.fsQuad.render( renderer );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
||||
this.material.dispose();
|
||||
this.fsQuad.dispose();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { OutputPass };
|
95
static/sdk/three/jsm/postprocessing/Pass.js
Normal file
95
static/sdk/three/jsm/postprocessing/Pass.js
Normal file
@ -0,0 +1,95 @@
|
||||
import {
|
||||
BufferGeometry,
|
||||
Float32BufferAttribute,
|
||||
OrthographicCamera,
|
||||
Mesh
|
||||
} from 'three';
|
||||
|
||||
class Pass {
|
||||
|
||||
constructor() {
|
||||
|
||||
this.isPass = true;
|
||||
|
||||
// if set to true, the pass is processed by the composer
|
||||
this.enabled = true;
|
||||
|
||||
// if set to true, the pass indicates to swap read and write buffer after rendering
|
||||
this.needsSwap = true;
|
||||
|
||||
// if set to true, the pass clears its buffer before rendering
|
||||
this.clear = false;
|
||||
|
||||
// if set to true, the result of the pass is rendered to screen. This is set automatically by EffectComposer.
|
||||
this.renderToScreen = false;
|
||||
|
||||
}
|
||||
|
||||
setSize( /* width, height */ ) {}
|
||||
|
||||
render( /* renderer, writeBuffer, readBuffer, deltaTime, maskActive */ ) {
|
||||
|
||||
console.error( 'THREE.Pass: .render() must be implemented in derived pass.' );
|
||||
|
||||
}
|
||||
|
||||
dispose() {}
|
||||
|
||||
}
|
||||
|
||||
// Helper for passes that need to fill the viewport with a single quad.
|
||||
|
||||
const _camera = new OrthographicCamera( - 1, 1, 1, - 1, 0, 1 );
|
||||
|
||||
// https://github.com/mrdoob/three.js/pull/21358
|
||||
|
||||
class FullscreenTriangleGeometry extends BufferGeometry {
|
||||
|
||||
constructor() {
|
||||
|
||||
super();
|
||||
|
||||
this.setAttribute( 'position', new Float32BufferAttribute( [ - 1, 3, 0, - 1, - 1, 0, 3, - 1, 0 ], 3 ) );
|
||||
this.setAttribute( 'uv', new Float32BufferAttribute( [ 0, 2, 0, 0, 2, 0 ], 2 ) );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const _geometry = new FullscreenTriangleGeometry();
|
||||
|
||||
class FullScreenQuad {
|
||||
|
||||
constructor( material ) {
|
||||
|
||||
this._mesh = new Mesh( _geometry, material );
|
||||
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
||||
this._mesh.geometry.dispose();
|
||||
|
||||
}
|
||||
|
||||
render( renderer ) {
|
||||
|
||||
renderer.render( this._mesh, _camera );
|
||||
|
||||
}
|
||||
|
||||
get material() {
|
||||
|
||||
return this._mesh.material;
|
||||
|
||||
}
|
||||
|
||||
set material( value ) {
|
||||
|
||||
this._mesh.material = value;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { Pass, FullScreenQuad };
|
99
static/sdk/three/jsm/postprocessing/RenderPass.js
Normal file
99
static/sdk/three/jsm/postprocessing/RenderPass.js
Normal file
@ -0,0 +1,99 @@
|
||||
import {
|
||||
Color
|
||||
} from 'three';
|
||||
import { Pass } from './Pass.js';
|
||||
|
||||
class RenderPass extends Pass {
|
||||
|
||||
constructor( scene, camera, overrideMaterial = null, clearColor = null, clearAlpha = null ) {
|
||||
|
||||
super();
|
||||
|
||||
this.scene = scene;
|
||||
this.camera = camera;
|
||||
|
||||
this.overrideMaterial = overrideMaterial;
|
||||
|
||||
this.clearColor = clearColor;
|
||||
this.clearAlpha = clearAlpha;
|
||||
|
||||
this.clear = true;
|
||||
this.clearDepth = false;
|
||||
this.needsSwap = false;
|
||||
this._oldClearColor = new Color();
|
||||
|
||||
}
|
||||
|
||||
render( renderer, writeBuffer, readBuffer /*, deltaTime, maskActive */ ) {
|
||||
|
||||
const oldAutoClear = renderer.autoClear;
|
||||
renderer.autoClear = false;
|
||||
|
||||
let oldClearAlpha, oldOverrideMaterial;
|
||||
|
||||
if ( this.overrideMaterial !== null ) {
|
||||
|
||||
oldOverrideMaterial = this.scene.overrideMaterial;
|
||||
|
||||
this.scene.overrideMaterial = this.overrideMaterial;
|
||||
|
||||
}
|
||||
|
||||
if ( this.clearColor !== null ) {
|
||||
|
||||
renderer.getClearColor( this._oldClearColor );
|
||||
renderer.setClearColor( this.clearColor );
|
||||
|
||||
}
|
||||
|
||||
if ( this.clearAlpha !== null ) {
|
||||
|
||||
oldClearAlpha = renderer.getClearAlpha();
|
||||
renderer.setClearAlpha( this.clearAlpha );
|
||||
|
||||
}
|
||||
|
||||
if ( this.clearDepth == true ) {
|
||||
|
||||
renderer.clearDepth();
|
||||
|
||||
}
|
||||
|
||||
renderer.setRenderTarget( this.renderToScreen ? null : readBuffer );
|
||||
|
||||
if ( this.clear === true ) {
|
||||
|
||||
// TODO: Avoid using autoClear properties, see https://github.com/mrdoob/three.js/pull/15571#issuecomment-465669600
|
||||
renderer.clear( renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil );
|
||||
|
||||
}
|
||||
|
||||
renderer.render( this.scene, this.camera );
|
||||
|
||||
// restore
|
||||
|
||||
if ( this.clearColor !== null ) {
|
||||
|
||||
renderer.setClearColor( this._oldClearColor );
|
||||
|
||||
}
|
||||
|
||||
if ( this.clearAlpha !== null ) {
|
||||
|
||||
renderer.setClearAlpha( oldClearAlpha );
|
||||
|
||||
}
|
||||
|
||||
if ( this.overrideMaterial !== null ) {
|
||||
|
||||
this.scene.overrideMaterial = oldOverrideMaterial;
|
||||
|
||||
}
|
||||
|
||||
renderer.autoClear = oldAutoClear;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { RenderPass };
|
235
static/sdk/three/jsm/postprocessing/RenderPixelatedPass.js
Normal file
235
static/sdk/three/jsm/postprocessing/RenderPixelatedPass.js
Normal file
@ -0,0 +1,235 @@
|
||||
import {
|
||||
WebGLRenderTarget,
|
||||
MeshNormalMaterial,
|
||||
ShaderMaterial,
|
||||
Vector2,
|
||||
Vector4,
|
||||
DepthTexture,
|
||||
NearestFilter,
|
||||
HalfFloatType
|
||||
} from 'three';
|
||||
import { Pass, FullScreenQuad } from './Pass.js';
|
||||
|
||||
class RenderPixelatedPass extends Pass {
|
||||
|
||||
constructor( pixelSize, scene, camera, options = {} ) {
|
||||
|
||||
super();
|
||||
|
||||
this.pixelSize = pixelSize;
|
||||
this.resolution = new Vector2();
|
||||
this.renderResolution = new Vector2();
|
||||
|
||||
this.pixelatedMaterial = this.createPixelatedMaterial();
|
||||
this.normalMaterial = new MeshNormalMaterial();
|
||||
|
||||
this.fsQuad = new FullScreenQuad( this.pixelatedMaterial );
|
||||
this.scene = scene;
|
||||
this.camera = camera;
|
||||
|
||||
this.normalEdgeStrength = options.normalEdgeStrength || 0.3;
|
||||
this.depthEdgeStrength = options.depthEdgeStrength || 0.4;
|
||||
|
||||
this.beautyRenderTarget = new WebGLRenderTarget();
|
||||
this.beautyRenderTarget.texture.minFilter = NearestFilter;
|
||||
this.beautyRenderTarget.texture.magFilter = NearestFilter;
|
||||
this.beautyRenderTarget.texture.type = HalfFloatType;
|
||||
this.beautyRenderTarget.depthTexture = new DepthTexture();
|
||||
|
||||
this.normalRenderTarget = new WebGLRenderTarget();
|
||||
this.normalRenderTarget.texture.minFilter = NearestFilter;
|
||||
this.normalRenderTarget.texture.magFilter = NearestFilter;
|
||||
this.normalRenderTarget.texture.type = HalfFloatType;
|
||||
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
||||
this.beautyRenderTarget.dispose();
|
||||
this.normalRenderTarget.dispose();
|
||||
|
||||
this.pixelatedMaterial.dispose();
|
||||
this.normalMaterial.dispose();
|
||||
|
||||
this.fsQuad.dispose();
|
||||
|
||||
}
|
||||
|
||||
setSize( width, height ) {
|
||||
|
||||
this.resolution.set( width, height );
|
||||
this.renderResolution.set( ( width / this.pixelSize ) | 0, ( height / this.pixelSize ) | 0 );
|
||||
const { x, y } = this.renderResolution;
|
||||
this.beautyRenderTarget.setSize( x, y );
|
||||
this.normalRenderTarget.setSize( x, y );
|
||||
this.fsQuad.material.uniforms.resolution.value.set( x, y, 1 / x, 1 / y );
|
||||
|
||||
}
|
||||
|
||||
setPixelSize( pixelSize ) {
|
||||
|
||||
this.pixelSize = pixelSize;
|
||||
this.setSize( this.resolution.x, this.resolution.y );
|
||||
|
||||
}
|
||||
|
||||
render( renderer, writeBuffer ) {
|
||||
|
||||
const uniforms = this.fsQuad.material.uniforms;
|
||||
uniforms.normalEdgeStrength.value = this.normalEdgeStrength;
|
||||
uniforms.depthEdgeStrength.value = this.depthEdgeStrength;
|
||||
|
||||
renderer.setRenderTarget( this.beautyRenderTarget );
|
||||
renderer.render( this.scene, this.camera );
|
||||
|
||||
const overrideMaterial_old = this.scene.overrideMaterial;
|
||||
renderer.setRenderTarget( this.normalRenderTarget );
|
||||
this.scene.overrideMaterial = this.normalMaterial;
|
||||
renderer.render( this.scene, this.camera );
|
||||
this.scene.overrideMaterial = overrideMaterial_old;
|
||||
|
||||
uniforms.tDiffuse.value = this.beautyRenderTarget.texture;
|
||||
uniforms.tDepth.value = this.beautyRenderTarget.depthTexture;
|
||||
uniforms.tNormal.value = this.normalRenderTarget.texture;
|
||||
|
||||
if ( this.renderToScreen ) {
|
||||
|
||||
renderer.setRenderTarget( null );
|
||||
|
||||
} else {
|
||||
|
||||
renderer.setRenderTarget( writeBuffer );
|
||||
|
||||
if ( this.clear ) renderer.clear();
|
||||
|
||||
}
|
||||
|
||||
this.fsQuad.render( renderer );
|
||||
|
||||
}
|
||||
|
||||
createPixelatedMaterial() {
|
||||
|
||||
return new ShaderMaterial( {
|
||||
uniforms: {
|
||||
tDiffuse: { value: null },
|
||||
tDepth: { value: null },
|
||||
tNormal: { value: null },
|
||||
resolution: {
|
||||
value: new Vector4(
|
||||
this.renderResolution.x,
|
||||
this.renderResolution.y,
|
||||
1 / this.renderResolution.x,
|
||||
1 / this.renderResolution.y,
|
||||
)
|
||||
},
|
||||
normalEdgeStrength: { value: 0 },
|
||||
depthEdgeStrength: { value: 0 }
|
||||
},
|
||||
vertexShader: /* glsl */`
|
||||
varying vec2 vUv;
|
||||
|
||||
void main() {
|
||||
|
||||
vUv = uv;
|
||||
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
|
||||
|
||||
}
|
||||
`,
|
||||
fragmentShader: /* glsl */`
|
||||
uniform sampler2D tDiffuse;
|
||||
uniform sampler2D tDepth;
|
||||
uniform sampler2D tNormal;
|
||||
uniform vec4 resolution;
|
||||
uniform float normalEdgeStrength;
|
||||
uniform float depthEdgeStrength;
|
||||
varying vec2 vUv;
|
||||
|
||||
float getDepth(int x, int y) {
|
||||
|
||||
return texture2D( tDepth, vUv + vec2(x, y) * resolution.zw ).r;
|
||||
|
||||
}
|
||||
|
||||
vec3 getNormal(int x, int y) {
|
||||
|
||||
return texture2D( tNormal, vUv + vec2(x, y) * resolution.zw ).rgb * 2.0 - 1.0;
|
||||
|
||||
}
|
||||
|
||||
float depthEdgeIndicator(float depth, vec3 normal) {
|
||||
|
||||
float diff = 0.0;
|
||||
diff += clamp(getDepth(1, 0) - depth, 0.0, 1.0);
|
||||
diff += clamp(getDepth(-1, 0) - depth, 0.0, 1.0);
|
||||
diff += clamp(getDepth(0, 1) - depth, 0.0, 1.0);
|
||||
diff += clamp(getDepth(0, -1) - depth, 0.0, 1.0);
|
||||
return floor(smoothstep(0.01, 0.02, diff) * 2.) / 2.;
|
||||
|
||||
}
|
||||
|
||||
float neighborNormalEdgeIndicator(int x, int y, float depth, vec3 normal) {
|
||||
|
||||
float depthDiff = getDepth(x, y) - depth;
|
||||
vec3 neighborNormal = getNormal(x, y);
|
||||
|
||||
// Edge pixels should yield to faces who's normals are closer to the bias normal.
|
||||
vec3 normalEdgeBias = vec3(1., 1., 1.); // This should probably be a parameter.
|
||||
float normalDiff = dot(normal - neighborNormal, normalEdgeBias);
|
||||
float normalIndicator = clamp(smoothstep(-.01, .01, normalDiff), 0.0, 1.0);
|
||||
|
||||
// Only the shallower pixel should detect the normal edge.
|
||||
float depthIndicator = clamp(sign(depthDiff * .25 + .0025), 0.0, 1.0);
|
||||
|
||||
return (1.0 - dot(normal, neighborNormal)) * depthIndicator * normalIndicator;
|
||||
|
||||
}
|
||||
|
||||
float normalEdgeIndicator(float depth, vec3 normal) {
|
||||
|
||||
float indicator = 0.0;
|
||||
|
||||
indicator += neighborNormalEdgeIndicator(0, -1, depth, normal);
|
||||
indicator += neighborNormalEdgeIndicator(0, 1, depth, normal);
|
||||
indicator += neighborNormalEdgeIndicator(-1, 0, depth, normal);
|
||||
indicator += neighborNormalEdgeIndicator(1, 0, depth, normal);
|
||||
|
||||
return step(0.1, indicator);
|
||||
|
||||
}
|
||||
|
||||
void main() {
|
||||
|
||||
vec4 texel = texture2D( tDiffuse, vUv );
|
||||
|
||||
float depth = 0.0;
|
||||
vec3 normal = vec3(0.0);
|
||||
|
||||
if (depthEdgeStrength > 0.0 || normalEdgeStrength > 0.0) {
|
||||
|
||||
depth = getDepth(0, 0);
|
||||
normal = getNormal(0, 0);
|
||||
|
||||
}
|
||||
|
||||
float dei = 0.0;
|
||||
if (depthEdgeStrength > 0.0)
|
||||
dei = depthEdgeIndicator(depth, normal);
|
||||
|
||||
float nei = 0.0;
|
||||
if (normalEdgeStrength > 0.0)
|
||||
nei = normalEdgeIndicator(depth, normal);
|
||||
|
||||
float Strength = dei > 0.0 ? (1.0 - depthEdgeStrength * dei) : (1.0 + normalEdgeStrength * nei);
|
||||
|
||||
gl_FragColor = texel * Strength;
|
||||
|
||||
}
|
||||
`
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { RenderPixelatedPass };
|
193
static/sdk/three/jsm/postprocessing/RenderTransitionPass.js
Normal file
193
static/sdk/three/jsm/postprocessing/RenderTransitionPass.js
Normal file
@ -0,0 +1,193 @@
|
||||
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 <tonemapping_fragment>
|
||||
#include <colorspace_fragment>
|
||||
|
||||
}
|
||||
`
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { RenderTransitionPass };
|
334
static/sdk/three/jsm/postprocessing/SAOPass.js
Normal file
334
static/sdk/three/jsm/postprocessing/SAOPass.js
Normal file
@ -0,0 +1,334 @@
|
||||
import {
|
||||
AddEquation,
|
||||
Color,
|
||||
CustomBlending,
|
||||
DepthTexture,
|
||||
DstAlphaFactor,
|
||||
DstColorFactor,
|
||||
HalfFloatType,
|
||||
MeshNormalMaterial,
|
||||
NearestFilter,
|
||||
NoBlending,
|
||||
ShaderMaterial,
|
||||
UniformsUtils,
|
||||
DepthStencilFormat,
|
||||
UnsignedInt248Type,
|
||||
Vector2,
|
||||
WebGLRenderTarget,
|
||||
ZeroFactor
|
||||
} from 'three';
|
||||
import { Pass, FullScreenQuad } from './Pass.js';
|
||||
import { SAOShader } from '../shaders/SAOShader.js';
|
||||
import { DepthLimitedBlurShader } from '../shaders/DepthLimitedBlurShader.js';
|
||||
import { BlurShaderUtils } from '../shaders/DepthLimitedBlurShader.js';
|
||||
import { CopyShader } from '../shaders/CopyShader.js';
|
||||
|
||||
/**
|
||||
* SAO implementation inspired from bhouston previous SAO work
|
||||
*/
|
||||
|
||||
class SAOPass extends Pass {
|
||||
|
||||
constructor( scene, camera, resolution = new Vector2( 256, 256 ) ) {
|
||||
|
||||
super();
|
||||
|
||||
this.scene = scene;
|
||||
this.camera = camera;
|
||||
|
||||
this.clear = true;
|
||||
this.needsSwap = false;
|
||||
|
||||
this.originalClearColor = new Color();
|
||||
this._oldClearColor = new Color();
|
||||
this.oldClearAlpha = 1;
|
||||
|
||||
this.params = {
|
||||
output: 0,
|
||||
saoBias: 0.5,
|
||||
saoIntensity: 0.18,
|
||||
saoScale: 1,
|
||||
saoKernelRadius: 100,
|
||||
saoMinResolution: 0,
|
||||
saoBlur: true,
|
||||
saoBlurRadius: 8,
|
||||
saoBlurStdDev: 4,
|
||||
saoBlurDepthCutoff: 0.01
|
||||
};
|
||||
|
||||
this.resolution = new Vector2( resolution.x, resolution.y );
|
||||
|
||||
this.saoRenderTarget = new WebGLRenderTarget( this.resolution.x, this.resolution.y, { type: HalfFloatType } );
|
||||
this.blurIntermediateRenderTarget = this.saoRenderTarget.clone();
|
||||
|
||||
const depthTexture = new DepthTexture();
|
||||
depthTexture.format = DepthStencilFormat;
|
||||
depthTexture.type = UnsignedInt248Type;
|
||||
|
||||
this.normalRenderTarget = new WebGLRenderTarget( this.resolution.x, this.resolution.y, {
|
||||
minFilter: NearestFilter,
|
||||
magFilter: NearestFilter,
|
||||
type: HalfFloatType,
|
||||
depthTexture: depthTexture
|
||||
} );
|
||||
|
||||
this.normalMaterial = new MeshNormalMaterial();
|
||||
this.normalMaterial.blending = NoBlending;
|
||||
|
||||
this.saoMaterial = new ShaderMaterial( {
|
||||
defines: Object.assign( {}, SAOShader.defines ),
|
||||
fragmentShader: SAOShader.fragmentShader,
|
||||
vertexShader: SAOShader.vertexShader,
|
||||
uniforms: UniformsUtils.clone( SAOShader.uniforms )
|
||||
} );
|
||||
this.saoMaterial.defines[ 'PERSPECTIVE_CAMERA' ] = this.camera.isPerspectiveCamera ? 1 : 0;
|
||||
this.saoMaterial.uniforms[ 'tDepth' ].value = depthTexture;
|
||||
this.saoMaterial.uniforms[ 'tNormal' ].value = this.normalRenderTarget.texture;
|
||||
this.saoMaterial.uniforms[ 'size' ].value.set( this.resolution.x, this.resolution.y );
|
||||
this.saoMaterial.uniforms[ 'cameraInverseProjectionMatrix' ].value.copy( this.camera.projectionMatrixInverse );
|
||||
this.saoMaterial.uniforms[ 'cameraProjectionMatrix' ].value = this.camera.projectionMatrix;
|
||||
this.saoMaterial.blending = NoBlending;
|
||||
|
||||
this.vBlurMaterial = new ShaderMaterial( {
|
||||
uniforms: UniformsUtils.clone( DepthLimitedBlurShader.uniforms ),
|
||||
defines: Object.assign( {}, DepthLimitedBlurShader.defines ),
|
||||
vertexShader: DepthLimitedBlurShader.vertexShader,
|
||||
fragmentShader: DepthLimitedBlurShader.fragmentShader
|
||||
} );
|
||||
this.vBlurMaterial.defines[ 'DEPTH_PACKING' ] = 0;
|
||||
this.vBlurMaterial.defines[ 'PERSPECTIVE_CAMERA' ] = this.camera.isPerspectiveCamera ? 1 : 0;
|
||||
this.vBlurMaterial.uniforms[ 'tDiffuse' ].value = this.saoRenderTarget.texture;
|
||||
this.vBlurMaterial.uniforms[ 'tDepth' ].value = depthTexture;
|
||||
this.vBlurMaterial.uniforms[ 'size' ].value.set( this.resolution.x, this.resolution.y );
|
||||
this.vBlurMaterial.blending = NoBlending;
|
||||
|
||||
this.hBlurMaterial = new ShaderMaterial( {
|
||||
uniforms: UniformsUtils.clone( DepthLimitedBlurShader.uniforms ),
|
||||
defines: Object.assign( {}, DepthLimitedBlurShader.defines ),
|
||||
vertexShader: DepthLimitedBlurShader.vertexShader,
|
||||
fragmentShader: DepthLimitedBlurShader.fragmentShader
|
||||
} );
|
||||
this.hBlurMaterial.defines[ 'DEPTH_PACKING' ] = 0;
|
||||
this.hBlurMaterial.defines[ 'PERSPECTIVE_CAMERA' ] = this.camera.isPerspectiveCamera ? 1 : 0;
|
||||
this.hBlurMaterial.uniforms[ 'tDiffuse' ].value = this.blurIntermediateRenderTarget.texture;
|
||||
this.hBlurMaterial.uniforms[ 'tDepth' ].value = depthTexture;
|
||||
this.hBlurMaterial.uniforms[ 'size' ].value.set( this.resolution.x, this.resolution.y );
|
||||
this.hBlurMaterial.blending = NoBlending;
|
||||
|
||||
this.materialCopy = new ShaderMaterial( {
|
||||
uniforms: UniformsUtils.clone( CopyShader.uniforms ),
|
||||
vertexShader: CopyShader.vertexShader,
|
||||
fragmentShader: CopyShader.fragmentShader,
|
||||
blending: NoBlending
|
||||
} );
|
||||
this.materialCopy.transparent = true;
|
||||
this.materialCopy.depthTest = false;
|
||||
this.materialCopy.depthWrite = false;
|
||||
this.materialCopy.blending = CustomBlending;
|
||||
this.materialCopy.blendSrc = DstColorFactor;
|
||||
this.materialCopy.blendDst = ZeroFactor;
|
||||
this.materialCopy.blendEquation = AddEquation;
|
||||
this.materialCopy.blendSrcAlpha = DstAlphaFactor;
|
||||
this.materialCopy.blendDstAlpha = ZeroFactor;
|
||||
this.materialCopy.blendEquationAlpha = AddEquation;
|
||||
|
||||
this.fsQuad = new FullScreenQuad( null );
|
||||
|
||||
}
|
||||
|
||||
render( renderer, writeBuffer, readBuffer/*, deltaTime, maskActive*/ ) {
|
||||
|
||||
// Rendering readBuffer first when rendering to screen
|
||||
if ( this.renderToScreen ) {
|
||||
|
||||
this.materialCopy.blending = NoBlending;
|
||||
this.materialCopy.uniforms[ 'tDiffuse' ].value = readBuffer.texture;
|
||||
this.materialCopy.needsUpdate = true;
|
||||
this.renderPass( renderer, this.materialCopy, null );
|
||||
|
||||
}
|
||||
|
||||
renderer.getClearColor( this._oldClearColor );
|
||||
this.oldClearAlpha = renderer.getClearAlpha();
|
||||
const oldAutoClear = renderer.autoClear;
|
||||
renderer.autoClear = false;
|
||||
|
||||
this.saoMaterial.uniforms[ 'bias' ].value = this.params.saoBias;
|
||||
this.saoMaterial.uniforms[ 'intensity' ].value = this.params.saoIntensity;
|
||||
this.saoMaterial.uniforms[ 'scale' ].value = this.params.saoScale;
|
||||
this.saoMaterial.uniforms[ 'kernelRadius' ].value = this.params.saoKernelRadius;
|
||||
this.saoMaterial.uniforms[ 'minResolution' ].value = this.params.saoMinResolution;
|
||||
this.saoMaterial.uniforms[ 'cameraNear' ].value = this.camera.near;
|
||||
this.saoMaterial.uniforms[ 'cameraFar' ].value = this.camera.far;
|
||||
// this.saoMaterial.uniforms['randomSeed'].value = Math.random();
|
||||
|
||||
const depthCutoff = this.params.saoBlurDepthCutoff * ( this.camera.far - this.camera.near );
|
||||
this.vBlurMaterial.uniforms[ 'depthCutoff' ].value = depthCutoff;
|
||||
this.hBlurMaterial.uniforms[ 'depthCutoff' ].value = depthCutoff;
|
||||
|
||||
this.vBlurMaterial.uniforms[ 'cameraNear' ].value = this.camera.near;
|
||||
this.vBlurMaterial.uniforms[ 'cameraFar' ].value = this.camera.far;
|
||||
this.hBlurMaterial.uniforms[ 'cameraNear' ].value = this.camera.near;
|
||||
this.hBlurMaterial.uniforms[ 'cameraFar' ].value = this.camera.far;
|
||||
|
||||
this.params.saoBlurRadius = Math.floor( this.params.saoBlurRadius );
|
||||
if ( ( this.prevStdDev !== this.params.saoBlurStdDev ) || ( this.prevNumSamples !== this.params.saoBlurRadius ) ) {
|
||||
|
||||
BlurShaderUtils.configure( this.vBlurMaterial, this.params.saoBlurRadius, this.params.saoBlurStdDev, new Vector2( 0, 1 ) );
|
||||
BlurShaderUtils.configure( this.hBlurMaterial, this.params.saoBlurRadius, this.params.saoBlurStdDev, new Vector2( 1, 0 ) );
|
||||
this.prevStdDev = this.params.saoBlurStdDev;
|
||||
this.prevNumSamples = this.params.saoBlurRadius;
|
||||
|
||||
}
|
||||
|
||||
// render normal and depth
|
||||
this.renderOverride( renderer, this.normalMaterial, this.normalRenderTarget, 0x7777ff, 1.0 );
|
||||
|
||||
// Rendering SAO texture
|
||||
this.renderPass( renderer, this.saoMaterial, this.saoRenderTarget, 0xffffff, 1.0 );
|
||||
|
||||
// Blurring SAO texture
|
||||
if ( this.params.saoBlur ) {
|
||||
|
||||
this.renderPass( renderer, this.vBlurMaterial, this.blurIntermediateRenderTarget, 0xffffff, 1.0 );
|
||||
this.renderPass( renderer, this.hBlurMaterial, this.saoRenderTarget, 0xffffff, 1.0 );
|
||||
|
||||
}
|
||||
|
||||
const outputMaterial = this.materialCopy;
|
||||
|
||||
// Setting up SAO rendering
|
||||
if ( this.params.output === SAOPass.OUTPUT.Normal ) {
|
||||
|
||||
this.materialCopy.uniforms[ 'tDiffuse' ].value = this.normalRenderTarget.texture;
|
||||
this.materialCopy.needsUpdate = true;
|
||||
|
||||
} else {
|
||||
|
||||
this.materialCopy.uniforms[ 'tDiffuse' ].value = this.saoRenderTarget.texture;
|
||||
this.materialCopy.needsUpdate = true;
|
||||
|
||||
}
|
||||
|
||||
// Blending depends on output
|
||||
if ( this.params.output === SAOPass.OUTPUT.Default ) {
|
||||
|
||||
outputMaterial.blending = CustomBlending;
|
||||
|
||||
} else {
|
||||
|
||||
outputMaterial.blending = NoBlending;
|
||||
|
||||
}
|
||||
|
||||
// Rendering SAOPass result on top of previous pass
|
||||
this.renderPass( renderer, outputMaterial, this.renderToScreen ? null : readBuffer );
|
||||
|
||||
renderer.setClearColor( this._oldClearColor, this.oldClearAlpha );
|
||||
renderer.autoClear = oldAutoClear;
|
||||
|
||||
}
|
||||
|
||||
renderPass( renderer, passMaterial, renderTarget, clearColor, clearAlpha ) {
|
||||
|
||||
// save original state
|
||||
renderer.getClearColor( this.originalClearColor );
|
||||
const originalClearAlpha = renderer.getClearAlpha();
|
||||
const originalAutoClear = renderer.autoClear;
|
||||
|
||||
renderer.setRenderTarget( renderTarget );
|
||||
|
||||
// setup pass state
|
||||
renderer.autoClear = false;
|
||||
if ( ( clearColor !== undefined ) && ( clearColor !== null ) ) {
|
||||
|
||||
renderer.setClearColor( clearColor );
|
||||
renderer.setClearAlpha( clearAlpha || 0.0 );
|
||||
renderer.clear();
|
||||
|
||||
}
|
||||
|
||||
this.fsQuad.material = passMaterial;
|
||||
this.fsQuad.render( renderer );
|
||||
|
||||
// restore original state
|
||||
renderer.autoClear = originalAutoClear;
|
||||
renderer.setClearColor( this.originalClearColor );
|
||||
renderer.setClearAlpha( originalClearAlpha );
|
||||
|
||||
}
|
||||
|
||||
renderOverride( renderer, overrideMaterial, renderTarget, clearColor, clearAlpha ) {
|
||||
|
||||
renderer.getClearColor( this.originalClearColor );
|
||||
const originalClearAlpha = renderer.getClearAlpha();
|
||||
const originalAutoClear = renderer.autoClear;
|
||||
|
||||
renderer.setRenderTarget( renderTarget );
|
||||
renderer.autoClear = false;
|
||||
|
||||
clearColor = overrideMaterial.clearColor || clearColor;
|
||||
clearAlpha = overrideMaterial.clearAlpha || clearAlpha;
|
||||
if ( ( clearColor !== undefined ) && ( clearColor !== null ) ) {
|
||||
|
||||
renderer.setClearColor( clearColor );
|
||||
renderer.setClearAlpha( clearAlpha || 0.0 );
|
||||
renderer.clear();
|
||||
|
||||
}
|
||||
|
||||
this.scene.overrideMaterial = overrideMaterial;
|
||||
renderer.render( this.scene, this.camera );
|
||||
this.scene.overrideMaterial = null;
|
||||
|
||||
// restore original state
|
||||
renderer.autoClear = originalAutoClear;
|
||||
renderer.setClearColor( this.originalClearColor );
|
||||
renderer.setClearAlpha( originalClearAlpha );
|
||||
|
||||
}
|
||||
|
||||
setSize( width, height ) {
|
||||
|
||||
this.saoRenderTarget.setSize( width, height );
|
||||
this.blurIntermediateRenderTarget.setSize( width, height );
|
||||
this.normalRenderTarget.setSize( width, height );
|
||||
|
||||
this.saoMaterial.uniforms[ 'size' ].value.set( width, height );
|
||||
this.saoMaterial.uniforms[ 'cameraInverseProjectionMatrix' ].value.copy( this.camera.projectionMatrixInverse );
|
||||
this.saoMaterial.uniforms[ 'cameraProjectionMatrix' ].value = this.camera.projectionMatrix;
|
||||
this.saoMaterial.needsUpdate = true;
|
||||
|
||||
this.vBlurMaterial.uniforms[ 'size' ].value.set( width, height );
|
||||
this.vBlurMaterial.needsUpdate = true;
|
||||
|
||||
this.hBlurMaterial.uniforms[ 'size' ].value.set( width, height );
|
||||
this.hBlurMaterial.needsUpdate = true;
|
||||
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
||||
this.saoRenderTarget.dispose();
|
||||
this.blurIntermediateRenderTarget.dispose();
|
||||
this.normalRenderTarget.dispose();
|
||||
|
||||
this.normalMaterial.dispose();
|
||||
this.saoMaterial.dispose();
|
||||
this.vBlurMaterial.dispose();
|
||||
this.hBlurMaterial.dispose();
|
||||
this.materialCopy.dispose();
|
||||
|
||||
this.fsQuad.dispose();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
SAOPass.OUTPUT = {
|
||||
'Default': 0,
|
||||
'SAO': 1,
|
||||
'Normal': 2
|
||||
};
|
||||
|
||||
export { SAOPass };
|
199
static/sdk/three/jsm/postprocessing/SMAAPass.js
Normal file
199
static/sdk/three/jsm/postprocessing/SMAAPass.js
Normal file
File diff suppressed because one or more lines are too long
228
static/sdk/three/jsm/postprocessing/SSAARenderPass.js
Normal file
228
static/sdk/three/jsm/postprocessing/SSAARenderPass.js
Normal file
@ -0,0 +1,228 @@
|
||||
import {
|
||||
AdditiveBlending,
|
||||
Color,
|
||||
HalfFloatType,
|
||||
ShaderMaterial,
|
||||
UniformsUtils,
|
||||
WebGLRenderTarget
|
||||
} from 'three';
|
||||
import { Pass, FullScreenQuad } from './Pass.js';
|
||||
import { CopyShader } from '../shaders/CopyShader.js';
|
||||
|
||||
/**
|
||||
*
|
||||
* Supersample Anti-Aliasing Render Pass
|
||||
*
|
||||
* This manual approach to SSAA re-renders the scene ones for each sample with camera jitter and accumulates the results.
|
||||
*
|
||||
* References: https://en.wikipedia.org/wiki/Supersampling
|
||||
*
|
||||
*/
|
||||
|
||||
class SSAARenderPass extends Pass {
|
||||
|
||||
constructor( scene, camera, clearColor, clearAlpha ) {
|
||||
|
||||
super();
|
||||
|
||||
this.scene = scene;
|
||||
this.camera = camera;
|
||||
|
||||
this.sampleLevel = 4; // specified as n, where the number of samples is 2^n, so sampleLevel = 4, is 2^4 samples, 16.
|
||||
this.unbiased = true;
|
||||
|
||||
// as we need to clear the buffer in this pass, clearColor must be set to something, defaults to black.
|
||||
this.clearColor = ( clearColor !== undefined ) ? clearColor : 0x000000;
|
||||
this.clearAlpha = ( clearAlpha !== undefined ) ? clearAlpha : 0;
|
||||
this._oldClearColor = new Color();
|
||||
|
||||
const copyShader = CopyShader;
|
||||
this.copyUniforms = UniformsUtils.clone( copyShader.uniforms );
|
||||
|
||||
this.copyMaterial = new ShaderMaterial( {
|
||||
uniforms: this.copyUniforms,
|
||||
vertexShader: copyShader.vertexShader,
|
||||
fragmentShader: copyShader.fragmentShader,
|
||||
transparent: true,
|
||||
depthTest: false,
|
||||
depthWrite: false,
|
||||
premultipliedAlpha: true,
|
||||
blending: AdditiveBlending
|
||||
} );
|
||||
|
||||
this.fsQuad = new FullScreenQuad( this.copyMaterial );
|
||||
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
||||
if ( this.sampleRenderTarget ) {
|
||||
|
||||
this.sampleRenderTarget.dispose();
|
||||
this.sampleRenderTarget = null;
|
||||
|
||||
}
|
||||
|
||||
this.copyMaterial.dispose();
|
||||
|
||||
this.fsQuad.dispose();
|
||||
|
||||
}
|
||||
|
||||
setSize( width, height ) {
|
||||
|
||||
if ( this.sampleRenderTarget ) this.sampleRenderTarget.setSize( width, height );
|
||||
|
||||
}
|
||||
|
||||
render( renderer, writeBuffer, readBuffer ) {
|
||||
|
||||
if ( ! this.sampleRenderTarget ) {
|
||||
|
||||
this.sampleRenderTarget = new WebGLRenderTarget( readBuffer.width, readBuffer.height, { type: HalfFloatType } );
|
||||
this.sampleRenderTarget.texture.name = 'SSAARenderPass.sample';
|
||||
|
||||
}
|
||||
|
||||
const jitterOffsets = _JitterVectors[ Math.max( 0, Math.min( this.sampleLevel, 5 ) ) ];
|
||||
|
||||
const autoClear = renderer.autoClear;
|
||||
renderer.autoClear = false;
|
||||
|
||||
renderer.getClearColor( this._oldClearColor );
|
||||
const oldClearAlpha = renderer.getClearAlpha();
|
||||
|
||||
const baseSampleWeight = 1.0 / jitterOffsets.length;
|
||||
const roundingRange = 1 / 32;
|
||||
this.copyUniforms[ 'tDiffuse' ].value = this.sampleRenderTarget.texture;
|
||||
|
||||
const viewOffset = {
|
||||
|
||||
fullWidth: readBuffer.width,
|
||||
fullHeight: readBuffer.height,
|
||||
offsetX: 0,
|
||||
offsetY: 0,
|
||||
width: readBuffer.width,
|
||||
height: readBuffer.height
|
||||
|
||||
};
|
||||
|
||||
const originalViewOffset = Object.assign( {}, this.camera.view );
|
||||
|
||||
if ( originalViewOffset.enabled ) Object.assign( viewOffset, originalViewOffset );
|
||||
|
||||
// render the scene multiple times, each slightly jitter offset from the last and accumulate the results.
|
||||
for ( let i = 0; i < jitterOffsets.length; i ++ ) {
|
||||
|
||||
const jitterOffset = jitterOffsets[ i ];
|
||||
|
||||
if ( this.camera.setViewOffset ) {
|
||||
|
||||
this.camera.setViewOffset(
|
||||
|
||||
viewOffset.fullWidth, viewOffset.fullHeight,
|
||||
|
||||
viewOffset.offsetX + jitterOffset[ 0 ] * 0.0625, viewOffset.offsetY + jitterOffset[ 1 ] * 0.0625, // 0.0625 = 1 / 16
|
||||
|
||||
viewOffset.width, viewOffset.height
|
||||
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
let sampleWeight = baseSampleWeight;
|
||||
|
||||
if ( this.unbiased ) {
|
||||
|
||||
// the theory is that equal weights for each sample lead to an accumulation of rounding errors.
|
||||
// The following equation varies the sampleWeight per sample so that it is uniformly distributed
|
||||
// across a range of values whose rounding errors cancel each other out.
|
||||
|
||||
const uniformCenteredDistribution = ( - 0.5 + ( i + 0.5 ) / jitterOffsets.length );
|
||||
sampleWeight += roundingRange * uniformCenteredDistribution;
|
||||
|
||||
}
|
||||
|
||||
this.copyUniforms[ 'opacity' ].value = sampleWeight;
|
||||
renderer.setClearColor( this.clearColor, this.clearAlpha );
|
||||
renderer.setRenderTarget( this.sampleRenderTarget );
|
||||
renderer.clear();
|
||||
renderer.render( this.scene, this.camera );
|
||||
|
||||
renderer.setRenderTarget( this.renderToScreen ? null : writeBuffer );
|
||||
|
||||
if ( i === 0 ) {
|
||||
|
||||
renderer.setClearColor( 0x000000, 0.0 );
|
||||
renderer.clear();
|
||||
|
||||
}
|
||||
|
||||
this.fsQuad.render( renderer );
|
||||
|
||||
}
|
||||
|
||||
if ( this.camera.setViewOffset && originalViewOffset.enabled ) {
|
||||
|
||||
this.camera.setViewOffset(
|
||||
|
||||
originalViewOffset.fullWidth, originalViewOffset.fullHeight,
|
||||
|
||||
originalViewOffset.offsetX, originalViewOffset.offsetY,
|
||||
|
||||
originalViewOffset.width, originalViewOffset.height
|
||||
|
||||
);
|
||||
|
||||
} else if ( this.camera.clearViewOffset ) {
|
||||
|
||||
this.camera.clearViewOffset();
|
||||
|
||||
}
|
||||
|
||||
renderer.autoClear = autoClear;
|
||||
renderer.setClearColor( this._oldClearColor, oldClearAlpha );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// These jitter vectors are specified in integers because it is easier.
|
||||
// I am assuming a [-8,8) integer grid, but it needs to be mapped onto [-0.5,0.5)
|
||||
// before being used, thus these integers need to be scaled by 1/16.
|
||||
//
|
||||
// Sample patterns reference: https://msdn.microsoft.com/en-us/library/windows/desktop/ff476218%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396
|
||||
const _JitterVectors = [
|
||||
[
|
||||
[ 0, 0 ]
|
||||
],
|
||||
[
|
||||
[ 4, 4 ], [ - 4, - 4 ]
|
||||
],
|
||||
[
|
||||
[ - 2, - 6 ], [ 6, - 2 ], [ - 6, 2 ], [ 2, 6 ]
|
||||
],
|
||||
[
|
||||
[ 1, - 3 ], [ - 1, 3 ], [ 5, 1 ], [ - 3, - 5 ],
|
||||
[ - 5, 5 ], [ - 7, - 1 ], [ 3, 7 ], [ 7, - 7 ]
|
||||
],
|
||||
[
|
||||
[ 1, 1 ], [ - 1, - 3 ], [ - 3, 2 ], [ 4, - 1 ],
|
||||
[ - 5, - 2 ], [ 2, 5 ], [ 5, 3 ], [ 3, - 5 ],
|
||||
[ - 2, 6 ], [ 0, - 7 ], [ - 4, - 6 ], [ - 6, 4 ],
|
||||
[ - 8, 0 ], [ 7, - 4 ], [ 6, 7 ], [ - 7, - 8 ]
|
||||
],
|
||||
[
|
||||
[ - 4, - 7 ], [ - 7, - 5 ], [ - 3, - 5 ], [ - 5, - 4 ],
|
||||
[ - 1, - 4 ], [ - 2, - 2 ], [ - 6, - 1 ], [ - 4, 0 ],
|
||||
[ - 7, 1 ], [ - 1, 2 ], [ - 6, 3 ], [ - 3, 3 ],
|
||||
[ - 7, 6 ], [ - 3, 6 ], [ - 5, 7 ], [ - 1, 7 ],
|
||||
[ 5, - 7 ], [ 1, - 6 ], [ 6, - 5 ], [ 4, - 4 ],
|
||||
[ 2, - 3 ], [ 7, - 2 ], [ 1, - 1 ], [ 4, - 1 ],
|
||||
[ 2, 1 ], [ 6, 2 ], [ 0, 4 ], [ 4, 4 ],
|
||||
[ 2, 5 ], [ 7, 5 ], [ 5, 6 ], [ 3, 7 ]
|
||||
]
|
||||
];
|
||||
|
||||
export { SSAARenderPass };
|
417
static/sdk/three/jsm/postprocessing/SSAOPass.js
Normal file
417
static/sdk/three/jsm/postprocessing/SSAOPass.js
Normal file
@ -0,0 +1,417 @@
|
||||
import {
|
||||
AddEquation,
|
||||
Color,
|
||||
CustomBlending,
|
||||
DataTexture,
|
||||
DepthTexture,
|
||||
DstAlphaFactor,
|
||||
DstColorFactor,
|
||||
FloatType,
|
||||
HalfFloatType,
|
||||
MathUtils,
|
||||
MeshNormalMaterial,
|
||||
NearestFilter,
|
||||
NoBlending,
|
||||
RedFormat,
|
||||
DepthStencilFormat,
|
||||
UnsignedInt248Type,
|
||||
RepeatWrapping,
|
||||
ShaderMaterial,
|
||||
UniformsUtils,
|
||||
Vector3,
|
||||
WebGLRenderTarget,
|
||||
ZeroFactor
|
||||
} from 'three';
|
||||
import { Pass, FullScreenQuad } from './Pass.js';
|
||||
import { SimplexNoise } from '../math/SimplexNoise.js';
|
||||
import { SSAOShader } from '../shaders/SSAOShader.js';
|
||||
import { SSAOBlurShader } from '../shaders/SSAOShader.js';
|
||||
import { SSAODepthShader } from '../shaders/SSAOShader.js';
|
||||
import { CopyShader } from '../shaders/CopyShader.js';
|
||||
|
||||
class SSAOPass extends Pass {
|
||||
|
||||
constructor( scene, camera, width, height, kernelSize = 32 ) {
|
||||
|
||||
super();
|
||||
|
||||
this.width = ( width !== undefined ) ? width : 512;
|
||||
this.height = ( height !== undefined ) ? height : 512;
|
||||
|
||||
this.clear = true;
|
||||
|
||||
this.camera = camera;
|
||||
this.scene = scene;
|
||||
|
||||
this.kernelRadius = 8;
|
||||
this.kernel = [];
|
||||
this.noiseTexture = null;
|
||||
this.output = 0;
|
||||
|
||||
this.minDistance = 0.005;
|
||||
this.maxDistance = 0.1;
|
||||
|
||||
this._visibilityCache = new Map();
|
||||
|
||||
//
|
||||
|
||||
this.generateSampleKernel( kernelSize );
|
||||
this.generateRandomKernelRotations();
|
||||
|
||||
// depth texture
|
||||
|
||||
const depthTexture = new DepthTexture();
|
||||
depthTexture.format = DepthStencilFormat;
|
||||
depthTexture.type = UnsignedInt248Type;
|
||||
|
||||
// normal render target with depth buffer
|
||||
|
||||
this.normalRenderTarget = new WebGLRenderTarget( this.width, this.height, {
|
||||
minFilter: NearestFilter,
|
||||
magFilter: NearestFilter,
|
||||
type: HalfFloatType,
|
||||
depthTexture: depthTexture
|
||||
} );
|
||||
|
||||
// ssao render target
|
||||
|
||||
this.ssaoRenderTarget = new WebGLRenderTarget( this.width, this.height, { type: HalfFloatType } );
|
||||
|
||||
this.blurRenderTarget = this.ssaoRenderTarget.clone();
|
||||
|
||||
// ssao material
|
||||
|
||||
this.ssaoMaterial = new ShaderMaterial( {
|
||||
defines: Object.assign( {}, SSAOShader.defines ),
|
||||
uniforms: UniformsUtils.clone( SSAOShader.uniforms ),
|
||||
vertexShader: SSAOShader.vertexShader,
|
||||
fragmentShader: SSAOShader.fragmentShader,
|
||||
blending: NoBlending
|
||||
} );
|
||||
|
||||
this.ssaoMaterial.defines[ 'KERNEL_SIZE' ] = kernelSize;
|
||||
|
||||
this.ssaoMaterial.uniforms[ 'tNormal' ].value = this.normalRenderTarget.texture;
|
||||
this.ssaoMaterial.uniforms[ 'tDepth' ].value = this.normalRenderTarget.depthTexture;
|
||||
this.ssaoMaterial.uniforms[ 'tNoise' ].value = this.noiseTexture;
|
||||
this.ssaoMaterial.uniforms[ 'kernel' ].value = this.kernel;
|
||||
this.ssaoMaterial.uniforms[ 'cameraNear' ].value = this.camera.near;
|
||||
this.ssaoMaterial.uniforms[ 'cameraFar' ].value = this.camera.far;
|
||||
this.ssaoMaterial.uniforms[ 'resolution' ].value.set( this.width, this.height );
|
||||
this.ssaoMaterial.uniforms[ 'cameraProjectionMatrix' ].value.copy( this.camera.projectionMatrix );
|
||||
this.ssaoMaterial.uniforms[ 'cameraInverseProjectionMatrix' ].value.copy( this.camera.projectionMatrixInverse );
|
||||
|
||||
// normal material
|
||||
|
||||
this.normalMaterial = new MeshNormalMaterial();
|
||||
this.normalMaterial.blending = NoBlending;
|
||||
|
||||
// blur material
|
||||
|
||||
this.blurMaterial = new ShaderMaterial( {
|
||||
defines: Object.assign( {}, SSAOBlurShader.defines ),
|
||||
uniforms: UniformsUtils.clone( SSAOBlurShader.uniforms ),
|
||||
vertexShader: SSAOBlurShader.vertexShader,
|
||||
fragmentShader: SSAOBlurShader.fragmentShader
|
||||
} );
|
||||
this.blurMaterial.uniforms[ 'tDiffuse' ].value = this.ssaoRenderTarget.texture;
|
||||
this.blurMaterial.uniforms[ 'resolution' ].value.set( this.width, this.height );
|
||||
|
||||
// material for rendering the depth
|
||||
|
||||
this.depthRenderMaterial = new ShaderMaterial( {
|
||||
defines: Object.assign( {}, SSAODepthShader.defines ),
|
||||
uniforms: UniformsUtils.clone( SSAODepthShader.uniforms ),
|
||||
vertexShader: SSAODepthShader.vertexShader,
|
||||
fragmentShader: SSAODepthShader.fragmentShader,
|
||||
blending: NoBlending
|
||||
} );
|
||||
this.depthRenderMaterial.uniforms[ 'tDepth' ].value = this.normalRenderTarget.depthTexture;
|
||||
this.depthRenderMaterial.uniforms[ 'cameraNear' ].value = this.camera.near;
|
||||
this.depthRenderMaterial.uniforms[ 'cameraFar' ].value = this.camera.far;
|
||||
|
||||
// material for rendering the content of a render target
|
||||
|
||||
this.copyMaterial = new ShaderMaterial( {
|
||||
uniforms: UniformsUtils.clone( CopyShader.uniforms ),
|
||||
vertexShader: CopyShader.vertexShader,
|
||||
fragmentShader: CopyShader.fragmentShader,
|
||||
transparent: true,
|
||||
depthTest: false,
|
||||
depthWrite: false,
|
||||
blendSrc: DstColorFactor,
|
||||
blendDst: ZeroFactor,
|
||||
blendEquation: AddEquation,
|
||||
blendSrcAlpha: DstAlphaFactor,
|
||||
blendDstAlpha: ZeroFactor,
|
||||
blendEquationAlpha: AddEquation
|
||||
} );
|
||||
|
||||
this.fsQuad = new FullScreenQuad( null );
|
||||
|
||||
this.originalClearColor = new Color();
|
||||
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
||||
// dispose render targets
|
||||
|
||||
this.normalRenderTarget.dispose();
|
||||
this.ssaoRenderTarget.dispose();
|
||||
this.blurRenderTarget.dispose();
|
||||
|
||||
// dispose materials
|
||||
|
||||
this.normalMaterial.dispose();
|
||||
this.blurMaterial.dispose();
|
||||
this.copyMaterial.dispose();
|
||||
this.depthRenderMaterial.dispose();
|
||||
|
||||
// dipsose full screen quad
|
||||
|
||||
this.fsQuad.dispose();
|
||||
|
||||
}
|
||||
|
||||
render( renderer, writeBuffer, readBuffer /*, deltaTime, maskActive */ ) {
|
||||
|
||||
// render normals and depth (honor only meshes, points and lines do not contribute to SSAO)
|
||||
|
||||
this.overrideVisibility();
|
||||
this.renderOverride( renderer, this.normalMaterial, this.normalRenderTarget, 0x7777ff, 1.0 );
|
||||
this.restoreVisibility();
|
||||
|
||||
// render SSAO
|
||||
|
||||
this.ssaoMaterial.uniforms[ 'kernelRadius' ].value = this.kernelRadius;
|
||||
this.ssaoMaterial.uniforms[ 'minDistance' ].value = this.minDistance;
|
||||
this.ssaoMaterial.uniforms[ 'maxDistance' ].value = this.maxDistance;
|
||||
this.renderPass( renderer, this.ssaoMaterial, this.ssaoRenderTarget );
|
||||
|
||||
// render blur
|
||||
|
||||
this.renderPass( renderer, this.blurMaterial, this.blurRenderTarget );
|
||||
|
||||
// output result to screen
|
||||
|
||||
switch ( this.output ) {
|
||||
|
||||
case SSAOPass.OUTPUT.SSAO:
|
||||
|
||||
this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.ssaoRenderTarget.texture;
|
||||
this.copyMaterial.blending = NoBlending;
|
||||
this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer );
|
||||
|
||||
break;
|
||||
|
||||
case SSAOPass.OUTPUT.Blur:
|
||||
|
||||
this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.blurRenderTarget.texture;
|
||||
this.copyMaterial.blending = NoBlending;
|
||||
this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer );
|
||||
|
||||
break;
|
||||
|
||||
case SSAOPass.OUTPUT.Depth:
|
||||
|
||||
this.renderPass( renderer, this.depthRenderMaterial, this.renderToScreen ? null : writeBuffer );
|
||||
|
||||
break;
|
||||
|
||||
case SSAOPass.OUTPUT.Normal:
|
||||
|
||||
this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.normalRenderTarget.texture;
|
||||
this.copyMaterial.blending = NoBlending;
|
||||
this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer );
|
||||
|
||||
break;
|
||||
|
||||
case SSAOPass.OUTPUT.Default:
|
||||
|
||||
this.copyMaterial.uniforms[ 'tDiffuse' ].value = readBuffer.texture;
|
||||
this.copyMaterial.blending = NoBlending;
|
||||
this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer );
|
||||
|
||||
this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.blurRenderTarget.texture;
|
||||
this.copyMaterial.blending = CustomBlending;
|
||||
this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer );
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
console.warn( 'THREE.SSAOPass: Unknown output type.' );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
renderPass( renderer, passMaterial, renderTarget, clearColor, clearAlpha ) {
|
||||
|
||||
// save original state
|
||||
renderer.getClearColor( this.originalClearColor );
|
||||
const originalClearAlpha = renderer.getClearAlpha();
|
||||
const originalAutoClear = renderer.autoClear;
|
||||
|
||||
renderer.setRenderTarget( renderTarget );
|
||||
|
||||
// setup pass state
|
||||
renderer.autoClear = false;
|
||||
if ( ( clearColor !== undefined ) && ( clearColor !== null ) ) {
|
||||
|
||||
renderer.setClearColor( clearColor );
|
||||
renderer.setClearAlpha( clearAlpha || 0.0 );
|
||||
renderer.clear();
|
||||
|
||||
}
|
||||
|
||||
this.fsQuad.material = passMaterial;
|
||||
this.fsQuad.render( renderer );
|
||||
|
||||
// restore original state
|
||||
renderer.autoClear = originalAutoClear;
|
||||
renderer.setClearColor( this.originalClearColor );
|
||||
renderer.setClearAlpha( originalClearAlpha );
|
||||
|
||||
}
|
||||
|
||||
renderOverride( renderer, overrideMaterial, renderTarget, clearColor, clearAlpha ) {
|
||||
|
||||
renderer.getClearColor( this.originalClearColor );
|
||||
const originalClearAlpha = renderer.getClearAlpha();
|
||||
const originalAutoClear = renderer.autoClear;
|
||||
|
||||
renderer.setRenderTarget( renderTarget );
|
||||
renderer.autoClear = false;
|
||||
|
||||
clearColor = overrideMaterial.clearColor || clearColor;
|
||||
clearAlpha = overrideMaterial.clearAlpha || clearAlpha;
|
||||
|
||||
if ( ( clearColor !== undefined ) && ( clearColor !== null ) ) {
|
||||
|
||||
renderer.setClearColor( clearColor );
|
||||
renderer.setClearAlpha( clearAlpha || 0.0 );
|
||||
renderer.clear();
|
||||
|
||||
}
|
||||
|
||||
this.scene.overrideMaterial = overrideMaterial;
|
||||
renderer.render( this.scene, this.camera );
|
||||
this.scene.overrideMaterial = null;
|
||||
|
||||
// restore original state
|
||||
|
||||
renderer.autoClear = originalAutoClear;
|
||||
renderer.setClearColor( this.originalClearColor );
|
||||
renderer.setClearAlpha( originalClearAlpha );
|
||||
|
||||
}
|
||||
|
||||
setSize( width, height ) {
|
||||
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
|
||||
this.ssaoRenderTarget.setSize( width, height );
|
||||
this.normalRenderTarget.setSize( width, height );
|
||||
this.blurRenderTarget.setSize( width, height );
|
||||
|
||||
this.ssaoMaterial.uniforms[ 'resolution' ].value.set( width, height );
|
||||
this.ssaoMaterial.uniforms[ 'cameraProjectionMatrix' ].value.copy( this.camera.projectionMatrix );
|
||||
this.ssaoMaterial.uniforms[ 'cameraInverseProjectionMatrix' ].value.copy( this.camera.projectionMatrixInverse );
|
||||
|
||||
this.blurMaterial.uniforms[ 'resolution' ].value.set( width, height );
|
||||
|
||||
}
|
||||
|
||||
generateSampleKernel( kernelSize ) {
|
||||
|
||||
const kernel = this.kernel;
|
||||
|
||||
for ( let i = 0; i < kernelSize; i ++ ) {
|
||||
|
||||
const sample = new Vector3();
|
||||
sample.x = ( Math.random() * 2 ) - 1;
|
||||
sample.y = ( Math.random() * 2 ) - 1;
|
||||
sample.z = Math.random();
|
||||
|
||||
sample.normalize();
|
||||
|
||||
let scale = i / kernelSize;
|
||||
scale = MathUtils.lerp( 0.1, 1, scale * scale );
|
||||
sample.multiplyScalar( scale );
|
||||
|
||||
kernel.push( sample );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
generateRandomKernelRotations() {
|
||||
|
||||
const width = 4, height = 4;
|
||||
|
||||
const simplex = new SimplexNoise();
|
||||
|
||||
const size = width * height;
|
||||
const data = new Float32Array( size );
|
||||
|
||||
for ( let i = 0; i < size; i ++ ) {
|
||||
|
||||
const x = ( Math.random() * 2 ) - 1;
|
||||
const y = ( Math.random() * 2 ) - 1;
|
||||
const z = 0;
|
||||
|
||||
data[ i ] = simplex.noise3d( x, y, z );
|
||||
|
||||
}
|
||||
|
||||
this.noiseTexture = new DataTexture( data, width, height, RedFormat, FloatType );
|
||||
this.noiseTexture.wrapS = RepeatWrapping;
|
||||
this.noiseTexture.wrapT = RepeatWrapping;
|
||||
this.noiseTexture.needsUpdate = true;
|
||||
|
||||
}
|
||||
|
||||
overrideVisibility() {
|
||||
|
||||
const scene = this.scene;
|
||||
const cache = this._visibilityCache;
|
||||
|
||||
scene.traverse( function ( object ) {
|
||||
|
||||
cache.set( object, object.visible );
|
||||
|
||||
if ( object.isPoints || object.isLine ) object.visible = false;
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
restoreVisibility() {
|
||||
|
||||
const scene = this.scene;
|
||||
const cache = this._visibilityCache;
|
||||
|
||||
scene.traverse( function ( object ) {
|
||||
|
||||
const visible = cache.get( object );
|
||||
object.visible = visible;
|
||||
|
||||
} );
|
||||
|
||||
cache.clear();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
SSAOPass.OUTPUT = {
|
||||
'Default': 0,
|
||||
'SSAO': 1,
|
||||
'Blur': 2,
|
||||
'Depth': 3,
|
||||
'Normal': 4
|
||||
};
|
||||
|
||||
export { SSAOPass };
|
641
static/sdk/three/jsm/postprocessing/SSRPass.js
Normal file
641
static/sdk/three/jsm/postprocessing/SSRPass.js
Normal file
@ -0,0 +1,641 @@
|
||||
import {
|
||||
AddEquation,
|
||||
Color,
|
||||
NormalBlending,
|
||||
DepthTexture,
|
||||
SrcAlphaFactor,
|
||||
OneMinusSrcAlphaFactor,
|
||||
MeshNormalMaterial,
|
||||
MeshBasicMaterial,
|
||||
NearestFilter,
|
||||
NoBlending,
|
||||
ShaderMaterial,
|
||||
UniformsUtils,
|
||||
UnsignedShortType,
|
||||
WebGLRenderTarget,
|
||||
HalfFloatType,
|
||||
} from 'three';
|
||||
import { Pass, FullScreenQuad } from './Pass.js';
|
||||
import { SSRShader } from '../shaders/SSRShader.js';
|
||||
import { SSRBlurShader } from '../shaders/SSRShader.js';
|
||||
import { SSRDepthShader } from '../shaders/SSRShader.js';
|
||||
import { CopyShader } from '../shaders/CopyShader.js';
|
||||
|
||||
class SSRPass extends Pass {
|
||||
|
||||
constructor( { renderer, scene, camera, width, height, selects, bouncing = false, groundReflector } ) {
|
||||
|
||||
super();
|
||||
|
||||
this.width = ( width !== undefined ) ? width : 512;
|
||||
this.height = ( height !== undefined ) ? height : 512;
|
||||
|
||||
this.clear = true;
|
||||
|
||||
this.renderer = renderer;
|
||||
this.scene = scene;
|
||||
this.camera = camera;
|
||||
this.groundReflector = groundReflector;
|
||||
|
||||
this.opacity = SSRShader.uniforms.opacity.value;
|
||||
this.output = 0;
|
||||
|
||||
this.maxDistance = SSRShader.uniforms.maxDistance.value;
|
||||
this.thickness = SSRShader.uniforms.thickness.value;
|
||||
|
||||
this.tempColor = new Color();
|
||||
|
||||
this._selects = selects;
|
||||
this.selective = Array.isArray( this._selects );
|
||||
Object.defineProperty( this, 'selects', {
|
||||
get() {
|
||||
|
||||
return this._selects;
|
||||
|
||||
},
|
||||
set( val ) {
|
||||
|
||||
if ( this._selects === val ) return;
|
||||
this._selects = val;
|
||||
if ( Array.isArray( val ) ) {
|
||||
|
||||
this.selective = true;
|
||||
this.ssrMaterial.defines.SELECTIVE = true;
|
||||
this.ssrMaterial.needsUpdate = true;
|
||||
|
||||
} else {
|
||||
|
||||
this.selective = false;
|
||||
this.ssrMaterial.defines.SELECTIVE = false;
|
||||
this.ssrMaterial.needsUpdate = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
} );
|
||||
|
||||
this._bouncing = bouncing;
|
||||
Object.defineProperty( this, 'bouncing', {
|
||||
get() {
|
||||
|
||||
return this._bouncing;
|
||||
|
||||
},
|
||||
set( val ) {
|
||||
|
||||
if ( this._bouncing === val ) return;
|
||||
this._bouncing = val;
|
||||
if ( val ) {
|
||||
|
||||
this.ssrMaterial.uniforms[ 'tDiffuse' ].value = this.prevRenderTarget.texture;
|
||||
|
||||
} else {
|
||||
|
||||
this.ssrMaterial.uniforms[ 'tDiffuse' ].value = this.beautyRenderTarget.texture;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
} );
|
||||
|
||||
this.blur = true;
|
||||
|
||||
this._distanceAttenuation = SSRShader.defines.DISTANCE_ATTENUATION;
|
||||
Object.defineProperty( this, 'distanceAttenuation', {
|
||||
get() {
|
||||
|
||||
return this._distanceAttenuation;
|
||||
|
||||
},
|
||||
set( val ) {
|
||||
|
||||
if ( this._distanceAttenuation === val ) return;
|
||||
this._distanceAttenuation = val;
|
||||
this.ssrMaterial.defines.DISTANCE_ATTENUATION = val;
|
||||
this.ssrMaterial.needsUpdate = true;
|
||||
|
||||
}
|
||||
} );
|
||||
|
||||
|
||||
this._fresnel = SSRShader.defines.FRESNEL;
|
||||
Object.defineProperty( this, 'fresnel', {
|
||||
get() {
|
||||
|
||||
return this._fresnel;
|
||||
|
||||
},
|
||||
set( val ) {
|
||||
|
||||
if ( this._fresnel === val ) return;
|
||||
this._fresnel = val;
|
||||
this.ssrMaterial.defines.FRESNEL = val;
|
||||
this.ssrMaterial.needsUpdate = true;
|
||||
|
||||
}
|
||||
} );
|
||||
|
||||
this._infiniteThick = SSRShader.defines.INFINITE_THICK;
|
||||
Object.defineProperty( this, 'infiniteThick', {
|
||||
get() {
|
||||
|
||||
return this._infiniteThick;
|
||||
|
||||
},
|
||||
set( val ) {
|
||||
|
||||
if ( this._infiniteThick === val ) return;
|
||||
this._infiniteThick = val;
|
||||
this.ssrMaterial.defines.INFINITE_THICK = val;
|
||||
this.ssrMaterial.needsUpdate = true;
|
||||
|
||||
}
|
||||
} );
|
||||
|
||||
// beauty render target with depth buffer
|
||||
|
||||
const depthTexture = new DepthTexture();
|
||||
depthTexture.type = UnsignedShortType;
|
||||
depthTexture.minFilter = NearestFilter;
|
||||
depthTexture.magFilter = NearestFilter;
|
||||
|
||||
this.beautyRenderTarget = new WebGLRenderTarget( this.width, this.height, {
|
||||
minFilter: NearestFilter,
|
||||
magFilter: NearestFilter,
|
||||
type: HalfFloatType,
|
||||
depthTexture: depthTexture,
|
||||
depthBuffer: true
|
||||
} );
|
||||
|
||||
//for bouncing
|
||||
this.prevRenderTarget = new WebGLRenderTarget( this.width, this.height, {
|
||||
minFilter: NearestFilter,
|
||||
magFilter: NearestFilter
|
||||
} );
|
||||
|
||||
// normal render target
|
||||
|
||||
this.normalRenderTarget = new WebGLRenderTarget( this.width, this.height, {
|
||||
minFilter: NearestFilter,
|
||||
magFilter: NearestFilter,
|
||||
type: HalfFloatType,
|
||||
} );
|
||||
|
||||
// metalness render target
|
||||
|
||||
this.metalnessRenderTarget = new WebGLRenderTarget( this.width, this.height, {
|
||||
minFilter: NearestFilter,
|
||||
magFilter: NearestFilter,
|
||||
type: HalfFloatType,
|
||||
} );
|
||||
|
||||
|
||||
|
||||
// ssr render target
|
||||
|
||||
this.ssrRenderTarget = new WebGLRenderTarget( this.width, this.height, {
|
||||
minFilter: NearestFilter,
|
||||
magFilter: NearestFilter
|
||||
} );
|
||||
|
||||
this.blurRenderTarget = this.ssrRenderTarget.clone();
|
||||
this.blurRenderTarget2 = this.ssrRenderTarget.clone();
|
||||
// this.blurRenderTarget3 = this.ssrRenderTarget.clone();
|
||||
|
||||
// ssr material
|
||||
|
||||
this.ssrMaterial = new ShaderMaterial( {
|
||||
defines: Object.assign( {}, SSRShader.defines, {
|
||||
MAX_STEP: Math.sqrt( this.width * this.width + this.height * this.height )
|
||||
} ),
|
||||
uniforms: UniformsUtils.clone( SSRShader.uniforms ),
|
||||
vertexShader: SSRShader.vertexShader,
|
||||
fragmentShader: SSRShader.fragmentShader,
|
||||
blending: NoBlending
|
||||
} );
|
||||
|
||||
this.ssrMaterial.uniforms[ 'tDiffuse' ].value = this.beautyRenderTarget.texture;
|
||||
this.ssrMaterial.uniforms[ 'tNormal' ].value = this.normalRenderTarget.texture;
|
||||
this.ssrMaterial.defines.SELECTIVE = this.selective;
|
||||
this.ssrMaterial.needsUpdate = true;
|
||||
this.ssrMaterial.uniforms[ 'tMetalness' ].value = this.metalnessRenderTarget.texture;
|
||||
this.ssrMaterial.uniforms[ 'tDepth' ].value = this.beautyRenderTarget.depthTexture;
|
||||
this.ssrMaterial.uniforms[ 'cameraNear' ].value = this.camera.near;
|
||||
this.ssrMaterial.uniforms[ 'cameraFar' ].value = this.camera.far;
|
||||
this.ssrMaterial.uniforms[ 'thickness' ].value = this.thickness;
|
||||
this.ssrMaterial.uniforms[ 'resolution' ].value.set( this.width, this.height );
|
||||
this.ssrMaterial.uniforms[ 'cameraProjectionMatrix' ].value.copy( this.camera.projectionMatrix );
|
||||
this.ssrMaterial.uniforms[ 'cameraInverseProjectionMatrix' ].value.copy( this.camera.projectionMatrixInverse );
|
||||
|
||||
// normal material
|
||||
|
||||
this.normalMaterial = new MeshNormalMaterial();
|
||||
this.normalMaterial.blending = NoBlending;
|
||||
|
||||
// metalnessOn material
|
||||
|
||||
this.metalnessOnMaterial = new MeshBasicMaterial( {
|
||||
color: 'white'
|
||||
} );
|
||||
|
||||
// metalnessOff material
|
||||
|
||||
this.metalnessOffMaterial = new MeshBasicMaterial( {
|
||||
color: 'black'
|
||||
} );
|
||||
|
||||
// blur material
|
||||
|
||||
this.blurMaterial = new ShaderMaterial( {
|
||||
defines: Object.assign( {}, SSRBlurShader.defines ),
|
||||
uniforms: UniformsUtils.clone( SSRBlurShader.uniforms ),
|
||||
vertexShader: SSRBlurShader.vertexShader,
|
||||
fragmentShader: SSRBlurShader.fragmentShader
|
||||
} );
|
||||
this.blurMaterial.uniforms[ 'tDiffuse' ].value = this.ssrRenderTarget.texture;
|
||||
this.blurMaterial.uniforms[ 'resolution' ].value.set( this.width, this.height );
|
||||
|
||||
// blur material 2
|
||||
|
||||
this.blurMaterial2 = new ShaderMaterial( {
|
||||
defines: Object.assign( {}, SSRBlurShader.defines ),
|
||||
uniforms: UniformsUtils.clone( SSRBlurShader.uniforms ),
|
||||
vertexShader: SSRBlurShader.vertexShader,
|
||||
fragmentShader: SSRBlurShader.fragmentShader
|
||||
} );
|
||||
this.blurMaterial2.uniforms[ 'tDiffuse' ].value = this.blurRenderTarget.texture;
|
||||
this.blurMaterial2.uniforms[ 'resolution' ].value.set( this.width, this.height );
|
||||
|
||||
// // blur material 3
|
||||
|
||||
// this.blurMaterial3 = new ShaderMaterial({
|
||||
// defines: Object.assign({}, SSRBlurShader.defines),
|
||||
// uniforms: UniformsUtils.clone(SSRBlurShader.uniforms),
|
||||
// vertexShader: SSRBlurShader.vertexShader,
|
||||
// fragmentShader: SSRBlurShader.fragmentShader
|
||||
// });
|
||||
// this.blurMaterial3.uniforms['tDiffuse'].value = this.blurRenderTarget2.texture;
|
||||
// this.blurMaterial3.uniforms['resolution'].value.set(this.width, this.height);
|
||||
|
||||
// material for rendering the depth
|
||||
|
||||
this.depthRenderMaterial = new ShaderMaterial( {
|
||||
defines: Object.assign( {}, SSRDepthShader.defines ),
|
||||
uniforms: UniformsUtils.clone( SSRDepthShader.uniforms ),
|
||||
vertexShader: SSRDepthShader.vertexShader,
|
||||
fragmentShader: SSRDepthShader.fragmentShader,
|
||||
blending: NoBlending
|
||||
} );
|
||||
this.depthRenderMaterial.uniforms[ 'tDepth' ].value = this.beautyRenderTarget.depthTexture;
|
||||
this.depthRenderMaterial.uniforms[ 'cameraNear' ].value = this.camera.near;
|
||||
this.depthRenderMaterial.uniforms[ 'cameraFar' ].value = this.camera.far;
|
||||
|
||||
// material for rendering the content of a render target
|
||||
|
||||
this.copyMaterial = new ShaderMaterial( {
|
||||
uniforms: UniformsUtils.clone( CopyShader.uniforms ),
|
||||
vertexShader: CopyShader.vertexShader,
|
||||
fragmentShader: CopyShader.fragmentShader,
|
||||
transparent: true,
|
||||
depthTest: false,
|
||||
depthWrite: false,
|
||||
blendSrc: SrcAlphaFactor,
|
||||
blendDst: OneMinusSrcAlphaFactor,
|
||||
blendEquation: AddEquation,
|
||||
blendSrcAlpha: SrcAlphaFactor,
|
||||
blendDstAlpha: OneMinusSrcAlphaFactor,
|
||||
blendEquationAlpha: AddEquation,
|
||||
// premultipliedAlpha:true,
|
||||
} );
|
||||
|
||||
this.fsQuad = new FullScreenQuad( null );
|
||||
|
||||
this.originalClearColor = new Color();
|
||||
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
||||
// dispose render targets
|
||||
|
||||
this.beautyRenderTarget.dispose();
|
||||
this.prevRenderTarget.dispose();
|
||||
this.normalRenderTarget.dispose();
|
||||
this.metalnessRenderTarget.dispose();
|
||||
this.ssrRenderTarget.dispose();
|
||||
this.blurRenderTarget.dispose();
|
||||
this.blurRenderTarget2.dispose();
|
||||
// this.blurRenderTarget3.dispose();
|
||||
|
||||
// dispose materials
|
||||
|
||||
this.normalMaterial.dispose();
|
||||
this.metalnessOnMaterial.dispose();
|
||||
this.metalnessOffMaterial.dispose();
|
||||
this.blurMaterial.dispose();
|
||||
this.blurMaterial2.dispose();
|
||||
this.copyMaterial.dispose();
|
||||
this.depthRenderMaterial.dispose();
|
||||
|
||||
// dipsose full screen quad
|
||||
|
||||
this.fsQuad.dispose();
|
||||
|
||||
}
|
||||
|
||||
render( renderer, writeBuffer /*, readBuffer, deltaTime, maskActive */ ) {
|
||||
|
||||
// render beauty and depth
|
||||
|
||||
renderer.setRenderTarget( this.beautyRenderTarget );
|
||||
renderer.clear();
|
||||
if ( this.groundReflector ) {
|
||||
|
||||
this.groundReflector.visible = false;
|
||||
this.groundReflector.doRender( this.renderer, this.scene, this.camera );
|
||||
this.groundReflector.visible = true;
|
||||
|
||||
}
|
||||
|
||||
renderer.render( this.scene, this.camera );
|
||||
if ( this.groundReflector ) this.groundReflector.visible = false;
|
||||
|
||||
// render normals
|
||||
|
||||
this.renderOverride( renderer, this.normalMaterial, this.normalRenderTarget, 0, 0 );
|
||||
|
||||
// render metalnesses
|
||||
|
||||
if ( this.selective ) {
|
||||
|
||||
this.renderMetalness( renderer, this.metalnessOnMaterial, this.metalnessRenderTarget, 0, 0 );
|
||||
|
||||
}
|
||||
|
||||
// render SSR
|
||||
|
||||
this.ssrMaterial.uniforms[ 'opacity' ].value = this.opacity;
|
||||
this.ssrMaterial.uniforms[ 'maxDistance' ].value = this.maxDistance;
|
||||
this.ssrMaterial.uniforms[ 'thickness' ].value = this.thickness;
|
||||
this.renderPass( renderer, this.ssrMaterial, this.ssrRenderTarget );
|
||||
|
||||
|
||||
// render blur
|
||||
|
||||
if ( this.blur ) {
|
||||
|
||||
this.renderPass( renderer, this.blurMaterial, this.blurRenderTarget );
|
||||
this.renderPass( renderer, this.blurMaterial2, this.blurRenderTarget2 );
|
||||
// this.renderPass(renderer, this.blurMaterial3, this.blurRenderTarget3);
|
||||
|
||||
}
|
||||
|
||||
// output result to screen
|
||||
|
||||
switch ( this.output ) {
|
||||
|
||||
case SSRPass.OUTPUT.Default:
|
||||
|
||||
if ( this.bouncing ) {
|
||||
|
||||
this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.beautyRenderTarget.texture;
|
||||
this.copyMaterial.blending = NoBlending;
|
||||
this.renderPass( renderer, this.copyMaterial, this.prevRenderTarget );
|
||||
|
||||
if ( this.blur )
|
||||
this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.blurRenderTarget2.texture;
|
||||
else
|
||||
this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.ssrRenderTarget.texture;
|
||||
this.copyMaterial.blending = NormalBlending;
|
||||
this.renderPass( renderer, this.copyMaterial, this.prevRenderTarget );
|
||||
|
||||
this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.prevRenderTarget.texture;
|
||||
this.copyMaterial.blending = NoBlending;
|
||||
this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer );
|
||||
|
||||
} else {
|
||||
|
||||
this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.beautyRenderTarget.texture;
|
||||
this.copyMaterial.blending = NoBlending;
|
||||
this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer );
|
||||
|
||||
if ( this.blur )
|
||||
this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.blurRenderTarget2.texture;
|
||||
else
|
||||
this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.ssrRenderTarget.texture;
|
||||
this.copyMaterial.blending = NormalBlending;
|
||||
this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer );
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
case SSRPass.OUTPUT.SSR:
|
||||
|
||||
if ( this.blur )
|
||||
this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.blurRenderTarget2.texture;
|
||||
else
|
||||
this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.ssrRenderTarget.texture;
|
||||
this.copyMaterial.blending = NoBlending;
|
||||
this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer );
|
||||
|
||||
if ( this.bouncing ) {
|
||||
|
||||
if ( this.blur )
|
||||
this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.blurRenderTarget2.texture;
|
||||
else
|
||||
this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.beautyRenderTarget.texture;
|
||||
this.copyMaterial.blending = NoBlending;
|
||||
this.renderPass( renderer, this.copyMaterial, this.prevRenderTarget );
|
||||
|
||||
this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.ssrRenderTarget.texture;
|
||||
this.copyMaterial.blending = NormalBlending;
|
||||
this.renderPass( renderer, this.copyMaterial, this.prevRenderTarget );
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case SSRPass.OUTPUT.Beauty:
|
||||
|
||||
this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.beautyRenderTarget.texture;
|
||||
this.copyMaterial.blending = NoBlending;
|
||||
this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer );
|
||||
|
||||
break;
|
||||
|
||||
case SSRPass.OUTPUT.Depth:
|
||||
|
||||
this.renderPass( renderer, this.depthRenderMaterial, this.renderToScreen ? null : writeBuffer );
|
||||
|
||||
break;
|
||||
|
||||
case SSRPass.OUTPUT.Normal:
|
||||
|
||||
this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.normalRenderTarget.texture;
|
||||
this.copyMaterial.blending = NoBlending;
|
||||
this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer );
|
||||
|
||||
break;
|
||||
|
||||
case SSRPass.OUTPUT.Metalness:
|
||||
|
||||
this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.metalnessRenderTarget.texture;
|
||||
this.copyMaterial.blending = NoBlending;
|
||||
this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer );
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
console.warn( 'THREE.SSRPass: Unknown output type.' );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
renderPass( renderer, passMaterial, renderTarget, clearColor, clearAlpha ) {
|
||||
|
||||
// save original state
|
||||
this.originalClearColor.copy( renderer.getClearColor( this.tempColor ) );
|
||||
const originalClearAlpha = renderer.getClearAlpha( this.tempColor );
|
||||
const originalAutoClear = renderer.autoClear;
|
||||
|
||||
renderer.setRenderTarget( renderTarget );
|
||||
|
||||
// setup pass state
|
||||
renderer.autoClear = false;
|
||||
if ( ( clearColor !== undefined ) && ( clearColor !== null ) ) {
|
||||
|
||||
renderer.setClearColor( clearColor );
|
||||
renderer.setClearAlpha( clearAlpha || 0.0 );
|
||||
renderer.clear();
|
||||
|
||||
}
|
||||
|
||||
this.fsQuad.material = passMaterial;
|
||||
this.fsQuad.render( renderer );
|
||||
|
||||
// restore original state
|
||||
renderer.autoClear = originalAutoClear;
|
||||
renderer.setClearColor( this.originalClearColor );
|
||||
renderer.setClearAlpha( originalClearAlpha );
|
||||
|
||||
}
|
||||
|
||||
renderOverride( renderer, overrideMaterial, renderTarget, clearColor, clearAlpha ) {
|
||||
|
||||
this.originalClearColor.copy( renderer.getClearColor( this.tempColor ) );
|
||||
const originalClearAlpha = renderer.getClearAlpha( this.tempColor );
|
||||
const originalAutoClear = renderer.autoClear;
|
||||
|
||||
renderer.setRenderTarget( renderTarget );
|
||||
renderer.autoClear = false;
|
||||
|
||||
clearColor = overrideMaterial.clearColor || clearColor;
|
||||
clearAlpha = overrideMaterial.clearAlpha || clearAlpha;
|
||||
|
||||
if ( ( clearColor !== undefined ) && ( clearColor !== null ) ) {
|
||||
|
||||
renderer.setClearColor( clearColor );
|
||||
renderer.setClearAlpha( clearAlpha || 0.0 );
|
||||
renderer.clear();
|
||||
|
||||
}
|
||||
|
||||
this.scene.overrideMaterial = overrideMaterial;
|
||||
renderer.render( this.scene, this.camera );
|
||||
this.scene.overrideMaterial = null;
|
||||
|
||||
// restore original state
|
||||
|
||||
renderer.autoClear = originalAutoClear;
|
||||
renderer.setClearColor( this.originalClearColor );
|
||||
renderer.setClearAlpha( originalClearAlpha );
|
||||
|
||||
}
|
||||
|
||||
renderMetalness( renderer, overrideMaterial, renderTarget, clearColor, clearAlpha ) {
|
||||
|
||||
this.originalClearColor.copy( renderer.getClearColor( this.tempColor ) );
|
||||
const originalClearAlpha = renderer.getClearAlpha( this.tempColor );
|
||||
const originalAutoClear = renderer.autoClear;
|
||||
|
||||
renderer.setRenderTarget( renderTarget );
|
||||
renderer.autoClear = false;
|
||||
|
||||
clearColor = overrideMaterial.clearColor || clearColor;
|
||||
clearAlpha = overrideMaterial.clearAlpha || clearAlpha;
|
||||
|
||||
if ( ( clearColor !== undefined ) && ( clearColor !== null ) ) {
|
||||
|
||||
renderer.setClearColor( clearColor );
|
||||
renderer.setClearAlpha( clearAlpha || 0.0 );
|
||||
renderer.clear();
|
||||
|
||||
}
|
||||
|
||||
this.scene.traverseVisible( child => {
|
||||
|
||||
child._SSRPassBackupMaterial = child.material;
|
||||
if ( this._selects.includes( child ) ) {
|
||||
|
||||
child.material = this.metalnessOnMaterial;
|
||||
|
||||
} else {
|
||||
|
||||
child.material = this.metalnessOffMaterial;
|
||||
|
||||
}
|
||||
|
||||
} );
|
||||
renderer.render( this.scene, this.camera );
|
||||
this.scene.traverseVisible( child => {
|
||||
|
||||
child.material = child._SSRPassBackupMaterial;
|
||||
|
||||
} );
|
||||
|
||||
// restore original state
|
||||
|
||||
renderer.autoClear = originalAutoClear;
|
||||
renderer.setClearColor( this.originalClearColor );
|
||||
renderer.setClearAlpha( originalClearAlpha );
|
||||
|
||||
}
|
||||
|
||||
setSize( width, height ) {
|
||||
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
|
||||
this.ssrMaterial.defines.MAX_STEP = Math.sqrt( width * width + height * height );
|
||||
this.ssrMaterial.needsUpdate = true;
|
||||
this.beautyRenderTarget.setSize( width, height );
|
||||
this.prevRenderTarget.setSize( width, height );
|
||||
this.ssrRenderTarget.setSize( width, height );
|
||||
this.normalRenderTarget.setSize( width, height );
|
||||
this.metalnessRenderTarget.setSize( width, height );
|
||||
this.blurRenderTarget.setSize( width, height );
|
||||
this.blurRenderTarget2.setSize( width, height );
|
||||
// this.blurRenderTarget3.setSize(width, height);
|
||||
|
||||
this.ssrMaterial.uniforms[ 'resolution' ].value.set( width, height );
|
||||
this.ssrMaterial.uniforms[ 'cameraProjectionMatrix' ].value.copy( this.camera.projectionMatrix );
|
||||
this.ssrMaterial.uniforms[ 'cameraInverseProjectionMatrix' ].value.copy( this.camera.projectionMatrixInverse );
|
||||
|
||||
this.blurMaterial.uniforms[ 'resolution' ].value.set( width, height );
|
||||
this.blurMaterial2.uniforms[ 'resolution' ].value.set( width, height );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
SSRPass.OUTPUT = {
|
||||
'Default': 0,
|
||||
'SSR': 1,
|
||||
'Beauty': 3,
|
||||
'Depth': 4,
|
||||
'Normal': 5,
|
||||
'Metalness': 7,
|
||||
};
|
||||
|
||||
export { SSRPass };
|
79
static/sdk/three/jsm/postprocessing/SavePass.js
Normal file
79
static/sdk/three/jsm/postprocessing/SavePass.js
Normal file
@ -0,0 +1,79 @@
|
||||
import {
|
||||
HalfFloatType,
|
||||
NoBlending,
|
||||
ShaderMaterial,
|
||||
UniformsUtils,
|
||||
WebGLRenderTarget
|
||||
} from 'three';
|
||||
import { Pass, FullScreenQuad } from './Pass.js';
|
||||
import { CopyShader } from '../shaders/CopyShader.js';
|
||||
|
||||
class SavePass extends Pass {
|
||||
|
||||
constructor( renderTarget ) {
|
||||
|
||||
super();
|
||||
|
||||
const shader = CopyShader;
|
||||
|
||||
this.textureID = 'tDiffuse';
|
||||
|
||||
this.uniforms = UniformsUtils.clone( shader.uniforms );
|
||||
|
||||
this.material = new ShaderMaterial( {
|
||||
|
||||
uniforms: this.uniforms,
|
||||
vertexShader: shader.vertexShader,
|
||||
fragmentShader: shader.fragmentShader,
|
||||
blending: NoBlending
|
||||
|
||||
} );
|
||||
|
||||
this.renderTarget = renderTarget;
|
||||
|
||||
if ( this.renderTarget === undefined ) {
|
||||
|
||||
this.renderTarget = new WebGLRenderTarget( 1, 1, { type: HalfFloatType } ); // will be resized later
|
||||
this.renderTarget.texture.name = 'SavePass.rt';
|
||||
|
||||
}
|
||||
|
||||
this.needsSwap = false;
|
||||
|
||||
this.fsQuad = new FullScreenQuad( this.material );
|
||||
|
||||
}
|
||||
|
||||
render( renderer, writeBuffer, readBuffer/*, deltaTime, maskActive */ ) {
|
||||
|
||||
if ( this.uniforms[ this.textureID ] ) {
|
||||
|
||||
this.uniforms[ this.textureID ].value = readBuffer.texture;
|
||||
|
||||
}
|
||||
|
||||
renderer.setRenderTarget( this.renderTarget );
|
||||
if ( this.clear ) renderer.clear();
|
||||
this.fsQuad.render( renderer );
|
||||
|
||||
}
|
||||
|
||||
setSize( width, height ) {
|
||||
|
||||
this.renderTarget.setSize( width, height );
|
||||
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
||||
this.renderTarget.dispose();
|
||||
|
||||
this.material.dispose();
|
||||
|
||||
this.fsQuad.dispose();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { SavePass };
|
77
static/sdk/three/jsm/postprocessing/ShaderPass.js
Normal file
77
static/sdk/three/jsm/postprocessing/ShaderPass.js
Normal file
@ -0,0 +1,77 @@
|
||||
import {
|
||||
ShaderMaterial,
|
||||
UniformsUtils
|
||||
} from 'three';
|
||||
import { Pass, FullScreenQuad } from './Pass.js';
|
||||
|
||||
class ShaderPass extends Pass {
|
||||
|
||||
constructor( shader, textureID ) {
|
||||
|
||||
super();
|
||||
|
||||
this.textureID = ( textureID !== undefined ) ? textureID : 'tDiffuse';
|
||||
|
||||
if ( shader instanceof ShaderMaterial ) {
|
||||
|
||||
this.uniforms = shader.uniforms;
|
||||
|
||||
this.material = shader;
|
||||
|
||||
} else if ( shader ) {
|
||||
|
||||
this.uniforms = UniformsUtils.clone( shader.uniforms );
|
||||
|
||||
this.material = new ShaderMaterial( {
|
||||
|
||||
name: ( shader.name !== undefined ) ? shader.name : 'unspecified',
|
||||
defines: Object.assign( {}, shader.defines ),
|
||||
uniforms: this.uniforms,
|
||||
vertexShader: shader.vertexShader,
|
||||
fragmentShader: shader.fragmentShader
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
this.fsQuad = new FullScreenQuad( this.material );
|
||||
|
||||
}
|
||||
|
||||
render( renderer, writeBuffer, readBuffer /*, deltaTime, maskActive */ ) {
|
||||
|
||||
if ( this.uniforms[ this.textureID ] ) {
|
||||
|
||||
this.uniforms[ this.textureID ].value = readBuffer.texture;
|
||||
|
||||
}
|
||||
|
||||
this.fsQuad.material = this.material;
|
||||
|
||||
if ( this.renderToScreen ) {
|
||||
|
||||
renderer.setRenderTarget( null );
|
||||
this.fsQuad.render( renderer );
|
||||
|
||||
} else {
|
||||
|
||||
renderer.setRenderTarget( writeBuffer );
|
||||
// TODO: Avoid using autoClear properties, see https://github.com/mrdoob/three.js/pull/15571#issuecomment-465669600
|
||||
if ( this.clear ) renderer.clear( renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil );
|
||||
this.fsQuad.render( renderer );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
||||
this.material.dispose();
|
||||
|
||||
this.fsQuad.dispose();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { ShaderPass };
|
188
static/sdk/three/jsm/postprocessing/TAARenderPass.js
Normal file
188
static/sdk/three/jsm/postprocessing/TAARenderPass.js
Normal file
@ -0,0 +1,188 @@
|
||||
import {
|
||||
HalfFloatType,
|
||||
WebGLRenderTarget
|
||||
} from 'three';
|
||||
import { SSAARenderPass } from './SSAARenderPass.js';
|
||||
|
||||
/**
|
||||
*
|
||||
* Temporal Anti-Aliasing Render Pass
|
||||
*
|
||||
* When there is no motion in the scene, the TAA render pass accumulates jittered camera samples across frames to create a high quality anti-aliased result.
|
||||
*
|
||||
* References:
|
||||
*
|
||||
* TODO: Add support for motion vector pas so that accumulation of samples across frames can occur on dynamics scenes.
|
||||
*
|
||||
*/
|
||||
|
||||
class TAARenderPass extends SSAARenderPass {
|
||||
|
||||
constructor( scene, camera, clearColor, clearAlpha ) {
|
||||
|
||||
super( scene, camera, clearColor, clearAlpha );
|
||||
|
||||
this.sampleLevel = 0;
|
||||
this.accumulate = false;
|
||||
this.accumulateIndex = - 1;
|
||||
|
||||
}
|
||||
|
||||
render( renderer, writeBuffer, readBuffer, deltaTime ) {
|
||||
|
||||
if ( this.accumulate === false ) {
|
||||
|
||||
super.render( renderer, writeBuffer, readBuffer, deltaTime );
|
||||
|
||||
this.accumulateIndex = - 1;
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
const jitterOffsets = _JitterVectors[ 5 ];
|
||||
|
||||
if ( this.sampleRenderTarget === undefined ) {
|
||||
|
||||
this.sampleRenderTarget = new WebGLRenderTarget( readBuffer.width, readBuffer.height, { type: HalfFloatType } );
|
||||
this.sampleRenderTarget.texture.name = 'TAARenderPass.sample';
|
||||
|
||||
}
|
||||
|
||||
if ( this.holdRenderTarget === undefined ) {
|
||||
|
||||
this.holdRenderTarget = new WebGLRenderTarget( readBuffer.width, readBuffer.height, { type: HalfFloatType } );
|
||||
this.holdRenderTarget.texture.name = 'TAARenderPass.hold';
|
||||
|
||||
}
|
||||
|
||||
if ( this.accumulateIndex === - 1 ) {
|
||||
|
||||
super.render( renderer, this.holdRenderTarget, readBuffer, deltaTime );
|
||||
|
||||
this.accumulateIndex = 0;
|
||||
|
||||
}
|
||||
|
||||
const autoClear = renderer.autoClear;
|
||||
renderer.autoClear = false;
|
||||
|
||||
renderer.getClearColor( this._oldClearColor );
|
||||
const oldClearAlpha = renderer.getClearAlpha();
|
||||
|
||||
const sampleWeight = 1.0 / ( jitterOffsets.length );
|
||||
|
||||
if ( this.accumulateIndex >= 0 && this.accumulateIndex < jitterOffsets.length ) {
|
||||
|
||||
this.copyUniforms[ 'opacity' ].value = sampleWeight;
|
||||
this.copyUniforms[ 'tDiffuse' ].value = writeBuffer.texture;
|
||||
|
||||
// render the scene multiple times, each slightly jitter offset from the last and accumulate the results.
|
||||
const numSamplesPerFrame = Math.pow( 2, this.sampleLevel );
|
||||
for ( let i = 0; i < numSamplesPerFrame; i ++ ) {
|
||||
|
||||
const j = this.accumulateIndex;
|
||||
const jitterOffset = jitterOffsets[ j ];
|
||||
|
||||
if ( this.camera.setViewOffset ) {
|
||||
|
||||
this.camera.setViewOffset( readBuffer.width, readBuffer.height,
|
||||
jitterOffset[ 0 ] * 0.0625, jitterOffset[ 1 ] * 0.0625, // 0.0625 = 1 / 16
|
||||
readBuffer.width, readBuffer.height );
|
||||
|
||||
}
|
||||
|
||||
renderer.setRenderTarget( writeBuffer );
|
||||
renderer.setClearColor( this.clearColor, this.clearAlpha );
|
||||
renderer.clear();
|
||||
renderer.render( this.scene, this.camera );
|
||||
|
||||
renderer.setRenderTarget( this.sampleRenderTarget );
|
||||
if ( this.accumulateIndex === 0 ) {
|
||||
|
||||
renderer.setClearColor( 0x000000, 0.0 );
|
||||
renderer.clear();
|
||||
|
||||
}
|
||||
|
||||
this.fsQuad.render( renderer );
|
||||
|
||||
this.accumulateIndex ++;
|
||||
|
||||
if ( this.accumulateIndex >= jitterOffsets.length ) break;
|
||||
|
||||
}
|
||||
|
||||
if ( this.camera.clearViewOffset ) this.camera.clearViewOffset();
|
||||
|
||||
}
|
||||
|
||||
renderer.setClearColor( this.clearColor, this.clearAlpha );
|
||||
const accumulationWeight = this.accumulateIndex * sampleWeight;
|
||||
|
||||
if ( accumulationWeight > 0 ) {
|
||||
|
||||
this.copyUniforms[ 'opacity' ].value = 1.0;
|
||||
this.copyUniforms[ 'tDiffuse' ].value = this.sampleRenderTarget.texture;
|
||||
renderer.setRenderTarget( writeBuffer );
|
||||
renderer.clear();
|
||||
this.fsQuad.render( renderer );
|
||||
|
||||
}
|
||||
|
||||
if ( accumulationWeight < 1.0 ) {
|
||||
|
||||
this.copyUniforms[ 'opacity' ].value = 1.0 - accumulationWeight;
|
||||
this.copyUniforms[ 'tDiffuse' ].value = this.holdRenderTarget.texture;
|
||||
renderer.setRenderTarget( writeBuffer );
|
||||
this.fsQuad.render( renderer );
|
||||
|
||||
}
|
||||
|
||||
renderer.autoClear = autoClear;
|
||||
renderer.setClearColor( this._oldClearColor, oldClearAlpha );
|
||||
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
||||
super.dispose();
|
||||
|
||||
if ( this.holdRenderTarget ) this.holdRenderTarget.dispose();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const _JitterVectors = [
|
||||
[
|
||||
[ 0, 0 ]
|
||||
],
|
||||
[
|
||||
[ 4, 4 ], [ - 4, - 4 ]
|
||||
],
|
||||
[
|
||||
[ - 2, - 6 ], [ 6, - 2 ], [ - 6, 2 ], [ 2, 6 ]
|
||||
],
|
||||
[
|
||||
[ 1, - 3 ], [ - 1, 3 ], [ 5, 1 ], [ - 3, - 5 ],
|
||||
[ - 5, 5 ], [ - 7, - 1 ], [ 3, 7 ], [ 7, - 7 ]
|
||||
],
|
||||
[
|
||||
[ 1, 1 ], [ - 1, - 3 ], [ - 3, 2 ], [ 4, - 1 ],
|
||||
[ - 5, - 2 ], [ 2, 5 ], [ 5, 3 ], [ 3, - 5 ],
|
||||
[ - 2, 6 ], [ 0, - 7 ], [ - 4, - 6 ], [ - 6, 4 ],
|
||||
[ - 8, 0 ], [ 7, - 4 ], [ 6, 7 ], [ - 7, - 8 ]
|
||||
],
|
||||
[
|
||||
[ - 4, - 7 ], [ - 7, - 5 ], [ - 3, - 5 ], [ - 5, - 4 ],
|
||||
[ - 1, - 4 ], [ - 2, - 2 ], [ - 6, - 1 ], [ - 4, 0 ],
|
||||
[ - 7, 1 ], [ - 1, 2 ], [ - 6, 3 ], [ - 3, 3 ],
|
||||
[ - 7, 6 ], [ - 3, 6 ], [ - 5, 7 ], [ - 1, 7 ],
|
||||
[ 5, - 7 ], [ 1, - 6 ], [ 6, - 5 ], [ 4, - 4 ],
|
||||
[ 2, - 3 ], [ 7, - 2 ], [ 1, - 1 ], [ 4, - 1 ],
|
||||
[ 2, 1 ], [ 6, 2 ], [ 0, 4 ], [ 4, 4 ],
|
||||
[ 2, 5 ], [ 7, 5 ], [ 5, 6 ], [ 3, 7 ]
|
||||
]
|
||||
];
|
||||
|
||||
export { TAARenderPass };
|
67
static/sdk/three/jsm/postprocessing/TexturePass.js
Normal file
67
static/sdk/three/jsm/postprocessing/TexturePass.js
Normal file
@ -0,0 +1,67 @@
|
||||
import {
|
||||
ShaderMaterial,
|
||||
UniformsUtils
|
||||
} from 'three';
|
||||
import { Pass, FullScreenQuad } from './Pass.js';
|
||||
import { CopyShader } from '../shaders/CopyShader.js';
|
||||
|
||||
class TexturePass extends Pass {
|
||||
|
||||
constructor( map, opacity ) {
|
||||
|
||||
super();
|
||||
|
||||
const shader = CopyShader;
|
||||
|
||||
this.map = map;
|
||||
this.opacity = ( opacity !== undefined ) ? opacity : 1.0;
|
||||
|
||||
this.uniforms = UniformsUtils.clone( shader.uniforms );
|
||||
|
||||
this.material = new ShaderMaterial( {
|
||||
|
||||
uniforms: this.uniforms,
|
||||
vertexShader: shader.vertexShader,
|
||||
fragmentShader: shader.fragmentShader,
|
||||
depthTest: false,
|
||||
depthWrite: false,
|
||||
premultipliedAlpha: true
|
||||
|
||||
} );
|
||||
|
||||
this.needsSwap = false;
|
||||
|
||||
this.fsQuad = new FullScreenQuad( null );
|
||||
|
||||
}
|
||||
|
||||
render( renderer, writeBuffer, readBuffer /*, deltaTime, maskActive */ ) {
|
||||
|
||||
const oldAutoClear = renderer.autoClear;
|
||||
renderer.autoClear = false;
|
||||
|
||||
this.fsQuad.material = this.material;
|
||||
|
||||
this.uniforms[ 'opacity' ].value = this.opacity;
|
||||
this.uniforms[ 'tDiffuse' ].value = this.map;
|
||||
this.material.transparent = ( this.opacity < 1.0 );
|
||||
|
||||
renderer.setRenderTarget( this.renderToScreen ? null : readBuffer );
|
||||
if ( this.clear ) renderer.clear();
|
||||
this.fsQuad.render( renderer );
|
||||
|
||||
renderer.autoClear = oldAutoClear;
|
||||
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
||||
this.material.dispose();
|
||||
|
||||
this.fsQuad.dispose();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { TexturePass };
|
415
static/sdk/three/jsm/postprocessing/UnrealBloomPass.js
Normal file
415
static/sdk/three/jsm/postprocessing/UnrealBloomPass.js
Normal file
@ -0,0 +1,415 @@
|
||||
import {
|
||||
AdditiveBlending,
|
||||
Color,
|
||||
HalfFloatType,
|
||||
MeshBasicMaterial,
|
||||
ShaderMaterial,
|
||||
UniformsUtils,
|
||||
Vector2,
|
||||
Vector3,
|
||||
WebGLRenderTarget
|
||||
} from 'three';
|
||||
import { Pass, FullScreenQuad } from './Pass.js';
|
||||
import { CopyShader } from '../shaders/CopyShader.js';
|
||||
import { LuminosityHighPassShader } from '../shaders/LuminosityHighPassShader.js';
|
||||
|
||||
/**
|
||||
* UnrealBloomPass is inspired by the bloom pass of Unreal Engine. It creates a
|
||||
* mip map chain of bloom textures and blurs them with different radii. Because
|
||||
* of the weighted combination of mips, and because larger blurs are done on
|
||||
* higher mips, this effect provides good quality and performance.
|
||||
*
|
||||
* Reference:
|
||||
* - https://docs.unrealengine.com/latest/INT/Engine/Rendering/PostProcessEffects/Bloom/
|
||||
*/
|
||||
class UnrealBloomPass extends Pass {
|
||||
|
||||
constructor( resolution, strength, radius, threshold ) {
|
||||
|
||||
super();
|
||||
|
||||
this.strength = ( strength !== undefined ) ? strength : 1;
|
||||
this.radius = radius;
|
||||
this.threshold = threshold;
|
||||
this.resolution = ( resolution !== undefined ) ? new Vector2( resolution.x, resolution.y ) : new Vector2( 256, 256 );
|
||||
|
||||
// create color only once here, reuse it later inside the render function
|
||||
this.clearColor = new Color( 0, 0, 0 );
|
||||
|
||||
// render targets
|
||||
this.renderTargetsHorizontal = [];
|
||||
this.renderTargetsVertical = [];
|
||||
this.nMips = 5;
|
||||
let resx = Math.round( this.resolution.x / 2 );
|
||||
let resy = Math.round( this.resolution.y / 2 );
|
||||
|
||||
this.renderTargetBright = new WebGLRenderTarget( resx, resy, { type: HalfFloatType } );
|
||||
this.renderTargetBright.texture.name = 'UnrealBloomPass.bright';
|
||||
this.renderTargetBright.texture.generateMipmaps = false;
|
||||
|
||||
for ( let i = 0; i < this.nMips; i ++ ) {
|
||||
|
||||
const renderTargetHorizonal = new WebGLRenderTarget( resx, resy, { type: HalfFloatType } );
|
||||
|
||||
renderTargetHorizonal.texture.name = 'UnrealBloomPass.h' + i;
|
||||
renderTargetHorizonal.texture.generateMipmaps = false;
|
||||
|
||||
this.renderTargetsHorizontal.push( renderTargetHorizonal );
|
||||
|
||||
const renderTargetVertical = new WebGLRenderTarget( resx, resy, { type: HalfFloatType } );
|
||||
|
||||
renderTargetVertical.texture.name = 'UnrealBloomPass.v' + i;
|
||||
renderTargetVertical.texture.generateMipmaps = false;
|
||||
|
||||
this.renderTargetsVertical.push( renderTargetVertical );
|
||||
|
||||
resx = Math.round( resx / 2 );
|
||||
|
||||
resy = Math.round( resy / 2 );
|
||||
|
||||
}
|
||||
|
||||
// luminosity high pass material
|
||||
|
||||
const highPassShader = LuminosityHighPassShader;
|
||||
this.highPassUniforms = UniformsUtils.clone( highPassShader.uniforms );
|
||||
|
||||
this.highPassUniforms[ 'luminosityThreshold' ].value = threshold;
|
||||
this.highPassUniforms[ 'smoothWidth' ].value = 0.01;
|
||||
|
||||
this.materialHighPassFilter = new ShaderMaterial( {
|
||||
uniforms: this.highPassUniforms,
|
||||
vertexShader: highPassShader.vertexShader,
|
||||
fragmentShader: highPassShader.fragmentShader
|
||||
} );
|
||||
|
||||
// gaussian blur materials
|
||||
|
||||
this.separableBlurMaterials = [];
|
||||
const kernelSizeArray = [ 3, 5, 7, 9, 11 ];
|
||||
resx = Math.round( this.resolution.x / 2 );
|
||||
resy = Math.round( this.resolution.y / 2 );
|
||||
|
||||
for ( let i = 0; i < this.nMips; i ++ ) {
|
||||
|
||||
this.separableBlurMaterials.push( this.getSeperableBlurMaterial( kernelSizeArray[ i ] ) );
|
||||
|
||||
this.separableBlurMaterials[ i ].uniforms[ 'invSize' ].value = new Vector2( 1 / resx, 1 / resy );
|
||||
|
||||
resx = Math.round( resx / 2 );
|
||||
|
||||
resy = Math.round( resy / 2 );
|
||||
|
||||
}
|
||||
|
||||
// composite material
|
||||
|
||||
this.compositeMaterial = this.getCompositeMaterial( this.nMips );
|
||||
this.compositeMaterial.uniforms[ 'blurTexture1' ].value = this.renderTargetsVertical[ 0 ].texture;
|
||||
this.compositeMaterial.uniforms[ 'blurTexture2' ].value = this.renderTargetsVertical[ 1 ].texture;
|
||||
this.compositeMaterial.uniforms[ 'blurTexture3' ].value = this.renderTargetsVertical[ 2 ].texture;
|
||||
this.compositeMaterial.uniforms[ 'blurTexture4' ].value = this.renderTargetsVertical[ 3 ].texture;
|
||||
this.compositeMaterial.uniforms[ 'blurTexture5' ].value = this.renderTargetsVertical[ 4 ].texture;
|
||||
this.compositeMaterial.uniforms[ 'bloomStrength' ].value = strength;
|
||||
this.compositeMaterial.uniforms[ 'bloomRadius' ].value = 0.1;
|
||||
|
||||
const bloomFactors = [ 1.0, 0.8, 0.6, 0.4, 0.2 ];
|
||||
this.compositeMaterial.uniforms[ 'bloomFactors' ].value = bloomFactors;
|
||||
this.bloomTintColors = [ new Vector3( 1, 1, 1 ), new Vector3( 1, 1, 1 ), new Vector3( 1, 1, 1 ), new Vector3( 1, 1, 1 ), new Vector3( 1, 1, 1 ) ];
|
||||
this.compositeMaterial.uniforms[ 'bloomTintColors' ].value = this.bloomTintColors;
|
||||
|
||||
// blend material
|
||||
|
||||
const copyShader = CopyShader;
|
||||
|
||||
this.copyUniforms = UniformsUtils.clone( copyShader.uniforms );
|
||||
|
||||
this.blendMaterial = new ShaderMaterial( {
|
||||
uniforms: this.copyUniforms,
|
||||
vertexShader: copyShader.vertexShader,
|
||||
fragmentShader: copyShader.fragmentShader,
|
||||
blending: AdditiveBlending,
|
||||
depthTest: false,
|
||||
depthWrite: false,
|
||||
transparent: true
|
||||
} );
|
||||
|
||||
this.enabled = true;
|
||||
this.needsSwap = false;
|
||||
|
||||
this._oldClearColor = new Color();
|
||||
this.oldClearAlpha = 1;
|
||||
|
||||
this.basic = new MeshBasicMaterial();
|
||||
|
||||
this.fsQuad = new FullScreenQuad( null );
|
||||
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
||||
for ( let i = 0; i < this.renderTargetsHorizontal.length; i ++ ) {
|
||||
|
||||
this.renderTargetsHorizontal[ i ].dispose();
|
||||
|
||||
}
|
||||
|
||||
for ( let i = 0; i < this.renderTargetsVertical.length; i ++ ) {
|
||||
|
||||
this.renderTargetsVertical[ i ].dispose();
|
||||
|
||||
}
|
||||
|
||||
this.renderTargetBright.dispose();
|
||||
|
||||
//
|
||||
|
||||
for ( let i = 0; i < this.separableBlurMaterials.length; i ++ ) {
|
||||
|
||||
this.separableBlurMaterials[ i ].dispose();
|
||||
|
||||
}
|
||||
|
||||
this.compositeMaterial.dispose();
|
||||
this.blendMaterial.dispose();
|
||||
this.basic.dispose();
|
||||
|
||||
//
|
||||
|
||||
this.fsQuad.dispose();
|
||||
|
||||
}
|
||||
|
||||
setSize( width, height ) {
|
||||
|
||||
let resx = Math.round( width / 2 );
|
||||
let resy = Math.round( height / 2 );
|
||||
|
||||
this.renderTargetBright.setSize( resx, resy );
|
||||
|
||||
for ( let i = 0; i < this.nMips; i ++ ) {
|
||||
|
||||
this.renderTargetsHorizontal[ i ].setSize( resx, resy );
|
||||
this.renderTargetsVertical[ i ].setSize( resx, resy );
|
||||
|
||||
this.separableBlurMaterials[ i ].uniforms[ 'invSize' ].value = new Vector2( 1 / resx, 1 / resy );
|
||||
|
||||
resx = Math.round( resx / 2 );
|
||||
resy = Math.round( resy / 2 );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
render( renderer, writeBuffer, readBuffer, deltaTime, maskActive ) {
|
||||
|
||||
renderer.getClearColor( this._oldClearColor );
|
||||
this.oldClearAlpha = renderer.getClearAlpha();
|
||||
const oldAutoClear = renderer.autoClear;
|
||||
renderer.autoClear = false;
|
||||
|
||||
renderer.setClearColor( this.clearColor, 0 );
|
||||
|
||||
if ( maskActive ) renderer.state.buffers.stencil.setTest( false );
|
||||
|
||||
// Render input to screen
|
||||
|
||||
if ( this.renderToScreen ) {
|
||||
|
||||
this.fsQuad.material = this.basic;
|
||||
this.basic.map = readBuffer.texture;
|
||||
|
||||
renderer.setRenderTarget( null );
|
||||
renderer.clear();
|
||||
this.fsQuad.render( renderer );
|
||||
|
||||
}
|
||||
|
||||
// 1. Extract Bright Areas
|
||||
|
||||
this.highPassUniforms[ 'tDiffuse' ].value = readBuffer.texture;
|
||||
this.highPassUniforms[ 'luminosityThreshold' ].value = this.threshold;
|
||||
this.fsQuad.material = this.materialHighPassFilter;
|
||||
|
||||
renderer.setRenderTarget( this.renderTargetBright );
|
||||
renderer.clear();
|
||||
this.fsQuad.render( renderer );
|
||||
|
||||
// 2. Blur All the mips progressively
|
||||
|
||||
let inputRenderTarget = this.renderTargetBright;
|
||||
|
||||
for ( let i = 0; i < this.nMips; i ++ ) {
|
||||
|
||||
this.fsQuad.material = this.separableBlurMaterials[ i ];
|
||||
|
||||
this.separableBlurMaterials[ i ].uniforms[ 'colorTexture' ].value = inputRenderTarget.texture;
|
||||
this.separableBlurMaterials[ i ].uniforms[ 'direction' ].value = UnrealBloomPass.BlurDirectionX;
|
||||
renderer.setRenderTarget( this.renderTargetsHorizontal[ i ] );
|
||||
renderer.clear();
|
||||
this.fsQuad.render( renderer );
|
||||
|
||||
this.separableBlurMaterials[ i ].uniforms[ 'colorTexture' ].value = this.renderTargetsHorizontal[ i ].texture;
|
||||
this.separableBlurMaterials[ i ].uniforms[ 'direction' ].value = UnrealBloomPass.BlurDirectionY;
|
||||
renderer.setRenderTarget( this.renderTargetsVertical[ i ] );
|
||||
renderer.clear();
|
||||
this.fsQuad.render( renderer );
|
||||
|
||||
inputRenderTarget = this.renderTargetsVertical[ i ];
|
||||
|
||||
}
|
||||
|
||||
// Composite All the mips
|
||||
|
||||
this.fsQuad.material = this.compositeMaterial;
|
||||
this.compositeMaterial.uniforms[ 'bloomStrength' ].value = this.strength;
|
||||
this.compositeMaterial.uniforms[ 'bloomRadius' ].value = this.radius;
|
||||
this.compositeMaterial.uniforms[ 'bloomTintColors' ].value = this.bloomTintColors;
|
||||
|
||||
renderer.setRenderTarget( this.renderTargetsHorizontal[ 0 ] );
|
||||
renderer.clear();
|
||||
this.fsQuad.render( renderer );
|
||||
|
||||
// Blend it additively over the input texture
|
||||
|
||||
this.fsQuad.material = this.blendMaterial;
|
||||
this.copyUniforms[ 'tDiffuse' ].value = this.renderTargetsHorizontal[ 0 ].texture;
|
||||
|
||||
if ( maskActive ) renderer.state.buffers.stencil.setTest( true );
|
||||
|
||||
if ( this.renderToScreen ) {
|
||||
|
||||
renderer.setRenderTarget( null );
|
||||
this.fsQuad.render( renderer );
|
||||
|
||||
} else {
|
||||
|
||||
renderer.setRenderTarget( readBuffer );
|
||||
this.fsQuad.render( renderer );
|
||||
|
||||
}
|
||||
|
||||
// Restore renderer settings
|
||||
|
||||
renderer.setClearColor( this._oldClearColor, this.oldClearAlpha );
|
||||
renderer.autoClear = oldAutoClear;
|
||||
|
||||
}
|
||||
|
||||
getSeperableBlurMaterial( kernelRadius ) {
|
||||
|
||||
const coefficients = [];
|
||||
|
||||
for ( let i = 0; i < kernelRadius; i ++ ) {
|
||||
|
||||
coefficients.push( 0.39894 * Math.exp( - 0.5 * i * i / ( kernelRadius * kernelRadius ) ) / kernelRadius );
|
||||
|
||||
}
|
||||
|
||||
return new ShaderMaterial( {
|
||||
|
||||
defines: {
|
||||
'KERNEL_RADIUS': kernelRadius
|
||||
},
|
||||
|
||||
uniforms: {
|
||||
'colorTexture': { value: null },
|
||||
'invSize': { value: new Vector2( 0.5, 0.5 ) }, // inverse texture size
|
||||
'direction': { value: new Vector2( 0.5, 0.5 ) },
|
||||
'gaussianCoefficients': { value: coefficients } // precomputed Gaussian coefficients
|
||||
},
|
||||
|
||||
vertexShader:
|
||||
`varying vec2 vUv;
|
||||
void main() {
|
||||
vUv = uv;
|
||||
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
|
||||
}`,
|
||||
|
||||
fragmentShader:
|
||||
`#include <common>
|
||||
varying vec2 vUv;
|
||||
uniform sampler2D colorTexture;
|
||||
uniform vec2 invSize;
|
||||
uniform vec2 direction;
|
||||
uniform float gaussianCoefficients[KERNEL_RADIUS];
|
||||
|
||||
void main() {
|
||||
float weightSum = gaussianCoefficients[0];
|
||||
vec3 diffuseSum = texture2D( colorTexture, vUv ).rgb * weightSum;
|
||||
for( int i = 1; i < KERNEL_RADIUS; i ++ ) {
|
||||
float x = float(i);
|
||||
float w = gaussianCoefficients[i];
|
||||
vec2 uvOffset = direction * invSize * x;
|
||||
vec3 sample1 = texture2D( colorTexture, vUv + uvOffset ).rgb;
|
||||
vec3 sample2 = texture2D( colorTexture, vUv - uvOffset ).rgb;
|
||||
diffuseSum += (sample1 + sample2) * w;
|
||||
weightSum += 2.0 * w;
|
||||
}
|
||||
gl_FragColor = vec4(diffuseSum/weightSum, 1.0);
|
||||
}`
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
getCompositeMaterial( nMips ) {
|
||||
|
||||
return new ShaderMaterial( {
|
||||
|
||||
defines: {
|
||||
'NUM_MIPS': nMips
|
||||
},
|
||||
|
||||
uniforms: {
|
||||
'blurTexture1': { value: null },
|
||||
'blurTexture2': { value: null },
|
||||
'blurTexture3': { value: null },
|
||||
'blurTexture4': { value: null },
|
||||
'blurTexture5': { value: null },
|
||||
'bloomStrength': { value: 1.0 },
|
||||
'bloomFactors': { value: null },
|
||||
'bloomTintColors': { value: null },
|
||||
'bloomRadius': { value: 0.0 }
|
||||
},
|
||||
|
||||
vertexShader:
|
||||
`varying vec2 vUv;
|
||||
void main() {
|
||||
vUv = uv;
|
||||
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
|
||||
}`,
|
||||
|
||||
fragmentShader:
|
||||
`varying vec2 vUv;
|
||||
uniform sampler2D blurTexture1;
|
||||
uniform sampler2D blurTexture2;
|
||||
uniform sampler2D blurTexture3;
|
||||
uniform sampler2D blurTexture4;
|
||||
uniform sampler2D blurTexture5;
|
||||
uniform float bloomStrength;
|
||||
uniform float bloomRadius;
|
||||
uniform float bloomFactors[NUM_MIPS];
|
||||
uniform vec3 bloomTintColors[NUM_MIPS];
|
||||
|
||||
float lerpBloomFactor(const in float factor) {
|
||||
float mirrorFactor = 1.2 - factor;
|
||||
return mix(factor, mirrorFactor, bloomRadius);
|
||||
}
|
||||
|
||||
void main() {
|
||||
gl_FragColor = bloomStrength * ( lerpBloomFactor(bloomFactors[0]) * vec4(bloomTintColors[0], 1.0) * texture2D(blurTexture1, vUv) +
|
||||
lerpBloomFactor(bloomFactors[1]) * vec4(bloomTintColors[1], 1.0) * texture2D(blurTexture2, vUv) +
|
||||
lerpBloomFactor(bloomFactors[2]) * vec4(bloomTintColors[2], 1.0) * texture2D(blurTexture3, vUv) +
|
||||
lerpBloomFactor(bloomFactors[3]) * vec4(bloomTintColors[3], 1.0) * texture2D(blurTexture4, vUv) +
|
||||
lerpBloomFactor(bloomFactors[4]) * vec4(bloomTintColors[4], 1.0) * texture2D(blurTexture5, vUv) );
|
||||
}`
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
UnrealBloomPass.BlurDirectionX = new Vector2( 1.0, 0.0 );
|
||||
UnrealBloomPass.BlurDirectionY = new Vector2( 0.0, 1.0 );
|
||||
|
||||
export { UnrealBloomPass };
|
Reference in New Issue
Block a user