feat(3d): integrate ZJB scene with project context
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016-2025 Arseny Kapoulkine
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,21 @@
|
||||
The MIT License
|
||||
|
||||
Copyright © 2010-2025 three.js authors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
+1860
File diff suppressed because it is too large
Load Diff
+182
@@ -0,0 +1,182 @@
|
||||
import {
|
||||
BackSide,
|
||||
BoxGeometry,
|
||||
InstancedMesh,
|
||||
Mesh,
|
||||
MeshLambertMaterial,
|
||||
MeshStandardMaterial,
|
||||
PointLight,
|
||||
Scene,
|
||||
Object3D,
|
||||
} from 'three';
|
||||
|
||||
/**
|
||||
* This class represents a scene with a basic room setup that can be used as
|
||||
* input for {@link PMREMGenerator#fromScene}. The resulting PMREM represents the room's
|
||||
* lighting and can be used for Image Based Lighting by assigning it to {@link Scene#environment}
|
||||
* or directly as an environment map to PBR materials.
|
||||
*
|
||||
* The implementation is based on the [EnvironmentScene](https://github.com/google/model-viewer/blob/master/packages/model-viewer/src/three-components/EnvironmentScene.ts)
|
||||
* component from the `model-viewer` project.
|
||||
*
|
||||
* ```js
|
||||
* const environment = new RoomEnvironment();
|
||||
* const pmremGenerator = new THREE.PMREMGenerator( renderer );
|
||||
*
|
||||
* const envMap = pmremGenerator.fromScene( environment ).texture;
|
||||
* scene.environment = envMap;
|
||||
* ```
|
||||
*
|
||||
* @augments Scene
|
||||
* @three_import import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js';
|
||||
*/
|
||||
class RoomEnvironment extends Scene {
|
||||
|
||||
constructor() {
|
||||
|
||||
super();
|
||||
|
||||
const geometry = new BoxGeometry();
|
||||
geometry.deleteAttribute( 'uv' );
|
||||
|
||||
const roomMaterial = new MeshStandardMaterial( { side: BackSide } );
|
||||
const boxMaterial = new MeshStandardMaterial();
|
||||
|
||||
const mainLight = new PointLight( 0xffffff, 900, 28, 2 );
|
||||
mainLight.position.set( 0.418, 16.199, 0.300 );
|
||||
this.add( mainLight );
|
||||
|
||||
const room = new Mesh( geometry, roomMaterial );
|
||||
room.position.set( - 0.757, 13.219, 0.717 );
|
||||
room.scale.set( 31.713, 28.305, 28.591 );
|
||||
this.add( room );
|
||||
|
||||
const boxes = new InstancedMesh( geometry, boxMaterial, 6 );
|
||||
const transform = new Object3D();
|
||||
|
||||
// box1
|
||||
transform.position.set( - 10.906, 2.009, 1.846 );
|
||||
transform.rotation.set( 0, - 0.195, 0 );
|
||||
transform.scale.set( 2.328, 7.905, 4.651 );
|
||||
transform.updateMatrix();
|
||||
boxes.setMatrixAt( 0, transform.matrix );
|
||||
|
||||
// box2
|
||||
transform.position.set( - 5.607, - 0.754, - 0.758 );
|
||||
transform.rotation.set( 0, 0.994, 0 );
|
||||
transform.scale.set( 1.970, 1.534, 3.955 );
|
||||
transform.updateMatrix();
|
||||
boxes.setMatrixAt( 1, transform.matrix );
|
||||
|
||||
// box3
|
||||
transform.position.set( 6.167, 0.857, 7.803 );
|
||||
transform.rotation.set( 0, 0.561, 0 );
|
||||
transform.scale.set( 3.927, 6.285, 3.687 );
|
||||
transform.updateMatrix();
|
||||
boxes.setMatrixAt( 2, transform.matrix );
|
||||
|
||||
// box4
|
||||
transform.position.set( - 2.017, 0.018, 6.124 );
|
||||
transform.rotation.set( 0, 0.333, 0 );
|
||||
transform.scale.set( 2.002, 4.566, 2.064 );
|
||||
transform.updateMatrix();
|
||||
boxes.setMatrixAt( 3, transform.matrix );
|
||||
|
||||
// box5
|
||||
transform.position.set( 2.291, - 0.756, - 2.621 );
|
||||
transform.rotation.set( 0, - 0.286, 0 );
|
||||
transform.scale.set( 1.546, 1.552, 1.496 );
|
||||
transform.updateMatrix();
|
||||
boxes.setMatrixAt( 4, transform.matrix );
|
||||
|
||||
// box6
|
||||
transform.position.set( - 2.193, - 0.369, - 5.547 );
|
||||
transform.rotation.set( 0, 0.516, 0 );
|
||||
transform.scale.set( 3.875, 3.487, 2.986 );
|
||||
transform.updateMatrix();
|
||||
boxes.setMatrixAt( 5, transform.matrix );
|
||||
|
||||
this.add( boxes );
|
||||
|
||||
|
||||
// -x right
|
||||
const light1 = new Mesh( geometry, createAreaLightMaterial( 50 ) );
|
||||
light1.position.set( - 16.116, 14.37, 8.208 );
|
||||
light1.scale.set( 0.1, 2.428, 2.739 );
|
||||
this.add( light1 );
|
||||
|
||||
// -x left
|
||||
const light2 = new Mesh( geometry, createAreaLightMaterial( 50 ) );
|
||||
light2.position.set( - 16.109, 18.021, - 8.207 );
|
||||
light2.scale.set( 0.1, 2.425, 2.751 );
|
||||
this.add( light2 );
|
||||
|
||||
// +x
|
||||
const light3 = new Mesh( geometry, createAreaLightMaterial( 17 ) );
|
||||
light3.position.set( 14.904, 12.198, - 1.832 );
|
||||
light3.scale.set( 0.15, 4.265, 6.331 );
|
||||
this.add( light3 );
|
||||
|
||||
// +z
|
||||
const light4 = new Mesh( geometry, createAreaLightMaterial( 43 ) );
|
||||
light4.position.set( - 0.462, 8.89, 14.520 );
|
||||
light4.scale.set( 4.38, 5.441, 0.088 );
|
||||
this.add( light4 );
|
||||
|
||||
// -z
|
||||
const light5 = new Mesh( geometry, createAreaLightMaterial( 20 ) );
|
||||
light5.position.set( 3.235, 11.486, - 12.541 );
|
||||
light5.scale.set( 2.5, 2.0, 0.1 );
|
||||
this.add( light5 );
|
||||
|
||||
// +y
|
||||
const light6 = new Mesh( geometry, createAreaLightMaterial( 100 ) );
|
||||
light6.position.set( 0.0, 20.0, 0.0 );
|
||||
light6.scale.set( 1.0, 0.1, 1.0 );
|
||||
this.add( light6 );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Frees internal resources. This method should be called
|
||||
* when the environment is no longer required.
|
||||
*/
|
||||
dispose() {
|
||||
|
||||
const resources = new Set();
|
||||
|
||||
this.traverse( ( object ) => {
|
||||
|
||||
if ( object.isMesh ) {
|
||||
|
||||
resources.add( object.geometry );
|
||||
resources.add( object.material );
|
||||
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
for ( const resource of resources ) {
|
||||
|
||||
resource.dispose();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function createAreaLightMaterial( intensity ) {
|
||||
|
||||
// create an emissive-only material. see #31348
|
||||
const material = new MeshLambertMaterial( {
|
||||
color: 0x000000,
|
||||
emissive: 0xffffff,
|
||||
emissiveIntensity: intensity
|
||||
} );
|
||||
|
||||
return material;
|
||||
|
||||
}
|
||||
|
||||
export { RoomEnvironment };
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,470 @@
|
||||
/**
|
||||
* A utility class providing noise functions.
|
||||
*
|
||||
* The code is based on [Simplex noise demystified]{@link https://web.archive.org/web/20210210162332/http://staffwww.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf}
|
||||
* by Stefan Gustavson, 2005.
|
||||
*
|
||||
* @three_import import { SimplexNoise } from 'three/addons/math/SimplexNoise.js';
|
||||
*/
|
||||
class SimplexNoise {
|
||||
|
||||
/**
|
||||
* Constructs a new simplex noise object.
|
||||
*
|
||||
* @param {Object} [r=Math] - A math utility class that holds a `random()` method. This makes it
|
||||
* possible to pass in custom random number generator.
|
||||
*/
|
||||
constructor( r = Math ) {
|
||||
|
||||
this.grad3 = [[ 1, 1, 0 ], [ - 1, 1, 0 ], [ 1, - 1, 0 ], [ - 1, - 1, 0 ],
|
||||
[ 1, 0, 1 ], [ - 1, 0, 1 ], [ 1, 0, - 1 ], [ - 1, 0, - 1 ],
|
||||
[ 0, 1, 1 ], [ 0, - 1, 1 ], [ 0, 1, - 1 ], [ 0, - 1, - 1 ]];
|
||||
|
||||
this.grad4 = [[ 0, 1, 1, 1 ], [ 0, 1, 1, - 1 ], [ 0, 1, - 1, 1 ], [ 0, 1, - 1, - 1 ],
|
||||
[ 0, - 1, 1, 1 ], [ 0, - 1, 1, - 1 ], [ 0, - 1, - 1, 1 ], [ 0, - 1, - 1, - 1 ],
|
||||
[ 1, 0, 1, 1 ], [ 1, 0, 1, - 1 ], [ 1, 0, - 1, 1 ], [ 1, 0, - 1, - 1 ],
|
||||
[ - 1, 0, 1, 1 ], [ - 1, 0, 1, - 1 ], [ - 1, 0, - 1, 1 ], [ - 1, 0, - 1, - 1 ],
|
||||
[ 1, 1, 0, 1 ], [ 1, 1, 0, - 1 ], [ 1, - 1, 0, 1 ], [ 1, - 1, 0, - 1 ],
|
||||
[ - 1, 1, 0, 1 ], [ - 1, 1, 0, - 1 ], [ - 1, - 1, 0, 1 ], [ - 1, - 1, 0, - 1 ],
|
||||
[ 1, 1, 1, 0 ], [ 1, 1, - 1, 0 ], [ 1, - 1, 1, 0 ], [ 1, - 1, - 1, 0 ],
|
||||
[ - 1, 1, 1, 0 ], [ - 1, 1, - 1, 0 ], [ - 1, - 1, 1, 0 ], [ - 1, - 1, - 1, 0 ]];
|
||||
|
||||
this.p = [];
|
||||
|
||||
for ( let i = 0; i < 256; i ++ ) {
|
||||
|
||||
this.p[ i ] = Math.floor( r.random() * 256 );
|
||||
|
||||
}
|
||||
|
||||
// To remove the need for index wrapping, double the permutation table length
|
||||
this.perm = [];
|
||||
|
||||
for ( let i = 0; i < 512; i ++ ) {
|
||||
|
||||
this.perm[ i ] = this.p[ i & 255 ];
|
||||
|
||||
}
|
||||
|
||||
// A lookup table to traverse the simplex around a given point in 4D.
|
||||
// Details can be found where this table is used, in the 4D noise method.
|
||||
this.simplex = [
|
||||
[ 0, 1, 2, 3 ], [ 0, 1, 3, 2 ], [ 0, 0, 0, 0 ], [ 0, 2, 3, 1 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 1, 2, 3, 0 ],
|
||||
[ 0, 2, 1, 3 ], [ 0, 0, 0, 0 ], [ 0, 3, 1, 2 ], [ 0, 3, 2, 1 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 1, 3, 2, 0 ],
|
||||
[ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ],
|
||||
[ 1, 2, 0, 3 ], [ 0, 0, 0, 0 ], [ 1, 3, 0, 2 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 2, 3, 0, 1 ], [ 2, 3, 1, 0 ],
|
||||
[ 1, 0, 2, 3 ], [ 1, 0, 3, 2 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 2, 0, 3, 1 ], [ 0, 0, 0, 0 ], [ 2, 1, 3, 0 ],
|
||||
[ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ],
|
||||
[ 2, 0, 1, 3 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 3, 0, 1, 2 ], [ 3, 0, 2, 1 ], [ 0, 0, 0, 0 ], [ 3, 1, 2, 0 ],
|
||||
[ 2, 1, 0, 3 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 3, 1, 0, 2 ], [ 0, 0, 0, 0 ], [ 3, 2, 0, 1 ], [ 3, 2, 1, 0 ]];
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A 2D simplex noise method.
|
||||
*
|
||||
* @param {number} xin - The x coordinate.
|
||||
* @param {number} yin - The y coordinate.
|
||||
* @return {number} The noise value.
|
||||
*/
|
||||
noise( xin, yin ) {
|
||||
|
||||
let n0; // Noise contributions from the three corners
|
||||
let n1;
|
||||
let n2;
|
||||
// Skew the input space to determine which simplex cell we're in
|
||||
const F2 = 0.5 * ( Math.sqrt( 3.0 ) - 1.0 );
|
||||
const s = ( xin + yin ) * F2; // Hairy factor for 2D
|
||||
const i = Math.floor( xin + s );
|
||||
const j = Math.floor( yin + s );
|
||||
const G2 = ( 3.0 - Math.sqrt( 3.0 ) ) / 6.0;
|
||||
const t = ( i + j ) * G2;
|
||||
const X0 = i - t; // Unskew the cell origin back to (x,y) space
|
||||
const Y0 = j - t;
|
||||
const x0 = xin - X0; // The x,y distances from the cell origin
|
||||
const y0 = yin - Y0;
|
||||
|
||||
// For the 2D case, the simplex shape is an equilateral triangle.
|
||||
// Determine which simplex we are in.
|
||||
let i1; // Offsets for second (middle) corner of simplex in (i,j) coords
|
||||
|
||||
let j1;
|
||||
if ( x0 > y0 ) {
|
||||
|
||||
i1 = 1; j1 = 0;
|
||||
|
||||
// lower triangle, XY order: (0,0)->(1,0)->(1,1)
|
||||
|
||||
} else {
|
||||
|
||||
i1 = 0; j1 = 1;
|
||||
|
||||
} // upper triangle, YX order: (0,0)->(0,1)->(1,1)
|
||||
|
||||
// A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and
|
||||
// a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where
|
||||
// c = (3-sqrt(3))/6
|
||||
const x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords
|
||||
const y1 = y0 - j1 + G2;
|
||||
const x2 = x0 - 1.0 + 2.0 * G2; // Offsets for last corner in (x,y) unskewed coords
|
||||
const y2 = y0 - 1.0 + 2.0 * G2;
|
||||
// Work out the hashed gradient indices of the three simplex corners
|
||||
const ii = i & 255;
|
||||
const jj = j & 255;
|
||||
const gi0 = this.perm[ ii + this.perm[ jj ] ] % 12;
|
||||
const gi1 = this.perm[ ii + i1 + this.perm[ jj + j1 ] ] % 12;
|
||||
const gi2 = this.perm[ ii + 1 + this.perm[ jj + 1 ] ] % 12;
|
||||
// Calculate the contribution from the three corners
|
||||
let t0 = 0.5 - x0 * x0 - y0 * y0;
|
||||
if ( t0 < 0 ) n0 = 0.0;
|
||||
else {
|
||||
|
||||
t0 *= t0;
|
||||
n0 = t0 * t0 * this._dot( this.grad3[ gi0 ], x0, y0 ); // (x,y) of grad3 used for 2D gradient
|
||||
|
||||
}
|
||||
|
||||
let t1 = 0.5 - x1 * x1 - y1 * y1;
|
||||
if ( t1 < 0 ) n1 = 0.0;
|
||||
else {
|
||||
|
||||
t1 *= t1;
|
||||
n1 = t1 * t1 * this._dot( this.grad3[ gi1 ], x1, y1 );
|
||||
|
||||
}
|
||||
|
||||
let t2 = 0.5 - x2 * x2 - y2 * y2;
|
||||
if ( t2 < 0 ) n2 = 0.0;
|
||||
else {
|
||||
|
||||
t2 *= t2;
|
||||
n2 = t2 * t2 * this._dot( this.grad3[ gi2 ], x2, y2 );
|
||||
|
||||
}
|
||||
|
||||
// Add contributions from each corner to get the final noise value.
|
||||
// The result is scaled to return values in the interval [-1,1].
|
||||
return 70.0 * ( n0 + n1 + n2 );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A 3D simplex noise method.
|
||||
*
|
||||
* @param {number} xin - The x coordinate.
|
||||
* @param {number} yin - The y coordinate.
|
||||
* @param {number} zin - The z coordinate.
|
||||
* @return {number} The noise value.
|
||||
*/
|
||||
noise3d( xin, yin, zin ) {
|
||||
|
||||
let n0; // Noise contributions from the four corners
|
||||
let n1;
|
||||
let n2;
|
||||
let n3;
|
||||
// Skew the input space to determine which simplex cell we're in
|
||||
const F3 = 1.0 / 3.0;
|
||||
const s = ( xin + yin + zin ) * F3; // Very nice and simple skew factor for 3D
|
||||
const i = Math.floor( xin + s );
|
||||
const j = Math.floor( yin + s );
|
||||
const k = Math.floor( zin + s );
|
||||
const G3 = 1.0 / 6.0; // Very nice and simple unskew factor, too
|
||||
const t = ( i + j + k ) * G3;
|
||||
const X0 = i - t; // Unskew the cell origin back to (x,y,z) space
|
||||
const Y0 = j - t;
|
||||
const Z0 = k - t;
|
||||
const x0 = xin - X0; // The x,y,z distances from the cell origin
|
||||
const y0 = yin - Y0;
|
||||
const z0 = zin - Z0;
|
||||
|
||||
// For the 3D case, the simplex shape is a slightly irregular tetrahedron.
|
||||
// Determine which simplex we are in.
|
||||
let i1; // Offsets for second corner of simplex in (i,j,k) coords
|
||||
|
||||
let j1;
|
||||
let k1;
|
||||
let i2; // Offsets for third corner of simplex in (i,j,k) coords
|
||||
let j2;
|
||||
let k2;
|
||||
if ( x0 >= y0 ) {
|
||||
|
||||
if ( y0 >= z0 ) {
|
||||
|
||||
i1 = 1; j1 = 0; k1 = 0; i2 = 1; j2 = 1; k2 = 0;
|
||||
|
||||
// X Y Z order
|
||||
|
||||
} else if ( x0 >= z0 ) {
|
||||
|
||||
i1 = 1; j1 = 0; k1 = 0; i2 = 1; j2 = 0; k2 = 1;
|
||||
|
||||
// X Z Y order
|
||||
|
||||
} else {
|
||||
|
||||
i1 = 0; j1 = 0; k1 = 1; i2 = 1; j2 = 0; k2 = 1;
|
||||
|
||||
} // Z X Y order
|
||||
|
||||
} else { // x0<y0
|
||||
|
||||
if ( y0 < z0 ) {
|
||||
|
||||
i1 = 0; j1 = 0; k1 = 1; i2 = 0; j2 = 1; k2 = 1;
|
||||
|
||||
// Z Y X order
|
||||
|
||||
} else if ( x0 < z0 ) {
|
||||
|
||||
i1 = 0; j1 = 1; k1 = 0; i2 = 0; j2 = 1; k2 = 1;
|
||||
|
||||
// Y Z X order
|
||||
|
||||
} else {
|
||||
|
||||
i1 = 0; j1 = 1; k1 = 0; i2 = 1; j2 = 1; k2 = 0;
|
||||
|
||||
} // Y X Z order
|
||||
|
||||
}
|
||||
|
||||
// A step of (1,0,0) in (i,j,k) means a step of (1-c,-c,-c) in (x,y,z),
|
||||
// a step of (0,1,0) in (i,j,k) means a step of (-c,1-c,-c) in (x,y,z), and
|
||||
// a step of (0,0,1) in (i,j,k) means a step of (-c,-c,1-c) in (x,y,z), where
|
||||
// c = 1/6.
|
||||
const x1 = x0 - i1 + G3; // Offsets for second corner in (x,y,z) coords
|
||||
const y1 = y0 - j1 + G3;
|
||||
const z1 = z0 - k1 + G3;
|
||||
const x2 = x0 - i2 + 2.0 * G3; // Offsets for third corner in (x,y,z) coords
|
||||
const y2 = y0 - j2 + 2.0 * G3;
|
||||
const z2 = z0 - k2 + 2.0 * G3;
|
||||
const x3 = x0 - 1.0 + 3.0 * G3; // Offsets for last corner in (x,y,z) coords
|
||||
const y3 = y0 - 1.0 + 3.0 * G3;
|
||||
const z3 = z0 - 1.0 + 3.0 * G3;
|
||||
// Work out the hashed gradient indices of the four simplex corners
|
||||
const ii = i & 255;
|
||||
const jj = j & 255;
|
||||
const kk = k & 255;
|
||||
const gi0 = this.perm[ ii + this.perm[ jj + this.perm[ kk ] ] ] % 12;
|
||||
const gi1 = this.perm[ ii + i1 + this.perm[ jj + j1 + this.perm[ kk + k1 ] ] ] % 12;
|
||||
const gi2 = this.perm[ ii + i2 + this.perm[ jj + j2 + this.perm[ kk + k2 ] ] ] % 12;
|
||||
const gi3 = this.perm[ ii + 1 + this.perm[ jj + 1 + this.perm[ kk + 1 ] ] ] % 12;
|
||||
// Calculate the contribution from the four corners
|
||||
let t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0;
|
||||
if ( t0 < 0 ) n0 = 0.0;
|
||||
else {
|
||||
|
||||
t0 *= t0;
|
||||
n0 = t0 * t0 * this._dot3( this.grad3[ gi0 ], x0, y0, z0 );
|
||||
|
||||
}
|
||||
|
||||
let t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1;
|
||||
if ( t1 < 0 ) n1 = 0.0;
|
||||
else {
|
||||
|
||||
t1 *= t1;
|
||||
n1 = t1 * t1 * this._dot3( this.grad3[ gi1 ], x1, y1, z1 );
|
||||
|
||||
}
|
||||
|
||||
let t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2;
|
||||
if ( t2 < 0 ) n2 = 0.0;
|
||||
else {
|
||||
|
||||
t2 *= t2;
|
||||
n2 = t2 * t2 * this._dot3( this.grad3[ gi2 ], x2, y2, z2 );
|
||||
|
||||
}
|
||||
|
||||
let t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3;
|
||||
if ( t3 < 0 ) n3 = 0.0;
|
||||
else {
|
||||
|
||||
t3 *= t3;
|
||||
n3 = t3 * t3 * this._dot3( this.grad3[ gi3 ], x3, y3, z3 );
|
||||
|
||||
}
|
||||
|
||||
// Add contributions from each corner to get the final noise value.
|
||||
// The result is scaled to stay just inside [-1,1]
|
||||
return 32.0 * ( n0 + n1 + n2 + n3 );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A 4D simplex noise method.
|
||||
*
|
||||
* @param {number} x - The x coordinate.
|
||||
* @param {number} y - The y coordinate.
|
||||
* @param {number} z - The z coordinate.
|
||||
* @param {number} w - The w coordinate.
|
||||
* @return {number} The noise value.
|
||||
*/
|
||||
noise4d( x, y, z, w ) {
|
||||
|
||||
// For faster and easier lookups
|
||||
const grad4 = this.grad4;
|
||||
const simplex = this.simplex;
|
||||
const perm = this.perm;
|
||||
|
||||
// The skewing and unskewing factors are hairy again for the 4D case
|
||||
const F4 = ( Math.sqrt( 5.0 ) - 1.0 ) / 4.0;
|
||||
const G4 = ( 5.0 - Math.sqrt( 5.0 ) ) / 20.0;
|
||||
let n0; // Noise contributions from the five corners
|
||||
let n1;
|
||||
let n2;
|
||||
let n3;
|
||||
let n4;
|
||||
// Skew the (x,y,z,w) space to determine which cell of 24 simplices we're in
|
||||
const s = ( x + y + z + w ) * F4; // Factor for 4D skewing
|
||||
const i = Math.floor( x + s );
|
||||
const j = Math.floor( y + s );
|
||||
const k = Math.floor( z + s );
|
||||
const l = Math.floor( w + s );
|
||||
const t = ( i + j + k + l ) * G4; // Factor for 4D unskewing
|
||||
const X0 = i - t; // Unskew the cell origin back to (x,y,z,w) space
|
||||
const Y0 = j - t;
|
||||
const Z0 = k - t;
|
||||
const W0 = l - t;
|
||||
const x0 = x - X0; // The x,y,z,w distances from the cell origin
|
||||
const y0 = y - Y0;
|
||||
const z0 = z - Z0;
|
||||
const w0 = w - W0;
|
||||
|
||||
// For the 4D case, the simplex is a 4D shape I won't even try to describe.
|
||||
// To find out which of the 24 possible simplices we're in, we need to
|
||||
// determine the magnitude ordering of x0, y0, z0 and w0.
|
||||
// The method below is a good way of finding the ordering of x,y,z,w and
|
||||
// then find the correct traversal order for the simplex we’re in.
|
||||
// First, six pair-wise comparisons are performed between each possible pair
|
||||
// of the four coordinates, and the results are used to add up binary bits
|
||||
// for an integer index.
|
||||
const c1 = ( x0 > y0 ) ? 32 : 0;
|
||||
const c2 = ( x0 > z0 ) ? 16 : 0;
|
||||
const c3 = ( y0 > z0 ) ? 8 : 0;
|
||||
const c4 = ( x0 > w0 ) ? 4 : 0;
|
||||
const c5 = ( y0 > w0 ) ? 2 : 0;
|
||||
const c6 = ( z0 > w0 ) ? 1 : 0;
|
||||
const c = c1 + c2 + c3 + c4 + c5 + c6;
|
||||
|
||||
// simplex[c] is a 4-vector with the numbers 0, 1, 2 and 3 in some order.
|
||||
// Many values of c will never occur, since e.g. x>y>z>w makes x<z, y<w and x<w
|
||||
// impossible. Only the 24 indices which have non-zero entries make any sense.
|
||||
// We use a thresholding to set the coordinates in turn from the largest magnitude.
|
||||
// The number 3 in the "simplex" array is at the position of the largest coordinate.
|
||||
const i1 = simplex[ c ][ 0 ] >= 3 ? 1 : 0;
|
||||
const j1 = simplex[ c ][ 1 ] >= 3 ? 1 : 0;
|
||||
const k1 = simplex[ c ][ 2 ] >= 3 ? 1 : 0;
|
||||
const l1 = simplex[ c ][ 3 ] >= 3 ? 1 : 0;
|
||||
// The number 2 in the "simplex" array is at the second largest coordinate.
|
||||
const i2 = simplex[ c ][ 0 ] >= 2 ? 1 : 0;
|
||||
const j2 = simplex[ c ][ 1 ] >= 2 ? 1 : 0;
|
||||
const k2 = simplex[ c ][ 2 ] >= 2 ? 1 : 0;
|
||||
const l2 = simplex[ c ][ 3 ] >= 2 ? 1 : 0;
|
||||
// The number 1 in the "simplex" array is at the second smallest coordinate.
|
||||
const i3 = simplex[ c ][ 0 ] >= 1 ? 1 : 0;
|
||||
const j3 = simplex[ c ][ 1 ] >= 1 ? 1 : 0;
|
||||
const k3 = simplex[ c ][ 2 ] >= 1 ? 1 : 0;
|
||||
const l3 = simplex[ c ][ 3 ] >= 1 ? 1 : 0;
|
||||
// The fifth corner has all coordinate offsets = 1, so no need to look that up.
|
||||
const x1 = x0 - i1 + G4; // Offsets for second corner in (x,y,z,w) coords
|
||||
const y1 = y0 - j1 + G4;
|
||||
const z1 = z0 - k1 + G4;
|
||||
const w1 = w0 - l1 + G4;
|
||||
const x2 = x0 - i2 + 2.0 * G4; // Offsets for third corner in (x,y,z,w) coords
|
||||
const y2 = y0 - j2 + 2.0 * G4;
|
||||
const z2 = z0 - k2 + 2.0 * G4;
|
||||
const w2 = w0 - l2 + 2.0 * G4;
|
||||
const x3 = x0 - i3 + 3.0 * G4; // Offsets for fourth corner in (x,y,z,w) coords
|
||||
const y3 = y0 - j3 + 3.0 * G4;
|
||||
const z3 = z0 - k3 + 3.0 * G4;
|
||||
const w3 = w0 - l3 + 3.0 * G4;
|
||||
const x4 = x0 - 1.0 + 4.0 * G4; // Offsets for last corner in (x,y,z,w) coords
|
||||
const y4 = y0 - 1.0 + 4.0 * G4;
|
||||
const z4 = z0 - 1.0 + 4.0 * G4;
|
||||
const w4 = w0 - 1.0 + 4.0 * G4;
|
||||
// Work out the hashed gradient indices of the five simplex corners
|
||||
const ii = i & 255;
|
||||
const jj = j & 255;
|
||||
const kk = k & 255;
|
||||
const ll = l & 255;
|
||||
const gi0 = perm[ ii + perm[ jj + perm[ kk + perm[ ll ] ] ] ] % 32;
|
||||
const gi1 = perm[ ii + i1 + perm[ jj + j1 + perm[ kk + k1 + perm[ ll + l1 ] ] ] ] % 32;
|
||||
const gi2 = perm[ ii + i2 + perm[ jj + j2 + perm[ kk + k2 + perm[ ll + l2 ] ] ] ] % 32;
|
||||
const gi3 = perm[ ii + i3 + perm[ jj + j3 + perm[ kk + k3 + perm[ ll + l3 ] ] ] ] % 32;
|
||||
const gi4 = perm[ ii + 1 + perm[ jj + 1 + perm[ kk + 1 + perm[ ll + 1 ] ] ] ] % 32;
|
||||
// Calculate the contribution from the five corners
|
||||
let t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0 - w0 * w0;
|
||||
if ( t0 < 0 ) n0 = 0.0;
|
||||
else {
|
||||
|
||||
t0 *= t0;
|
||||
n0 = t0 * t0 * this._dot4( grad4[ gi0 ], x0, y0, z0, w0 );
|
||||
|
||||
}
|
||||
|
||||
let t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1 - w1 * w1;
|
||||
if ( t1 < 0 ) n1 = 0.0;
|
||||
else {
|
||||
|
||||
t1 *= t1;
|
||||
n1 = t1 * t1 * this._dot4( grad4[ gi1 ], x1, y1, z1, w1 );
|
||||
|
||||
}
|
||||
|
||||
let t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2 - w2 * w2;
|
||||
if ( t2 < 0 ) n2 = 0.0;
|
||||
else {
|
||||
|
||||
t2 *= t2;
|
||||
n2 = t2 * t2 * this._dot4( grad4[ gi2 ], x2, y2, z2, w2 );
|
||||
|
||||
}
|
||||
|
||||
let t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3 - w3 * w3;
|
||||
if ( t3 < 0 ) n3 = 0.0;
|
||||
else {
|
||||
|
||||
t3 *= t3;
|
||||
n3 = t3 * t3 * this._dot4( grad4[ gi3 ], x3, y3, z3, w3 );
|
||||
|
||||
}
|
||||
|
||||
let t4 = 0.6 - x4 * x4 - y4 * y4 - z4 * z4 - w4 * w4;
|
||||
if ( t4 < 0 ) n4 = 0.0;
|
||||
else {
|
||||
|
||||
t4 *= t4;
|
||||
n4 = t4 * t4 * this._dot4( grad4[ gi4 ], x4, y4, z4, w4 );
|
||||
|
||||
}
|
||||
|
||||
// Sum up and scale the result to cover the range [-1,1]
|
||||
return 27.0 * ( n0 + n1 + n2 + n3 + n4 );
|
||||
|
||||
}
|
||||
|
||||
// private
|
||||
|
||||
_dot( g, x, y ) {
|
||||
|
||||
return g[ 0 ] * x + g[ 1 ] * y;
|
||||
|
||||
}
|
||||
|
||||
_dot3( g, x, y, z ) {
|
||||
|
||||
return g[ 0 ] * x + g[ 1 ] * y + g[ 2 ] * z;
|
||||
|
||||
}
|
||||
|
||||
_dot4( g, x, y, z, w ) {
|
||||
|
||||
return g[ 0 ] * x + g[ 1 ] * y + g[ 2 ] * z + g[ 3 ] * w;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { SimplexNoise };
|
||||
+363
@@ -0,0 +1,363 @@
|
||||
import {
|
||||
Clock,
|
||||
HalfFloatType,
|
||||
NoBlending,
|
||||
Vector2,
|
||||
WebGLRenderTarget
|
||||
} from 'three';
|
||||
import { CopyShader } from '../shaders/CopyShader.js';
|
||||
import { ShaderPass } from './ShaderPass.js';
|
||||
import { ClearMaskPass, MaskPass } from './MaskPass.js';
|
||||
|
||||
/**
|
||||
* Used to implement post-processing effects in three.js.
|
||||
* The class manages a chain of post-processing passes to produce the final visual result.
|
||||
* Post-processing passes are executed in order of their addition/insertion.
|
||||
* The last pass is automatically rendered to screen.
|
||||
*
|
||||
* This module can only be used with {@link WebGLRenderer}.
|
||||
*
|
||||
* ```js
|
||||
* const composer = new EffectComposer( renderer );
|
||||
*
|
||||
* // adding some passes
|
||||
* const renderPass = new RenderPass( scene, camera );
|
||||
* composer.addPass( renderPass );
|
||||
*
|
||||
* const glitchPass = new GlitchPass();
|
||||
* composer.addPass( glitchPass );
|
||||
*
|
||||
* const outputPass = new OutputPass()
|
||||
* composer.addPass( outputPass );
|
||||
*
|
||||
* function animate() {
|
||||
*
|
||||
* composer.render(); // instead of renderer.render()
|
||||
*
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @three_import import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
|
||||
*/
|
||||
class EffectComposer {
|
||||
|
||||
/**
|
||||
* Constructs a new effect composer.
|
||||
*
|
||||
* @param {WebGLRenderer} renderer - The renderer.
|
||||
* @param {WebGLRenderTarget} [renderTarget] - This render target and a clone will
|
||||
* be used as the internal read and write buffers. If not given, the composer creates
|
||||
* the buffers automatically.
|
||||
*/
|
||||
constructor( renderer, renderTarget ) {
|
||||
|
||||
/**
|
||||
* The renderer.
|
||||
*
|
||||
* @type {WebGLRenderer}
|
||||
*/
|
||||
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';
|
||||
|
||||
/**
|
||||
* A reference to the internal write buffer. Passes usually write
|
||||
* their result into this buffer.
|
||||
*
|
||||
* @type {WebGLRenderTarget}
|
||||
*/
|
||||
this.writeBuffer = this.renderTarget1;
|
||||
|
||||
/**
|
||||
* A reference to the internal read buffer. Passes usually read
|
||||
* the previous render result from this buffer.
|
||||
*
|
||||
* @type {WebGLRenderTarget}
|
||||
*/
|
||||
this.readBuffer = this.renderTarget2;
|
||||
|
||||
/**
|
||||
* Whether the final pass is rendered to the screen (default framebuffer) or not.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @default true
|
||||
*/
|
||||
this.renderToScreen = true;
|
||||
|
||||
/**
|
||||
* An array representing the (ordered) chain of post-processing passes.
|
||||
*
|
||||
* @type {Array<Pass>}
|
||||
*/
|
||||
this.passes = [];
|
||||
|
||||
/**
|
||||
* A copy pass used for internal swap operations.
|
||||
*
|
||||
* @private
|
||||
* @type {ShaderPass}
|
||||
*/
|
||||
this.copyPass = new ShaderPass( CopyShader );
|
||||
this.copyPass.material.blending = NoBlending;
|
||||
|
||||
/**
|
||||
* The internal clock for managing time data.
|
||||
*
|
||||
* @private
|
||||
* @type {Clock}
|
||||
*/
|
||||
this.clock = new Clock();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Swaps the internal read/write buffers.
|
||||
*/
|
||||
swapBuffers() {
|
||||
|
||||
const tmp = this.readBuffer;
|
||||
this.readBuffer = this.writeBuffer;
|
||||
this.writeBuffer = tmp;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given pass to the pass chain.
|
||||
*
|
||||
* @param {Pass} pass - The pass to add.
|
||||
*/
|
||||
addPass( pass ) {
|
||||
|
||||
this.passes.push( pass );
|
||||
pass.setSize( this._width * this._pixelRatio, this._height * this._pixelRatio );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts the given pass at a given index.
|
||||
*
|
||||
* @param {Pass} pass - The pass to insert.
|
||||
* @param {number} index - The index into the pass chain.
|
||||
*/
|
||||
insertPass( pass, index ) {
|
||||
|
||||
this.passes.splice( index, 0, pass );
|
||||
pass.setSize( this._width * this._pixelRatio, this._height * this._pixelRatio );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the given pass from the pass chain.
|
||||
*
|
||||
* @param {Pass} pass - The pass to remove.
|
||||
*/
|
||||
removePass( pass ) {
|
||||
|
||||
const index = this.passes.indexOf( pass );
|
||||
|
||||
if ( index !== - 1 ) {
|
||||
|
||||
this.passes.splice( index, 1 );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if the pass for the given index is the last enabled pass in the pass chain.
|
||||
*
|
||||
* @param {number} passIndex - The pass index.
|
||||
* @return {boolean} Whether the pass for the given index is the last pass in the pass chain.
|
||||
*/
|
||||
isLastEnabledPass( passIndex ) {
|
||||
|
||||
for ( let i = passIndex + 1; i < this.passes.length; i ++ ) {
|
||||
|
||||
if ( this.passes[ i ].enabled ) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes all enabled post-processing passes in order to produce the final frame.
|
||||
*
|
||||
* @param {number} deltaTime - The delta time in seconds. If not given, the composer computes
|
||||
* its own time delta value.
|
||||
*/
|
||||
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 );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the internal state of the EffectComposer.
|
||||
*
|
||||
* @param {WebGLRenderTarget} [renderTarget] - This render target has the same purpose like
|
||||
* the one from the constructor. If set, it is used to setup the read and write buffers.
|
||||
*/
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Resizes the internal read and write buffers as well as all passes. Similar to {@link WebGLRenderer#setSize},
|
||||
* this method honors the current pixel ration.
|
||||
*
|
||||
* @param {number} width - The width in logical pixels.
|
||||
* @param {number} height - The height in logical pixels.
|
||||
*/
|
||||
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 );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets device pixel ratio. This is usually used for HiDPI device to prevent blurring output.
|
||||
* Setting the pixel ratio will automatically resize the composer.
|
||||
*
|
||||
* @param {number} pixelRatio - The pixel ratio to set.
|
||||
*/
|
||||
setPixelRatio( pixelRatio ) {
|
||||
|
||||
this._pixelRatio = pixelRatio;
|
||||
|
||||
this.setSize( this._width, this._height );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Frees the GPU-related resources allocated by this instance. Call this
|
||||
* method whenever the composer is no longer used in your app.
|
||||
*/
|
||||
dispose() {
|
||||
|
||||
this.renderTarget1.dispose();
|
||||
this.renderTarget2.dispose();
|
||||
|
||||
this.copyPass.dispose();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { EffectComposer };
|
||||
+195
@@ -0,0 +1,195 @@
|
||||
import { Pass } from './Pass.js';
|
||||
|
||||
/**
|
||||
* This pass can be used to define a mask during post processing.
|
||||
* Meaning only areas of subsequent post processing are affected
|
||||
* which lie in the masking area of this pass. Internally, the masking
|
||||
* is implemented with the stencil buffer.
|
||||
*
|
||||
* ```js
|
||||
* const maskPass = new MaskPass( scene, camera );
|
||||
* composer.addPass( maskPass );
|
||||
* ```
|
||||
*
|
||||
* @augments Pass
|
||||
* @three_import import { MaskPass } from 'three/addons/postprocessing/MaskPass.js';
|
||||
*/
|
||||
class MaskPass extends Pass {
|
||||
|
||||
/**
|
||||
* Constructs a new mask pass.
|
||||
*
|
||||
* @param {Scene} scene - The 3D objects in this scene will define the mask.
|
||||
* @param {Camera} camera - The camera.
|
||||
*/
|
||||
constructor( scene, camera ) {
|
||||
|
||||
super();
|
||||
|
||||
/**
|
||||
* The scene that defines the mask.
|
||||
*
|
||||
* @type {Scene}
|
||||
*/
|
||||
this.scene = scene;
|
||||
|
||||
/**
|
||||
* The camera.
|
||||
*
|
||||
* @type {Camera}
|
||||
*/
|
||||
this.camera = camera;
|
||||
|
||||
/**
|
||||
* Overwritten to perform a clear operation by default.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @default true
|
||||
*/
|
||||
this.clear = true;
|
||||
|
||||
/**
|
||||
* Overwritten to disable the swap.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @default false
|
||||
*/
|
||||
this.needsSwap = false;
|
||||
|
||||
/**
|
||||
* Whether to inverse the mask or not.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @default false
|
||||
*/
|
||||
this.inverse = false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a mask pass with the configured scene and camera.
|
||||
*
|
||||
* @param {WebGLRenderer} renderer - The renderer.
|
||||
* @param {WebGLRenderTarget} writeBuffer - The write buffer. This buffer is intended as the rendering
|
||||
* destination for the pass.
|
||||
* @param {WebGLRenderTarget} readBuffer - The read buffer. The pass can access the result from the
|
||||
* previous pass from this buffer.
|
||||
* @param {number} deltaTime - The delta time in seconds.
|
||||
* @param {boolean} maskActive - Whether masking is active or not.
|
||||
*/
|
||||
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 );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This pass can be used to clear a mask previously defined with {@link MaskPass}.
|
||||
*
|
||||
* ```js
|
||||
* const clearPass = new ClearMaskPass();
|
||||
* composer.addPass( clearPass );
|
||||
* ```
|
||||
*
|
||||
* @augments Pass
|
||||
*/
|
||||
class ClearMaskPass extends Pass {
|
||||
|
||||
/**
|
||||
* Constructs a new clear mask pass.
|
||||
*/
|
||||
constructor() {
|
||||
|
||||
super();
|
||||
|
||||
/**
|
||||
* Overwritten to disable the swap.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @default false
|
||||
*/
|
||||
this.needsSwap = false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the clear of the currently defined mask.
|
||||
*
|
||||
* @param {WebGLRenderer} renderer - The renderer.
|
||||
* @param {WebGLRenderTarget} writeBuffer - The write buffer. This buffer is intended as the rendering
|
||||
* destination for the pass.
|
||||
* @param {WebGLRenderTarget} readBuffer - The read buffer. The pass can access the result from the
|
||||
* previous pass from this buffer.
|
||||
* @param {number} deltaTime - The delta time in seconds.
|
||||
* @param {boolean} maskActive - Whether masking is active or not.
|
||||
*/
|
||||
render( renderer /*, writeBuffer, readBuffer, deltaTime, maskActive */ ) {
|
||||
|
||||
renderer.state.buffers.stencil.setLocked( false );
|
||||
renderer.state.buffers.stencil.setTest( false );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { MaskPass, ClearMaskPass };
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
import {
|
||||
ColorManagement,
|
||||
RawShaderMaterial,
|
||||
UniformsUtils,
|
||||
LinearToneMapping,
|
||||
ReinhardToneMapping,
|
||||
CineonToneMapping,
|
||||
AgXToneMapping,
|
||||
ACESFilmicToneMapping,
|
||||
NeutralToneMapping,
|
||||
CustomToneMapping,
|
||||
SRGBTransfer
|
||||
} from 'three';
|
||||
import { Pass, FullScreenQuad } from './Pass.js';
|
||||
import { OutputShader } from '../shaders/OutputShader.js';
|
||||
|
||||
/**
|
||||
* This pass is responsible for including tone mapping and color space conversion
|
||||
* into your pass chain. In most cases, this pass should be included at the end
|
||||
* of each pass chain. If a pass requires sRGB input (e.g. like FXAA), the pass
|
||||
* must follow `OutputPass` in the pass chain.
|
||||
*
|
||||
* The tone mapping and color space settings are extracted from the renderer.
|
||||
*
|
||||
* ```js
|
||||
* const outputPass = new OutputPass();
|
||||
* composer.addPass( outputPass );
|
||||
* ```
|
||||
*
|
||||
* @augments Pass
|
||||
* @three_import import { OutputPass } from 'three/addons/postprocessing/OutputPass.js';
|
||||
*/
|
||||
class OutputPass extends Pass {
|
||||
|
||||
/**
|
||||
* Constructs a new output pass.
|
||||
*/
|
||||
constructor() {
|
||||
|
||||
super();
|
||||
|
||||
/**
|
||||
* The pass uniforms.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
this.uniforms = UniformsUtils.clone( OutputShader.uniforms );
|
||||
|
||||
/**
|
||||
* The pass material.
|
||||
*
|
||||
* @type {RawShaderMaterial}
|
||||
*/
|
||||
this.material = new RawShaderMaterial( {
|
||||
name: OutputShader.name,
|
||||
uniforms: this.uniforms,
|
||||
vertexShader: OutputShader.vertexShader,
|
||||
fragmentShader: OutputShader.fragmentShader
|
||||
} );
|
||||
|
||||
// internals
|
||||
|
||||
this._fsQuad = new FullScreenQuad( this.material );
|
||||
|
||||
this._outputColorSpace = null;
|
||||
this._toneMapping = null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the output pass.
|
||||
*
|
||||
* @param {WebGLRenderer} renderer - The renderer.
|
||||
* @param {WebGLRenderTarget} writeBuffer - The write buffer. This buffer is intended as the rendering
|
||||
* destination for the pass.
|
||||
* @param {WebGLRenderTarget} readBuffer - The read buffer. The pass can access the result from the
|
||||
* previous pass from this buffer.
|
||||
* @param {number} deltaTime - The delta time in seconds.
|
||||
* @param {boolean} maskActive - Whether masking is active or not.
|
||||
*/
|
||||
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 = '';
|
||||
else if ( this._toneMapping === CustomToneMapping ) this.material.defines.CUSTOM_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 );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Frees the GPU-related resources allocated by this instance. Call this
|
||||
* method whenever the pass is no longer used in your app.
|
||||
*/
|
||||
dispose() {
|
||||
|
||||
this.material.dispose();
|
||||
this._fsQuad.dispose();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { OutputPass };
|
||||
@@ -0,0 +1,191 @@
|
||||
import {
|
||||
BufferGeometry,
|
||||
Float32BufferAttribute,
|
||||
OrthographicCamera,
|
||||
Mesh
|
||||
} from 'three';
|
||||
|
||||
/**
|
||||
* Abstract base class for all post processing passes.
|
||||
*
|
||||
* This module is only relevant for post processing with {@link WebGLRenderer}.
|
||||
*
|
||||
* @abstract
|
||||
* @three_import import { Pass } from 'three/addons/postprocessing/Pass.js';
|
||||
*/
|
||||
class Pass {
|
||||
|
||||
/**
|
||||
* Constructs a new pass.
|
||||
*/
|
||||
constructor() {
|
||||
|
||||
/**
|
||||
* This flag can be used for type testing.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @readonly
|
||||
* @default true
|
||||
*/
|
||||
this.isPass = true;
|
||||
|
||||
/**
|
||||
* If set to `true`, the pass is processed by the composer.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @default true
|
||||
*/
|
||||
this.enabled = true;
|
||||
|
||||
/**
|
||||
* If set to `true`, the pass indicates to swap read and write buffer after rendering.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @default true
|
||||
*/
|
||||
this.needsSwap = true;
|
||||
|
||||
/**
|
||||
* If set to `true`, the pass clears its buffer before rendering
|
||||
*
|
||||
* @type {boolean}
|
||||
* @default false
|
||||
*/
|
||||
this.clear = false;
|
||||
|
||||
/**
|
||||
* If set to `true`, the result of the pass is rendered to screen. The last pass in the composers
|
||||
* pass chain gets automatically rendered to screen, no matter how this property is configured.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @default false
|
||||
*/
|
||||
this.renderToScreen = false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the size of the pass.
|
||||
*
|
||||
* @abstract
|
||||
* @param {number} width - The width to set.
|
||||
* @param {number} height - The height to set.
|
||||
*/
|
||||
setSize( /* width, height */ ) {}
|
||||
|
||||
/**
|
||||
* This method holds the render logic of a pass. It must be implemented in all derived classes.
|
||||
*
|
||||
* @abstract
|
||||
* @param {WebGLRenderer} renderer - The renderer.
|
||||
* @param {WebGLRenderTarget} writeBuffer - The write buffer. This buffer is intended as the rendering
|
||||
* destination for the pass.
|
||||
* @param {WebGLRenderTarget} readBuffer - The read buffer. The pass can access the result from the
|
||||
* previous pass from this buffer.
|
||||
* @param {number} deltaTime - The delta time in seconds.
|
||||
* @param {boolean} maskActive - Whether masking is active or not.
|
||||
*/
|
||||
render( /* renderer, writeBuffer, readBuffer, deltaTime, maskActive */ ) {
|
||||
|
||||
console.error( 'THREE.Pass: .render() must be implemented in derived pass.' );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Frees the GPU-related resources allocated by this instance. Call this
|
||||
* method whenever the pass is no longer used in your app.
|
||||
*
|
||||
* @abstract
|
||||
*/
|
||||
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();
|
||||
|
||||
|
||||
/**
|
||||
* This module is a helper for passes which need to render a full
|
||||
* screen effect which is quite common in context of post processing.
|
||||
*
|
||||
* The intended usage is to reuse a single full screen quad for rendering
|
||||
* subsequent passes by just reassigning the `material` reference.
|
||||
*
|
||||
* This module can only be used with {@link WebGLRenderer}.
|
||||
*
|
||||
* @augments Mesh
|
||||
* @three_import import { FullScreenQuad } from 'three/addons/postprocessing/Pass.js';
|
||||
*/
|
||||
class FullScreenQuad {
|
||||
|
||||
/**
|
||||
* Constructs a new full screen quad.
|
||||
*
|
||||
* @param {?Material} material - The material to render te full screen quad with.
|
||||
*/
|
||||
constructor( material ) {
|
||||
|
||||
this._mesh = new Mesh( _geometry, material );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Frees the GPU-related resources allocated by this instance. Call this
|
||||
* method whenever the instance is no longer used in your app.
|
||||
*/
|
||||
dispose() {
|
||||
|
||||
this._mesh.geometry.dispose();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the full screen quad.
|
||||
*
|
||||
* @param {WebGLRenderer} renderer - The renderer.
|
||||
*/
|
||||
render( renderer ) {
|
||||
|
||||
renderer.render( this._mesh, _camera );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The quad's material.
|
||||
*
|
||||
* @type {?Material}
|
||||
*/
|
||||
get material() {
|
||||
|
||||
return this._mesh.material;
|
||||
|
||||
}
|
||||
|
||||
set material( value ) {
|
||||
|
||||
this._mesh.material = value;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { Pass, FullScreenQuad };
|
||||
+183
@@ -0,0 +1,183 @@
|
||||
import {
|
||||
Color
|
||||
} from 'three';
|
||||
import { Pass } from './Pass.js';
|
||||
|
||||
/**
|
||||
* This class represents a render pass. It takes a camera and a scene and produces
|
||||
* a beauty pass for subsequent post processing effects.
|
||||
*
|
||||
* ```js
|
||||
* const renderPass = new RenderPass( scene, camera );
|
||||
* composer.addPass( renderPass );
|
||||
* ```
|
||||
*
|
||||
* @augments Pass
|
||||
* @three_import import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
|
||||
*/
|
||||
class RenderPass extends Pass {
|
||||
|
||||
/**
|
||||
* Constructs a new render pass.
|
||||
*
|
||||
* @param {Scene} scene - The scene to render.
|
||||
* @param {Camera} camera - The camera.
|
||||
* @param {?Material} [overrideMaterial=null] - The override material. If set, this material is used
|
||||
* for all objects in the scene.
|
||||
* @param {?(number|Color|string)} [clearColor=null] - The clear color of the render pass.
|
||||
* @param {?number} [clearAlpha=null] - The clear alpha of the render pass.
|
||||
*/
|
||||
constructor( scene, camera, overrideMaterial = null, clearColor = null, clearAlpha = null ) {
|
||||
|
||||
super();
|
||||
|
||||
/**
|
||||
* The scene to render.
|
||||
*
|
||||
* @type {Scene}
|
||||
*/
|
||||
this.scene = scene;
|
||||
|
||||
/**
|
||||
* The camera.
|
||||
*
|
||||
* @type {Camera}
|
||||
*/
|
||||
this.camera = camera;
|
||||
|
||||
/**
|
||||
* The override material. If set, this material is used
|
||||
* for all objects in the scene.
|
||||
*
|
||||
* @type {?Material}
|
||||
* @default null
|
||||
*/
|
||||
this.overrideMaterial = overrideMaterial;
|
||||
|
||||
/**
|
||||
* The clear color of the render pass.
|
||||
*
|
||||
* @type {?(number|Color|string)}
|
||||
* @default null
|
||||
*/
|
||||
this.clearColor = clearColor;
|
||||
|
||||
/**
|
||||
* The clear alpha of the render pass.
|
||||
*
|
||||
* @type {?number}
|
||||
* @default null
|
||||
*/
|
||||
this.clearAlpha = clearAlpha;
|
||||
|
||||
/**
|
||||
* Overwritten to perform a clear operation by default.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @default true
|
||||
*/
|
||||
this.clear = true;
|
||||
|
||||
/**
|
||||
* If set to `true`, only the depth can be cleared when `clear` is to `false`.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @default false
|
||||
*/
|
||||
this.clearDepth = false;
|
||||
|
||||
/**
|
||||
* Overwritten to disable the swap.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @default false
|
||||
*/
|
||||
this.needsSwap = false;
|
||||
this._oldClearColor = new Color();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a beauty pass with the configured scene and camera.
|
||||
*
|
||||
* @param {WebGLRenderer} renderer - The renderer.
|
||||
* @param {WebGLRenderTarget} writeBuffer - The write buffer. This buffer is intended as the rendering
|
||||
* destination for the pass.
|
||||
* @param {WebGLRenderTarget} readBuffer - The read buffer. The pass can access the result from the
|
||||
* previous pass from this buffer.
|
||||
* @param {number} deltaTime - The delta time in seconds.
|
||||
* @param {boolean} maskActive - Whether masking is active or not.
|
||||
*/
|
||||
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, renderer.getClearAlpha() );
|
||||
|
||||
}
|
||||
|
||||
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 };
|
||||
+527
@@ -0,0 +1,527 @@
|
||||
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 { SSAOBlurShader, SSAODepthShader, SSAOShader } from '../shaders/SSAOShader.js';
|
||||
import { CopyShader } from '../shaders/CopyShader.js';
|
||||
|
||||
/**
|
||||
* A pass for a basic SSAO effect.
|
||||
*
|
||||
* {@link SAOPass} and {@link GTAPass} produce a more advanced AO but are also
|
||||
* more expensive.
|
||||
*
|
||||
* ```js
|
||||
* const ssaoPass = new SSAOPass( scene, camera, width, height );
|
||||
* composer.addPass( ssaoPass );
|
||||
* ```
|
||||
*
|
||||
* @augments Pass
|
||||
* @three_import import { SSAOPass } from 'three/addons/postprocessing/SSAOPass.js';
|
||||
*/
|
||||
class SSAOPass extends Pass {
|
||||
|
||||
/**
|
||||
* Constructs a new SSAO pass.
|
||||
*
|
||||
* @param {Scene} scene - The scene to compute the AO for.
|
||||
* @param {Camera} camera - The camera.
|
||||
* @param {number} [width=512] - The width of the effect.
|
||||
* @param {number} [height=512] - The height of the effect.
|
||||
* @param {number} [kernelSize=32] - The kernel size.
|
||||
*/
|
||||
constructor( scene, camera, width = 512, height = 512, kernelSize = 32 ) {
|
||||
|
||||
super();
|
||||
|
||||
/**
|
||||
* The width of the effect.
|
||||
*
|
||||
* @type {number}
|
||||
* @default 512
|
||||
*/
|
||||
this.width = width;
|
||||
|
||||
/**
|
||||
* The height of the effect.
|
||||
*
|
||||
* @type {number}
|
||||
* @default 512
|
||||
*/
|
||||
this.height = height;
|
||||
|
||||
/**
|
||||
* Overwritten to perform a clear operation by default.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @default true
|
||||
*/
|
||||
this.clear = true;
|
||||
|
||||
/**
|
||||
* Overwritten to disable the swap.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @default false
|
||||
*/
|
||||
this.needsSwap = false;
|
||||
|
||||
/**
|
||||
* The camera.
|
||||
*
|
||||
* @type {Camera}
|
||||
*/
|
||||
this.camera = camera;
|
||||
|
||||
/**
|
||||
* The scene to render the AO for.
|
||||
*
|
||||
* @type {Scene}
|
||||
*/
|
||||
this.scene = scene;
|
||||
|
||||
/**
|
||||
* The kernel radius controls how wide the
|
||||
* AO spreads.
|
||||
*
|
||||
* @type {number}
|
||||
* @default 8
|
||||
*/
|
||||
this.kernelRadius = 8;
|
||||
this.kernel = [];
|
||||
this.noiseTexture = null;
|
||||
|
||||
/**
|
||||
* The output configuration.
|
||||
*
|
||||
* @type {number}
|
||||
* @default 0
|
||||
*/
|
||||
this.output = 0;
|
||||
|
||||
/**
|
||||
* Defines the minimum distance that should be
|
||||
* affected by the AO.
|
||||
*
|
||||
* @type {number}
|
||||
* @default 0.005
|
||||
*/
|
||||
this.minDistance = 0.005;
|
||||
|
||||
/**
|
||||
* Defines the maximum distance that should be
|
||||
* affected by the AO.
|
||||
*
|
||||
* @type {number}
|
||||
* @default 0.1
|
||||
*/
|
||||
this.maxDistance = 0.1;
|
||||
|
||||
this._visibilityCache = [];
|
||||
|
||||
//
|
||||
|
||||
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
|
||||
} );
|
||||
|
||||
// internals
|
||||
|
||||
this._fsQuad = new FullScreenQuad( null );
|
||||
|
||||
this._originalClearColor = new Color();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Frees the GPU-related resources allocated by this instance. Call this
|
||||
* method whenever the pass is no longer used in your app.
|
||||
*/
|
||||
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();
|
||||
|
||||
// dispose full screen quad
|
||||
|
||||
this._fsQuad.dispose();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the SSAO pass.
|
||||
*
|
||||
* @param {WebGLRenderer} renderer - The renderer.
|
||||
* @param {WebGLRenderTarget} writeBuffer - The write buffer. This buffer is intended as the rendering
|
||||
* destination for the pass.
|
||||
* @param {WebGLRenderTarget} readBuffer - The read buffer. The pass can access the result from the
|
||||
* previous pass from this buffer.
|
||||
* @param {number} deltaTime - The delta time in seconds.
|
||||
* @param {boolean} maskActive - Whether masking is active or not.
|
||||
*/
|
||||
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 : readBuffer );
|
||||
|
||||
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 : readBuffer );
|
||||
|
||||
break;
|
||||
|
||||
case SSAOPass.OUTPUT.Depth:
|
||||
|
||||
this._renderPass( renderer, this.depthRenderMaterial, this.renderToScreen ? null : readBuffer );
|
||||
|
||||
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 : readBuffer );
|
||||
|
||||
break;
|
||||
|
||||
case SSAOPass.OUTPUT.Default:
|
||||
|
||||
this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.blurRenderTarget.texture;
|
||||
this.copyMaterial.blending = CustomBlending;
|
||||
this._renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : readBuffer );
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
console.warn( 'THREE.SSAOPass: Unknown output type.' );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the size of the pass.
|
||||
*
|
||||
* @param {number} width - The width to set.
|
||||
* @param {number} height - The height to set.
|
||||
*/
|
||||
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 );
|
||||
|
||||
}
|
||||
|
||||
// internals
|
||||
|
||||
_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 );
|
||||
|
||||
}
|
||||
|
||||
_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 ) {
|
||||
|
||||
if ( ( object.isPoints || object.isLine || object.isLine2 ) && object.visible ) {
|
||||
|
||||
object.visible = false;
|
||||
cache.push( object );
|
||||
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
_restoreVisibility() {
|
||||
|
||||
const cache = this._visibilityCache;
|
||||
|
||||
for ( let i = 0; i < cache.length; i ++ ) {
|
||||
|
||||
cache[ i ].visible = true;
|
||||
|
||||
}
|
||||
|
||||
cache.length = 0;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
SSAOPass.OUTPUT = {
|
||||
'Default': 0,
|
||||
'SSAO': 1,
|
||||
'Blur': 2,
|
||||
'Depth': 3,
|
||||
'Normal': 4
|
||||
};
|
||||
|
||||
export { SSAOPass };
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
import {
|
||||
ShaderMaterial,
|
||||
UniformsUtils
|
||||
} from 'three';
|
||||
import { Pass, FullScreenQuad } from './Pass.js';
|
||||
|
||||
/**
|
||||
* This pass can be used to create a post processing effect
|
||||
* with a raw GLSL shader object. Useful for implementing custom
|
||||
* effects.
|
||||
*
|
||||
* ```js
|
||||
* const fxaaPass = new ShaderPass( FXAAShader );
|
||||
* composer.addPass( fxaaPass );
|
||||
* ```
|
||||
*
|
||||
* @augments Pass
|
||||
* @three_import import { ShaderPass } from 'three/addons/postprocessing/ShaderPass.js';
|
||||
*/
|
||||
class ShaderPass extends Pass {
|
||||
|
||||
/**
|
||||
* Constructs a new shader pass.
|
||||
*
|
||||
* @param {Object|ShaderMaterial} [shader] - A shader object holding vertex and fragment shader as well as
|
||||
* defines and uniforms. It's also valid to pass a custom shader material.
|
||||
* @param {string} [textureID='tDiffuse'] - The name of the texture uniform that should sample
|
||||
* the read buffer.
|
||||
*/
|
||||
constructor( shader, textureID = 'tDiffuse' ) {
|
||||
|
||||
super();
|
||||
|
||||
/**
|
||||
* The name of the texture uniform that should sample the read buffer.
|
||||
*
|
||||
* @type {string}
|
||||
* @default 'tDiffuse'
|
||||
*/
|
||||
this.textureID = textureID;
|
||||
|
||||
/**
|
||||
* The pass uniforms.
|
||||
*
|
||||
* @type {?Object}
|
||||
*/
|
||||
this.uniforms = null;
|
||||
|
||||
/**
|
||||
* The pass material.
|
||||
*
|
||||
* @type {?ShaderMaterial}
|
||||
*/
|
||||
this.material = null;
|
||||
|
||||
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
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
// internals
|
||||
|
||||
this._fsQuad = new FullScreenQuad( this.material );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the shader pass.
|
||||
*
|
||||
* @param {WebGLRenderer} renderer - The renderer.
|
||||
* @param {WebGLRenderTarget} writeBuffer - The write buffer. This buffer is intended as the rendering
|
||||
* destination for the pass.
|
||||
* @param {WebGLRenderTarget} readBuffer - The read buffer. The pass can access the result from the
|
||||
* previous pass from this buffer.
|
||||
* @param {number} deltaTime - The delta time in seconds.
|
||||
* @param {boolean} maskActive - Whether masking is active or not.
|
||||
*/
|
||||
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 );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Frees the GPU-related resources allocated by this instance. Call this
|
||||
* method whenever the pass is no longer used in your app.
|
||||
*/
|
||||
dispose() {
|
||||
|
||||
this.material.dispose();
|
||||
|
||||
this._fsQuad.dispose();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { ShaderPass };
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* @module CopyShader
|
||||
* @three_import import { CopyShader } from 'three/addons/shaders/CopyShader.js';
|
||||
*/
|
||||
|
||||
/**
|
||||
* Full-screen copy shader pass.
|
||||
*
|
||||
* @constant
|
||||
* @type {ShaderMaterial~Shader}
|
||||
*/
|
||||
const CopyShader = {
|
||||
|
||||
name: 'CopyShader',
|
||||
|
||||
uniforms: {
|
||||
|
||||
'tDiffuse': { value: null },
|
||||
'opacity': { value: 1.0 }
|
||||
|
||||
},
|
||||
|
||||
vertexShader: /* glsl */`
|
||||
|
||||
varying vec2 vUv;
|
||||
|
||||
void main() {
|
||||
|
||||
vUv = uv;
|
||||
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
|
||||
|
||||
}`,
|
||||
|
||||
fragmentShader: /* glsl */`
|
||||
|
||||
uniform float opacity;
|
||||
|
||||
uniform sampler2D tDiffuse;
|
||||
|
||||
varying vec2 vUv;
|
||||
|
||||
void main() {
|
||||
|
||||
vec4 texel = texture2D( tDiffuse, vUv );
|
||||
gl_FragColor = opacity * texel;
|
||||
|
||||
|
||||
}`
|
||||
|
||||
};
|
||||
|
||||
export { CopyShader };
|
||||
@@ -0,0 +1,298 @@
|
||||
import {
|
||||
Vector2
|
||||
} from 'three';
|
||||
|
||||
/**
|
||||
* @module FXAAShader
|
||||
* @three_import import { FXAAShader } from 'three/addons/shaders/FXAAShader.js';
|
||||
*/
|
||||
|
||||
/**
|
||||
* FXAA algorithm from NVIDIA, C# implementation by Jasper Flick, GLSL port by Dave Hoskins.
|
||||
*
|
||||
* References:
|
||||
* - {@link http://developer.download.nvidia.com/assets/gamedev/files/sdk/11/FXAA_WhitePaper.pdf}.
|
||||
* - {@link https://catlikecoding.com/unity/tutorials/advanced-rendering/fxaa/}.
|
||||
*
|
||||
* @constant
|
||||
* @type {ShaderMaterial~Shader}
|
||||
*/
|
||||
const FXAAShader = {
|
||||
|
||||
name: 'FXAAShader',
|
||||
|
||||
uniforms: {
|
||||
|
||||
'tDiffuse': { value: null },
|
||||
'resolution': { value: new Vector2( 1 / 1024, 1 / 512 ) }
|
||||
|
||||
},
|
||||
|
||||
vertexShader: /* glsl */`
|
||||
|
||||
varying vec2 vUv;
|
||||
|
||||
void main() {
|
||||
|
||||
vUv = uv;
|
||||
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
|
||||
|
||||
}`,
|
||||
|
||||
fragmentShader: /* glsl */`
|
||||
|
||||
uniform sampler2D tDiffuse;
|
||||
uniform vec2 resolution;
|
||||
varying vec2 vUv;
|
||||
|
||||
#define EDGE_STEP_COUNT 6
|
||||
#define EDGE_GUESS 8.0
|
||||
#define EDGE_STEPS 1.0, 1.5, 2.0, 2.0, 2.0, 4.0
|
||||
const float edgeSteps[EDGE_STEP_COUNT] = float[EDGE_STEP_COUNT]( EDGE_STEPS );
|
||||
|
||||
float _ContrastThreshold = 0.0312;
|
||||
float _RelativeThreshold = 0.063;
|
||||
float _SubpixelBlending = 1.0;
|
||||
|
||||
vec4 Sample( sampler2D tex2D, vec2 uv ) {
|
||||
|
||||
return texture( tex2D, uv );
|
||||
|
||||
}
|
||||
|
||||
float SampleLuminance( sampler2D tex2D, vec2 uv ) {
|
||||
|
||||
return dot( Sample( tex2D, uv ).rgb, vec3( 0.3, 0.59, 0.11 ) );
|
||||
|
||||
}
|
||||
|
||||
float SampleLuminance( sampler2D tex2D, vec2 texSize, vec2 uv, float uOffset, float vOffset ) {
|
||||
|
||||
uv += texSize * vec2(uOffset, vOffset);
|
||||
return SampleLuminance(tex2D, uv);
|
||||
|
||||
}
|
||||
|
||||
struct LuminanceData {
|
||||
|
||||
float m, n, e, s, w;
|
||||
float ne, nw, se, sw;
|
||||
float highest, lowest, contrast;
|
||||
|
||||
};
|
||||
|
||||
LuminanceData SampleLuminanceNeighborhood( sampler2D tex2D, vec2 texSize, vec2 uv ) {
|
||||
|
||||
LuminanceData l;
|
||||
l.m = SampleLuminance( tex2D, uv );
|
||||
l.n = SampleLuminance( tex2D, texSize, uv, 0.0, 1.0 );
|
||||
l.e = SampleLuminance( tex2D, texSize, uv, 1.0, 0.0 );
|
||||
l.s = SampleLuminance( tex2D, texSize, uv, 0.0, -1.0 );
|
||||
l.w = SampleLuminance( tex2D, texSize, uv, -1.0, 0.0 );
|
||||
|
||||
l.ne = SampleLuminance( tex2D, texSize, uv, 1.0, 1.0 );
|
||||
l.nw = SampleLuminance( tex2D, texSize, uv, -1.0, 1.0 );
|
||||
l.se = SampleLuminance( tex2D, texSize, uv, 1.0, -1.0 );
|
||||
l.sw = SampleLuminance( tex2D, texSize, uv, -1.0, -1.0 );
|
||||
|
||||
l.highest = max( max( max( max( l.n, l.e ), l.s ), l.w ), l.m );
|
||||
l.lowest = min( min( min( min( l.n, l.e ), l.s ), l.w ), l.m );
|
||||
l.contrast = l.highest - l.lowest;
|
||||
return l;
|
||||
|
||||
}
|
||||
|
||||
bool ShouldSkipPixel( LuminanceData l ) {
|
||||
|
||||
float threshold = max( _ContrastThreshold, _RelativeThreshold * l.highest );
|
||||
return l.contrast < threshold;
|
||||
|
||||
}
|
||||
|
||||
float DeterminePixelBlendFactor( LuminanceData l ) {
|
||||
|
||||
float f = 2.0 * ( l.n + l.e + l.s + l.w );
|
||||
f += l.ne + l.nw + l.se + l.sw;
|
||||
f *= 1.0 / 12.0;
|
||||
f = abs( f - l.m );
|
||||
f = clamp( f / l.contrast, 0.0, 1.0 );
|
||||
|
||||
float blendFactor = smoothstep( 0.0, 1.0, f );
|
||||
return blendFactor * blendFactor * _SubpixelBlending;
|
||||
|
||||
}
|
||||
|
||||
struct EdgeData {
|
||||
|
||||
bool isHorizontal;
|
||||
float pixelStep;
|
||||
float oppositeLuminance, gradient;
|
||||
|
||||
};
|
||||
|
||||
EdgeData DetermineEdge( vec2 texSize, LuminanceData l ) {
|
||||
|
||||
EdgeData e;
|
||||
float horizontal =
|
||||
abs( l.n + l.s - 2.0 * l.m ) * 2.0 +
|
||||
abs( l.ne + l.se - 2.0 * l.e ) +
|
||||
abs( l.nw + l.sw - 2.0 * l.w );
|
||||
float vertical =
|
||||
abs( l.e + l.w - 2.0 * l.m ) * 2.0 +
|
||||
abs( l.ne + l.nw - 2.0 * l.n ) +
|
||||
abs( l.se + l.sw - 2.0 * l.s );
|
||||
e.isHorizontal = horizontal >= vertical;
|
||||
|
||||
float pLuminance = e.isHorizontal ? l.n : l.e;
|
||||
float nLuminance = e.isHorizontal ? l.s : l.w;
|
||||
float pGradient = abs( pLuminance - l.m );
|
||||
float nGradient = abs( nLuminance - l.m );
|
||||
|
||||
e.pixelStep = e.isHorizontal ? texSize.y : texSize.x;
|
||||
|
||||
if (pGradient < nGradient) {
|
||||
|
||||
e.pixelStep = -e.pixelStep;
|
||||
e.oppositeLuminance = nLuminance;
|
||||
e.gradient = nGradient;
|
||||
|
||||
} else {
|
||||
|
||||
e.oppositeLuminance = pLuminance;
|
||||
e.gradient = pGradient;
|
||||
|
||||
}
|
||||
|
||||
return e;
|
||||
|
||||
}
|
||||
|
||||
float DetermineEdgeBlendFactor( sampler2D tex2D, vec2 texSize, LuminanceData l, EdgeData e, vec2 uv ) {
|
||||
|
||||
vec2 uvEdge = uv;
|
||||
vec2 edgeStep;
|
||||
if (e.isHorizontal) {
|
||||
|
||||
uvEdge.y += e.pixelStep * 0.5;
|
||||
edgeStep = vec2( texSize.x, 0.0 );
|
||||
|
||||
} else {
|
||||
|
||||
uvEdge.x += e.pixelStep * 0.5;
|
||||
edgeStep = vec2( 0.0, texSize.y );
|
||||
|
||||
}
|
||||
|
||||
float edgeLuminance = ( l.m + e.oppositeLuminance ) * 0.5;
|
||||
float gradientThreshold = e.gradient * 0.25;
|
||||
|
||||
vec2 puv = uvEdge + edgeStep * edgeSteps[0];
|
||||
float pLuminanceDelta = SampleLuminance( tex2D, puv ) - edgeLuminance;
|
||||
bool pAtEnd = abs( pLuminanceDelta ) >= gradientThreshold;
|
||||
|
||||
for ( int i = 1; i < EDGE_STEP_COUNT && !pAtEnd; i++ ) {
|
||||
|
||||
puv += edgeStep * edgeSteps[i];
|
||||
pLuminanceDelta = SampleLuminance( tex2D, puv ) - edgeLuminance;
|
||||
pAtEnd = abs( pLuminanceDelta ) >= gradientThreshold;
|
||||
|
||||
}
|
||||
|
||||
if ( !pAtEnd ) {
|
||||
|
||||
puv += edgeStep * EDGE_GUESS;
|
||||
|
||||
}
|
||||
|
||||
vec2 nuv = uvEdge - edgeStep * edgeSteps[0];
|
||||
float nLuminanceDelta = SampleLuminance( tex2D, nuv ) - edgeLuminance;
|
||||
bool nAtEnd = abs( nLuminanceDelta ) >= gradientThreshold;
|
||||
|
||||
for ( int i = 1; i < EDGE_STEP_COUNT && !nAtEnd; i++ ) {
|
||||
|
||||
nuv -= edgeStep * edgeSteps[i];
|
||||
nLuminanceDelta = SampleLuminance( tex2D, nuv ) - edgeLuminance;
|
||||
nAtEnd = abs( nLuminanceDelta ) >= gradientThreshold;
|
||||
|
||||
}
|
||||
|
||||
if ( !nAtEnd ) {
|
||||
|
||||
nuv -= edgeStep * EDGE_GUESS;
|
||||
|
||||
}
|
||||
|
||||
float pDistance, nDistance;
|
||||
if ( e.isHorizontal ) {
|
||||
|
||||
pDistance = puv.x - uv.x;
|
||||
nDistance = uv.x - nuv.x;
|
||||
|
||||
} else {
|
||||
|
||||
pDistance = puv.y - uv.y;
|
||||
nDistance = uv.y - nuv.y;
|
||||
|
||||
}
|
||||
|
||||
float shortestDistance;
|
||||
bool deltaSign;
|
||||
if ( pDistance <= nDistance ) {
|
||||
|
||||
shortestDistance = pDistance;
|
||||
deltaSign = pLuminanceDelta >= 0.0;
|
||||
|
||||
} else {
|
||||
|
||||
shortestDistance = nDistance;
|
||||
deltaSign = nLuminanceDelta >= 0.0;
|
||||
|
||||
}
|
||||
|
||||
if ( deltaSign == ( l.m - edgeLuminance >= 0.0 ) ) {
|
||||
|
||||
return 0.0;
|
||||
|
||||
}
|
||||
|
||||
return 0.5 - shortestDistance / ( pDistance + nDistance );
|
||||
|
||||
}
|
||||
|
||||
vec4 ApplyFXAA( sampler2D tex2D, vec2 texSize, vec2 uv ) {
|
||||
|
||||
LuminanceData luminance = SampleLuminanceNeighborhood( tex2D, texSize, uv );
|
||||
if ( ShouldSkipPixel( luminance ) ) {
|
||||
|
||||
return Sample( tex2D, uv );
|
||||
|
||||
}
|
||||
|
||||
float pixelBlend = DeterminePixelBlendFactor( luminance );
|
||||
EdgeData edge = DetermineEdge( texSize, luminance );
|
||||
float edgeBlend = DetermineEdgeBlendFactor( tex2D, texSize, luminance, edge, uv );
|
||||
float finalBlend = max( pixelBlend, edgeBlend );
|
||||
|
||||
if (edge.isHorizontal) {
|
||||
|
||||
uv.y += edge.pixelStep * finalBlend;
|
||||
|
||||
} else {
|
||||
|
||||
uv.x += edge.pixelStep * finalBlend;
|
||||
|
||||
}
|
||||
|
||||
return Sample( tex2D, uv );
|
||||
|
||||
}
|
||||
|
||||
void main() {
|
||||
|
||||
gl_FragColor = ApplyFXAA( tDiffuse, resolution.xy, vUv );
|
||||
|
||||
}`
|
||||
|
||||
};
|
||||
|
||||
export { FXAAShader };
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* @module OutputShader
|
||||
* @three_import import { OutputShader } from 'three/addons/shaders/OutputShader.js';
|
||||
*/
|
||||
|
||||
/**
|
||||
* Performs tone mapping and color space conversion for
|
||||
* FX workflows.
|
||||
*
|
||||
* Used by {@link OutputPass}.
|
||||
*
|
||||
* @constant
|
||||
* @type {ShaderMaterial~Shader}
|
||||
*/
|
||||
const OutputShader = {
|
||||
|
||||
name: 'OutputShader',
|
||||
|
||||
uniforms: {
|
||||
|
||||
'tDiffuse': { value: null },
|
||||
'toneMappingExposure': { value: 1 }
|
||||
|
||||
},
|
||||
|
||||
vertexShader: /* glsl */`
|
||||
precision highp float;
|
||||
|
||||
uniform mat4 modelViewMatrix;
|
||||
uniform mat4 projectionMatrix;
|
||||
|
||||
attribute vec3 position;
|
||||
attribute vec2 uv;
|
||||
|
||||
varying vec2 vUv;
|
||||
|
||||
void main() {
|
||||
|
||||
vUv = uv;
|
||||
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
|
||||
|
||||
}`,
|
||||
|
||||
fragmentShader: /* glsl */`
|
||||
|
||||
precision highp float;
|
||||
|
||||
uniform sampler2D tDiffuse;
|
||||
|
||||
#include <tonemapping_pars_fragment>
|
||||
#include <colorspace_pars_fragment>
|
||||
|
||||
varying vec2 vUv;
|
||||
|
||||
void main() {
|
||||
|
||||
gl_FragColor = texture2D( tDiffuse, vUv );
|
||||
|
||||
// tone mapping
|
||||
|
||||
#ifdef LINEAR_TONE_MAPPING
|
||||
|
||||
gl_FragColor.rgb = LinearToneMapping( gl_FragColor.rgb );
|
||||
|
||||
#elif defined( REINHARD_TONE_MAPPING )
|
||||
|
||||
gl_FragColor.rgb = ReinhardToneMapping( gl_FragColor.rgb );
|
||||
|
||||
#elif defined( CINEON_TONE_MAPPING )
|
||||
|
||||
gl_FragColor.rgb = CineonToneMapping( gl_FragColor.rgb );
|
||||
|
||||
#elif defined( ACES_FILMIC_TONE_MAPPING )
|
||||
|
||||
gl_FragColor.rgb = ACESFilmicToneMapping( gl_FragColor.rgb );
|
||||
|
||||
#elif defined( AGX_TONE_MAPPING )
|
||||
|
||||
gl_FragColor.rgb = AgXToneMapping( gl_FragColor.rgb );
|
||||
|
||||
#elif defined( NEUTRAL_TONE_MAPPING )
|
||||
|
||||
gl_FragColor.rgb = NeutralToneMapping( gl_FragColor.rgb );
|
||||
|
||||
#elif defined( CUSTOM_TONE_MAPPING )
|
||||
|
||||
gl_FragColor.rgb = CustomToneMapping( gl_FragColor.rgb );
|
||||
|
||||
#endif
|
||||
|
||||
// color space
|
||||
|
||||
#ifdef SRGB_TRANSFER
|
||||
|
||||
gl_FragColor = sRGBTransferOETF( gl_FragColor );
|
||||
|
||||
#endif
|
||||
|
||||
}`
|
||||
|
||||
};
|
||||
|
||||
export { OutputShader };
|
||||
@@ -0,0 +1,321 @@
|
||||
import {
|
||||
Matrix4,
|
||||
Vector2
|
||||
} from 'three';
|
||||
|
||||
/**
|
||||
* @module SSAOShader
|
||||
* @three_import import { SSAOShader } from 'three/addons/shaders/SSAOShader.js';
|
||||
*/
|
||||
|
||||
/**
|
||||
* SSAO shader.
|
||||
*
|
||||
* References:
|
||||
* - {@link http://john-chapman-graphics.blogspot.com/2013/01/ssao-tutorial.html}
|
||||
* - {@link https://learnopengl.com/Advanced-Lighting/SSAO}
|
||||
* - {@link https://github.com/McNopper/OpenGL/blob/master/Example28/shader/ssao.frag.glsl}
|
||||
*
|
||||
* @constant
|
||||
* @type {ShaderMaterial~Shader}
|
||||
*/
|
||||
const SSAOShader = {
|
||||
|
||||
name: 'SSAOShader',
|
||||
|
||||
defines: {
|
||||
'PERSPECTIVE_CAMERA': 1,
|
||||
'KERNEL_SIZE': 32
|
||||
},
|
||||
|
||||
uniforms: {
|
||||
|
||||
'tNormal': { value: null },
|
||||
'tDepth': { value: null },
|
||||
'tNoise': { value: null },
|
||||
'kernel': { value: null },
|
||||
'cameraNear': { value: null },
|
||||
'cameraFar': { value: null },
|
||||
'resolution': { value: new Vector2() },
|
||||
'cameraProjectionMatrix': { value: new Matrix4() },
|
||||
'cameraInverseProjectionMatrix': { value: new Matrix4() },
|
||||
'kernelRadius': { value: 8 },
|
||||
'minDistance': { value: 0.005 },
|
||||
'maxDistance': { value: 0.05 },
|
||||
|
||||
},
|
||||
|
||||
vertexShader: /* glsl */`
|
||||
|
||||
varying vec2 vUv;
|
||||
|
||||
void main() {
|
||||
|
||||
vUv = uv;
|
||||
|
||||
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
|
||||
|
||||
}`,
|
||||
|
||||
fragmentShader: /* glsl */`
|
||||
uniform highp sampler2D tNormal;
|
||||
uniform highp sampler2D tDepth;
|
||||
uniform sampler2D tNoise;
|
||||
|
||||
uniform vec3 kernel[ KERNEL_SIZE ];
|
||||
|
||||
uniform vec2 resolution;
|
||||
|
||||
uniform float cameraNear;
|
||||
uniform float cameraFar;
|
||||
uniform mat4 cameraProjectionMatrix;
|
||||
uniform mat4 cameraInverseProjectionMatrix;
|
||||
|
||||
uniform float kernelRadius;
|
||||
uniform float minDistance; // avoid artifacts caused by neighbour fragments with minimal depth difference
|
||||
uniform float maxDistance; // avoid the influence of fragments which are too far away
|
||||
|
||||
varying vec2 vUv;
|
||||
|
||||
#include <packing>
|
||||
|
||||
float getDepth( const in vec2 screenPosition ) {
|
||||
|
||||
return texture2D( tDepth, screenPosition ).x;
|
||||
|
||||
}
|
||||
|
||||
float getLinearDepth( const in vec2 screenPosition ) {
|
||||
|
||||
#if PERSPECTIVE_CAMERA == 1
|
||||
|
||||
float fragCoordZ = texture2D( tDepth, screenPosition ).x;
|
||||
float viewZ = perspectiveDepthToViewZ( fragCoordZ, cameraNear, cameraFar );
|
||||
return viewZToOrthographicDepth( viewZ, cameraNear, cameraFar );
|
||||
|
||||
#else
|
||||
|
||||
return texture2D( tDepth, screenPosition ).x;
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
float getViewZ( const in float depth ) {
|
||||
|
||||
#if PERSPECTIVE_CAMERA == 1
|
||||
|
||||
return perspectiveDepthToViewZ( depth, cameraNear, cameraFar );
|
||||
|
||||
#else
|
||||
|
||||
return orthographicDepthToViewZ( depth, cameraNear, cameraFar );
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
vec3 getViewPosition( const in vec2 screenPosition, const in float depth, const in float viewZ ) {
|
||||
|
||||
float clipW = cameraProjectionMatrix[2][3] * viewZ + cameraProjectionMatrix[3][3];
|
||||
|
||||
vec4 clipPosition = vec4( ( vec3( screenPosition, depth ) - 0.5 ) * 2.0, 1.0 );
|
||||
|
||||
clipPosition *= clipW; // unprojection.
|
||||
|
||||
return ( cameraInverseProjectionMatrix * clipPosition ).xyz;
|
||||
|
||||
}
|
||||
|
||||
vec3 getViewNormal( const in vec2 screenPosition ) {
|
||||
|
||||
return unpackRGBToNormal( texture2D( tNormal, screenPosition ).xyz );
|
||||
|
||||
}
|
||||
|
||||
void main() {
|
||||
|
||||
float depth = getDepth( vUv );
|
||||
|
||||
if ( depth == 1.0 ) {
|
||||
|
||||
gl_FragColor = vec4( 1.0 ); // don't influence background
|
||||
|
||||
} else {
|
||||
|
||||
float viewZ = getViewZ( depth );
|
||||
|
||||
vec3 viewPosition = getViewPosition( vUv, depth, viewZ );
|
||||
vec3 viewNormal = getViewNormal( vUv );
|
||||
|
||||
vec2 noiseScale = vec2( resolution.x / 4.0, resolution.y / 4.0 );
|
||||
vec3 random = vec3( texture2D( tNoise, vUv * noiseScale ).r );
|
||||
|
||||
// compute matrix used to reorient a kernel vector
|
||||
|
||||
vec3 tangent = normalize( random - viewNormal * dot( random, viewNormal ) );
|
||||
vec3 bitangent = cross( viewNormal, tangent );
|
||||
mat3 kernelMatrix = mat3( tangent, bitangent, viewNormal );
|
||||
|
||||
float occlusion = 0.0;
|
||||
|
||||
for ( int i = 0; i < KERNEL_SIZE; i ++ ) {
|
||||
|
||||
vec3 sampleVector = kernelMatrix * kernel[ i ]; // reorient sample vector in view space
|
||||
vec3 samplePoint = viewPosition + ( sampleVector * kernelRadius ); // calculate sample point
|
||||
|
||||
vec4 samplePointNDC = cameraProjectionMatrix * vec4( samplePoint, 1.0 ); // project point and calculate NDC
|
||||
samplePointNDC /= samplePointNDC.w;
|
||||
|
||||
vec2 samplePointUv = samplePointNDC.xy * 0.5 + 0.5; // compute uv coordinates
|
||||
|
||||
float realDepth = getLinearDepth( samplePointUv ); // get linear depth from depth texture
|
||||
float sampleDepth = viewZToOrthographicDepth( samplePoint.z, cameraNear, cameraFar ); // compute linear depth of the sample view Z value
|
||||
float delta = sampleDepth - realDepth;
|
||||
|
||||
if ( delta > minDistance && delta < maxDistance ) { // if fragment is before sample point, increase occlusion
|
||||
|
||||
occlusion += 1.0;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
occlusion = clamp( occlusion / float( KERNEL_SIZE ), 0.0, 1.0 );
|
||||
|
||||
gl_FragColor = vec4( vec3( 1.0 - occlusion ), 1.0 );
|
||||
|
||||
}
|
||||
|
||||
}`
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* SSAO depth shader.
|
||||
*
|
||||
* @constant
|
||||
* @type {ShaderMaterial~Shader}
|
||||
*/
|
||||
const SSAODepthShader = {
|
||||
|
||||
name: 'SSAODepthShader',
|
||||
|
||||
defines: {
|
||||
'PERSPECTIVE_CAMERA': 1
|
||||
},
|
||||
|
||||
uniforms: {
|
||||
|
||||
'tDepth': { value: null },
|
||||
'cameraNear': { value: null },
|
||||
'cameraFar': { value: null },
|
||||
|
||||
},
|
||||
|
||||
vertexShader:
|
||||
|
||||
`varying vec2 vUv;
|
||||
|
||||
void main() {
|
||||
|
||||
vUv = uv;
|
||||
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
|
||||
|
||||
}`,
|
||||
|
||||
fragmentShader:
|
||||
|
||||
`uniform sampler2D tDepth;
|
||||
|
||||
uniform float cameraNear;
|
||||
uniform float cameraFar;
|
||||
|
||||
varying vec2 vUv;
|
||||
|
||||
#include <packing>
|
||||
|
||||
float getLinearDepth( const in vec2 screenPosition ) {
|
||||
|
||||
#if PERSPECTIVE_CAMERA == 1
|
||||
|
||||
float fragCoordZ = texture2D( tDepth, screenPosition ).x;
|
||||
float viewZ = perspectiveDepthToViewZ( fragCoordZ, cameraNear, cameraFar );
|
||||
return viewZToOrthographicDepth( viewZ, cameraNear, cameraFar );
|
||||
|
||||
#else
|
||||
|
||||
return texture2D( tDepth, screenPosition ).x;
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void main() {
|
||||
|
||||
float depth = getLinearDepth( vUv );
|
||||
gl_FragColor = vec4( vec3( 1.0 - depth ), 1.0 );
|
||||
|
||||
}`
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* SSAO blur shader.
|
||||
*
|
||||
* @constant
|
||||
* @type {Object}
|
||||
*/
|
||||
const SSAOBlurShader = {
|
||||
|
||||
name: 'SSAOBlurShader',
|
||||
|
||||
uniforms: {
|
||||
|
||||
'tDiffuse': { value: null },
|
||||
'resolution': { value: new Vector2() }
|
||||
|
||||
},
|
||||
|
||||
vertexShader:
|
||||
|
||||
`varying vec2 vUv;
|
||||
|
||||
void main() {
|
||||
|
||||
vUv = uv;
|
||||
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
|
||||
|
||||
}`,
|
||||
|
||||
fragmentShader:
|
||||
|
||||
`uniform sampler2D tDiffuse;
|
||||
|
||||
uniform vec2 resolution;
|
||||
|
||||
varying vec2 vUv;
|
||||
|
||||
void main() {
|
||||
|
||||
vec2 texelSize = ( 1.0 / resolution );
|
||||
float result = 0.0;
|
||||
|
||||
for ( int i = - 2; i <= 2; i ++ ) {
|
||||
|
||||
for ( int j = - 2; j <= 2; j ++ ) {
|
||||
|
||||
vec2 offset = ( vec2( float( i ), float( j ) ) ) * texelSize;
|
||||
result += texture2D( tDiffuse, vUv + offset ).r;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
gl_FragColor = vec4( vec3( result / ( 5.0 * 5.0 ) ), 1.0 );
|
||||
|
||||
}`
|
||||
|
||||
};
|
||||
|
||||
export { SSAOShader, SSAODepthShader, SSAOBlurShader };
|
||||
+1435
File diff suppressed because it is too large
Load Diff
+58773
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user