diff --git a/docs/manual/en/introduction/Import-via-modules.html b/docs/manual/en/introduction/Import-via-modules.html
index 4f8449f54ca0ea..5cc905306399ae 100644
--- a/docs/manual/en/introduction/Import-via-modules.html
+++ b/docs/manual/en/introduction/Import-via-modules.html
@@ -143,6 +143,11 @@
Importable Examples
VRMLLoader
+ math
+
+
objects
- Lensflare
@@ -177,6 +182,7 @@ Importable Examples
- ShaderPass
- SMAAPass
- SSAARenderPass
+ - SSAOPass
- TAARenderPass
- TexturePass
- UnrealBloomPass
diff --git a/examples/jsm/math/SimplexNoise.d.ts b/examples/jsm/math/SimplexNoise.d.ts
new file mode 100644
index 00000000000000..129311fa9d6810
--- /dev/null
+++ b/examples/jsm/math/SimplexNoise.d.ts
@@ -0,0 +1,9 @@
+export class SimplexNoise {
+ constructor(r?: object);
+ dot(g: number[], x: number, y: number): number;
+ dot3(g: number[], x: number, y: number, z: number): number;
+ dot4(g: number[], x: number, y: number, z: number, w: number): number;
+ noise(xin: number, yin: number): number;
+ noise3d(xin: number, yin: number, zin: number): number;
+ noise4d(x: number, y: number, z: number, w: number): number;
+}
diff --git a/examples/jsm/math/SimplexNoise.js b/examples/jsm/math/SimplexNoise.js
new file mode 100644
index 00000000000000..7547b8903ffd6d
--- /dev/null
+++ b/examples/jsm/math/SimplexNoise.js
@@ -0,0 +1,407 @@
+// Ported from Stefan Gustavson's java implementation
+// http://staffwww.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf
+// Read Stefan's excellent paper for details on how this code works.
+//
+// Sean McCullough banksean@gmail.com
+//
+// Added 4D noise
+// Joshua Koo zz85nus@gmail.com
+
+/**
+ * You can pass in a random number generator object if you like.
+ * It is assumed to have a random() method.
+ */
+var SimplexNoise = function ( r ) {
+
+ if ( r == undefined ) 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 ( var 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 ( var 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 ]];
+
+};
+
+SimplexNoise.prototype.dot = function ( g, x, y ) {
+
+ return g[ 0 ] * x + g[ 1 ] * y;
+
+};
+
+SimplexNoise.prototype.dot3 = function ( g, x, y, z ) {
+
+ return g[ 0 ] * x + g[ 1 ] * y + g[ 2 ] * z;
+
+};
+
+SimplexNoise.prototype.dot4 = function ( g, x, y, z, w ) {
+
+ return g[ 0 ] * x + g[ 1 ] * y + g[ 2 ] * z + g[ 3 ] * w;
+
+};
+
+SimplexNoise.prototype.noise = function ( xin, yin ) {
+
+ var n0, n1, n2; // Noise contributions from the three corners
+ // Skew the input space to determine which simplex cell we're in
+ var F2 = 0.5 * ( Math.sqrt( 3.0 ) - 1.0 );
+ var s = ( xin + yin ) * F2; // Hairy factor for 2D
+ var i = Math.floor( xin + s );
+ var j = Math.floor( yin + s );
+ var G2 = ( 3.0 - Math.sqrt( 3.0 ) ) / 6.0;
+ var t = ( i + j ) * G2;
+ var X0 = i - t; // Unskew the cell origin back to (x,y) space
+ var Y0 = j - t;
+ var x0 = xin - X0; // The x,y distances from the cell origin
+ var y0 = yin - Y0;
+ // For the 2D case, the simplex shape is an equilateral triangle.
+ // Determine which simplex we are in.
+ var i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords
+ 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
+ var x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords
+ var y1 = y0 - j1 + G2;
+ var x2 = x0 - 1.0 + 2.0 * G2; // Offsets for last corner in (x,y) unskewed coords
+ var y2 = y0 - 1.0 + 2.0 * G2;
+ // Work out the hashed gradient indices of the three simplex corners
+ var ii = i & 255;
+ var jj = j & 255;
+ var gi0 = this.perm[ ii + this.perm[ jj ] ] % 12;
+ var gi1 = this.perm[ ii + i1 + this.perm[ jj + j1 ] ] % 12;
+ var gi2 = this.perm[ ii + 1 + this.perm[ jj + 1 ] ] % 12;
+ // Calculate the contribution from the three corners
+ var 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
+
+ }
+ var 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 );
+
+ }
+ var 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 );
+
+};
+
+// 3D simplex noise
+SimplexNoise.prototype.noise3d = function ( xin, yin, zin ) {
+
+ var n0, n1, n2, n3; // Noise contributions from the four corners
+ // Skew the input space to determine which simplex cell we're in
+ var F3 = 1.0 / 3.0;
+ var s = ( xin + yin + zin ) * F3; // Very nice and simple skew factor for 3D
+ var i = Math.floor( xin + s );
+ var j = Math.floor( yin + s );
+ var k = Math.floor( zin + s );
+ var G3 = 1.0 / 6.0; // Very nice and simple unskew factor, too
+ var t = ( i + j + k ) * G3;
+ var X0 = i - t; // Unskew the cell origin back to (x,y,z) space
+ var Y0 = j - t;
+ var Z0 = k - t;
+ var x0 = xin - X0; // The x,y,z distances from the cell origin
+ var y0 = yin - Y0;
+ var z0 = zin - Z0;
+ // For the 3D case, the simplex shape is a slightly irregular tetrahedron.
+ // Determine which simplex we are in.
+ var i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords
+ var i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords
+ 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 ) ? 32 : 0;
+ var c2 = ( x0 > z0 ) ? 16 : 0;
+ var c3 = ( y0 > z0 ) ? 8 : 0;
+ var c4 = ( x0 > w0 ) ? 4 : 0;
+ var c5 = ( y0 > w0 ) ? 2 : 0;
+ var c6 = ( z0 > w0 ) ? 1 : 0;
+ var c = c1 + c2 + c3 + c4 + c5 + c6;
+ var i1, j1, k1, l1; // The integer offsets for the second simplex corner
+ var i2, j2, k2, l2; // The integer offsets for the third simplex corner
+ var i3, j3, k3, l3; // The integer offsets for the fourth simplex corner
+ // 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= 3 ? 1 : 0;
+ j1 = simplex[ c ][ 1 ] >= 3 ? 1 : 0;
+ k1 = simplex[ c ][ 2 ] >= 3 ? 1 : 0;
+ l1 = simplex[ c ][ 3 ] >= 3 ? 1 : 0;
+ // The number 2 in the "simplex" array is at the second largest coordinate.
+ i2 = simplex[ c ][ 0 ] >= 2 ? 1 : 0;
+ j2 = simplex[ c ][ 1 ] >= 2 ? 1 : 0; k2 = simplex[ c ][ 2 ] >= 2 ? 1 : 0;
+ l2 = simplex[ c ][ 3 ] >= 2 ? 1 : 0;
+ // The number 1 in the "simplex" array is at the second smallest coordinate.
+ i3 = simplex[ c ][ 0 ] >= 1 ? 1 : 0;
+ j3 = simplex[ c ][ 1 ] >= 1 ? 1 : 0;
+ k3 = simplex[ c ][ 2 ] >= 1 ? 1 : 0;
+ l3 = simplex[ c ][ 3 ] >= 1 ? 1 : 0;
+ // The fifth corner has all coordinate offsets = 1, so no need to look that up.
+ var x1 = x0 - i1 + G4; // Offsets for second corner in (x,y,z,w) coords
+ var y1 = y0 - j1 + G4;
+ var z1 = z0 - k1 + G4;
+ var w1 = w0 - l1 + G4;
+ var x2 = x0 - i2 + 2.0 * G4; // Offsets for third corner in (x,y,z,w) coords
+ var y2 = y0 - j2 + 2.0 * G4;
+ var z2 = z0 - k2 + 2.0 * G4;
+ var w2 = w0 - l2 + 2.0 * G4;
+ var x3 = x0 - i3 + 3.0 * G4; // Offsets for fourth corner in (x,y,z,w) coords
+ var y3 = y0 - j3 + 3.0 * G4;
+ var z3 = z0 - k3 + 3.0 * G4;
+ var w3 = w0 - l3 + 3.0 * G4;
+ var x4 = x0 - 1.0 + 4.0 * G4; // Offsets for last corner in (x,y,z,w) coords
+ var y4 = y0 - 1.0 + 4.0 * G4;
+ var z4 = z0 - 1.0 + 4.0 * G4;
+ var w4 = w0 - 1.0 + 4.0 * G4;
+ // Work out the hashed gradient indices of the five simplex corners
+ var ii = i & 255;
+ var jj = j & 255;
+ var kk = k & 255;
+ var ll = l & 255;
+ var gi0 = perm[ ii + perm[ jj + perm[ kk + perm[ ll ] ] ] ] % 32;
+ var gi1 = perm[ ii + i1 + perm[ jj + j1 + perm[ kk + k1 + perm[ ll + l1 ] ] ] ] % 32;
+ var gi2 = perm[ ii + i2 + perm[ jj + j2 + perm[ kk + k2 + perm[ ll + l2 ] ] ] ] % 32;
+ var gi3 = perm[ ii + i3 + perm[ jj + j3 + perm[ kk + k3 + perm[ ll + l3 ] ] ] ] % 32;
+ var gi4 = perm[ ii + 1 + perm[ jj + 1 + perm[ kk + 1 + perm[ ll + 1 ] ] ] ] % 32;
+ // Calculate the contribution from the five corners
+ var 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 );
+
+ }
+ var 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 );
+
+ }
+ var 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 );
+
+ } var 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 );
+
+ }
+ var 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 );
+
+};
+
+export { SimplexNoise };
diff --git a/examples/jsm/postprocessing/SSAOPass.d.ts b/examples/jsm/postprocessing/SSAOPass.d.ts
new file mode 100644
index 00000000000000..11bf44bab75bf8
--- /dev/null
+++ b/examples/jsm/postprocessing/SSAOPass.d.ts
@@ -0,0 +1,56 @@
+import {
+ Camera,
+ Color,
+ DataTexture,
+ Material,
+ MeshNormalMaterial,
+ Scene,
+ ShaderMaterial,
+ Vector3,
+ WebGLRenderer,
+ WebGLRenderTarget
+} from '../../../src/Three';
+
+import { Pass } from './Pass';
+
+export class SSAOPass extends Pass {
+ constructor(scene: Scene, camera: Camera, width?: number, height?: number);
+ scene: Scene;
+ camera: Camera;
+ width: number;
+ height: boolean;
+ clear: boolean;
+ kernelRadius: boolean;
+ kernelSize: boolean;
+ kernel: Vector3[];
+ noiseTexture: DataTexture;
+ output: number;
+ minDistance: number;
+ maxDistance: number;
+ beautyRenderTarget: WebGLRenderTarget;
+ normalRenderTarget: WebGLRenderTarget;
+ ssaoRenderTarget: WebGLRenderTarget;
+ blurRenderTarget: WebGLRenderTarget;
+ ssaoMaterial: ShaderMaterial;
+ normalMaterial: MeshNormalMaterial;
+ blurMaterial: ShaderMaterial;
+ depthRenderMaterial: ShaderMaterial;
+ copyMaterial: ShaderMaterial;
+ fsQuad: object;
+ originalClearColor: Color;
+
+ static OUTPUT: {
+ Default: number;
+ SSAO: number;
+ Blur: number;
+ Beauty: number;
+ Depth: number;
+ Normal: number;
+ };
+
+ dipose(): void;
+ generateSampleKernel(): Vector3[];
+ generateRandomKernelRotations(): void;
+ renderPass(renderer: WebGLRenderer, passMaterial: Material, renderTarget: WebGLRenderer, clearColor: Color, clearAlpha: number): void;
+ renderOverride(renderer: WebGLRenderer, overrideMaterial: Material, renderTarget: WebGLRenderer, clearColor: Color, clearAlpha: number): void;
+}
diff --git a/examples/jsm/postprocessing/SSAOPass.js b/examples/jsm/postprocessing/SSAOPass.js
new file mode 100644
index 00000000000000..f59e922a220bd5
--- /dev/null
+++ b/examples/jsm/postprocessing/SSAOPass.js
@@ -0,0 +1,436 @@
+/**
+ * @author Mugen87 / https://github.com/Mugen87
+ */
+
+import {
+ AddEquation,
+ Color,
+ CustomBlending,
+ DataTexture,
+ DepthTexture,
+ DstAlphaFactor,
+ DstColorFactor,
+ FloatType,
+ LinearFilter,
+ Math as _Math,
+ MeshNormalMaterial,
+ NearestFilter,
+ NoBlending,
+ RGBAFormat,
+ RepeatWrapping,
+ ShaderMaterial,
+ UniformsUtils,
+ UnsignedShortType,
+ Vector3,
+ WebGLRenderTarget,
+ ZeroFactor
+} from "../../../build/three.module.js";
+import { Pass } from "../postprocessing/Pass.js";
+import { SimplexNoise } from "../math/SimplexNoise.js";
+import { SSAOShader } from "../shaders/SSAOShader.js";
+import { SSAOBlurShader } from "../shaders/SSAOShader.js";
+import { SSAODepthShader } from "../shaders/SSAOShader.js";
+import { CopyShader } from "../shaders/CopyShader.js";
+
+var SSAOPass = function ( scene, camera, width, height ) {
+
+ Pass.call( this );
+
+ this.width = ( width !== undefined ) ? width : 512;
+ this.height = ( height !== undefined ) ? height : 512;
+
+ this.clear = true;
+
+ this.camera = camera;
+ this.scene = scene;
+
+ this.kernelRadius = 8;
+ this.kernelSize = 32;
+ this.kernel = [];
+ this.noiseTexture = null;
+ this.output = 0;
+
+ this.minDistance = 0.005;
+ this.maxDistance = 0.1;
+
+ //
+
+ this.generateSampleKernel();
+ this.generateRandomKernelRotations();
+
+ // beauty render target with depth buffer
+
+ var depthTexture = new DepthTexture();
+ depthTexture.type = UnsignedShortType;
+ depthTexture.minFilter = NearestFilter;
+ depthTexture.maxFilter = NearestFilter;
+
+ this.beautyRenderTarget = new WebGLRenderTarget( this.width, this.height, {
+ minFilter: LinearFilter,
+ magFilter: LinearFilter,
+ format: RGBAFormat,
+ depthTexture: depthTexture,
+ depthBuffer: true
+ } );
+
+ // normal render target
+
+ this.normalRenderTarget = new WebGLRenderTarget( this.width, this.height, {
+ minFilter: NearestFilter,
+ magFilter: NearestFilter,
+ format: RGBAFormat
+ } );
+
+ // ssao render target
+
+ this.ssaoRenderTarget = new WebGLRenderTarget( this.width, this.height, {
+ minFilter: LinearFilter,
+ magFilter: LinearFilter,
+ format: RGBAFormat
+ } );
+
+ this.blurRenderTarget = this.ssaoRenderTarget.clone();
+
+ // ssao material
+
+ if ( SSAOShader === undefined ) {
+
+ console.error( 'THREE.SSAOPass: The pass relies on SSAOShader.' );
+
+ }
+
+ this.ssaoMaterial = new ShaderMaterial( {
+ defines: Object.assign( {}, SSAOShader.defines ),
+ uniforms: UniformsUtils.clone( SSAOShader.uniforms ),
+ vertexShader: SSAOShader.vertexShader,
+ fragmentShader: SSAOShader.fragmentShader,
+ blending: NoBlending
+ } );
+
+ this.ssaoMaterial.uniforms[ 'tDiffuse' ].value = this.beautyRenderTarget.texture;
+ this.ssaoMaterial.uniforms[ 'tNormal' ].value = this.normalRenderTarget.texture;
+ this.ssaoMaterial.uniforms[ 'tDepth' ].value = this.beautyRenderTarget.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.getInverse( this.camera.projectionMatrix );
+
+ // 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.beautyRenderTarget.depthTexture;
+ this.depthRenderMaterial.uniforms[ 'cameraNear' ].value = this.camera.near;
+ this.depthRenderMaterial.uniforms[ 'cameraFar' ].value = this.camera.far;
+
+ // material for rendering the content of a render target
+
+ this.copyMaterial = new ShaderMaterial( {
+ uniforms: UniformsUtils.clone( CopyShader.uniforms ),
+ vertexShader: CopyShader.vertexShader,
+ fragmentShader: CopyShader.fragmentShader,
+ transparent: true,
+ depthTest: false,
+ depthWrite: false,
+ blendSrc: DstColorFactor,
+ blendDst: ZeroFactor,
+ blendEquation: AddEquation,
+ blendSrcAlpha: DstAlphaFactor,
+ blendDstAlpha: ZeroFactor,
+ blendEquationAlpha: AddEquation
+ } );
+
+ this.fsQuad = new Pass.FullScreenQuad( null );
+
+ this.originalClearColor = new Color();
+
+};
+
+SSAOPass.prototype = Object.assign( Object.create( Pass.prototype ), {
+
+ constructor: SSAOPass,
+
+ dispose: function () {
+
+ // dispose render targets
+
+ this.beautyRenderTarget.dispose();
+ this.normalRenderTarget.dispose();
+ this.ssaoRenderTarget.dispose();
+ this.blurRenderTarget.dispose();
+
+ // dispose geometry
+
+ this.quad.geometry.dispose();
+
+ // dispose materials
+
+ this.normalMaterial.dispose();
+ this.blurMaterial.dispose();
+ this.copyMaterial.dispose();
+ this.depthRenderMaterial.dispose();
+
+ },
+
+ render: function ( renderer, writeBuffer /*, readBuffer, deltaTime, maskActive */ ) {
+
+ // render beauty and depth
+
+ renderer.setRenderTarget( this.beautyRenderTarget );
+ renderer.clear();
+ renderer.render( this.scene, this.camera );
+
+ // render normals
+
+ this.renderOverride( renderer, this.normalMaterial, this.normalRenderTarget, 0x7777ff, 1.0 );
+
+ // render SSAO
+
+ this.ssaoMaterial.uniforms[ 'kernelRadius' ].value = this.kernelRadius;
+ this.ssaoMaterial.uniforms[ 'minDistance' ].value = this.minDistance;
+ this.ssaoMaterial.uniforms[ 'maxDistance' ].value = this.maxDistance;
+ this.renderPass( renderer, this.ssaoMaterial, this.ssaoRenderTarget );
+
+ // render blur
+
+ this.renderPass( renderer, this.blurMaterial, this.blurRenderTarget );
+
+ // output result to screen
+
+ switch ( this.output ) {
+
+ case SSAOPass.OUTPUT.SSAO:
+
+ this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.ssaoRenderTarget.texture;
+ this.copyMaterial.blending = NoBlending;
+ this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer );
+
+ break;
+
+ case SSAOPass.OUTPUT.Blur:
+
+ this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.blurRenderTarget.texture;
+ this.copyMaterial.blending = NoBlending;
+ this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer );
+
+ break;
+
+ case SSAOPass.OUTPUT.Beauty:
+
+ this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.beautyRenderTarget.texture;
+ this.copyMaterial.blending = NoBlending;
+ this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer );
+
+ break;
+
+ case SSAOPass.OUTPUT.Depth:
+
+ this.renderPass( renderer, this.depthRenderMaterial, this.renderToScreen ? null : writeBuffer );
+
+ break;
+
+ case SSAOPass.OUTPUT.Normal:
+
+ this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.normalRenderTarget.texture;
+ this.copyMaterial.blending = NoBlending;
+ this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer );
+
+ break;
+
+ case SSAOPass.OUTPUT.Default:
+
+ this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.beautyRenderTarget.texture;
+ this.copyMaterial.blending = NoBlending;
+ this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer );
+
+ this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.blurRenderTarget.texture;
+ this.copyMaterial.blending = CustomBlending;
+ this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer );
+
+ break;
+
+ default:
+ console.warn( 'THREE.SSAOPass: Unknown output type.' );
+
+ }
+
+ },
+
+ renderPass: function ( renderer, passMaterial, renderTarget, clearColor, clearAlpha ) {
+
+ // save original state
+ this.originalClearColor.copy( renderer.getClearColor() );
+ var originalClearAlpha = renderer.getClearAlpha();
+ var 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: function ( renderer, overrideMaterial, renderTarget, clearColor, clearAlpha ) {
+
+ this.originalClearColor.copy( renderer.getClearColor() );
+ var originalClearAlpha = renderer.getClearAlpha();
+ var originalAutoClear = renderer.autoClear;
+
+ renderer.setRenderTarget( renderTarget );
+ renderer.autoClear = false;
+
+ clearColor = overrideMaterial.clearColor || clearColor;
+ clearAlpha = overrideMaterial.clearAlpha || clearAlpha;
+
+ if ( ( clearColor !== undefined ) && ( clearColor !== null ) ) {
+
+ renderer.setClearColor( clearColor );
+ renderer.setClearAlpha( clearAlpha || 0.0 );
+ renderer.clear();
+
+ }
+
+ this.scene.overrideMaterial = overrideMaterial;
+ renderer.render( this.scene, this.camera );
+ this.scene.overrideMaterial = null;
+
+ // restore original state
+
+ renderer.autoClear = originalAutoClear;
+ renderer.setClearColor( this.originalClearColor );
+ renderer.setClearAlpha( originalClearAlpha );
+
+ },
+
+ setSize: function ( width, height ) {
+
+ this.width = width;
+ this.height = height;
+
+ this.beautyRenderTarget.setSize( width, 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.getInverse( this.camera.projectionMatrix );
+
+ this.blurMaterial.uniforms[ 'resolution' ].value.set( width, height );
+
+ },
+
+ generateSampleKernel: function () {
+
+ var kernelSize = this.kernelSize;
+ var kernel = this.kernel;
+
+ for ( var i = 0; i < kernelSize; i ++ ) {
+
+ var sample = new Vector3();
+ sample.x = ( Math.random() * 2 ) - 1;
+ sample.y = ( Math.random() * 2 ) - 1;
+ sample.z = Math.random();
+
+ sample.normalize();
+
+ var scale = i / kernelSize;
+ scale = _Math.lerp( 0.1, 1, scale * scale );
+ sample.multiplyScalar( scale );
+
+ kernel.push( sample );
+
+ }
+
+ },
+
+ generateRandomKernelRotations: function () {
+
+ var width = 4, height = 4;
+
+ if ( SimplexNoise === undefined ) {
+
+ console.error( 'THREE.SSAOPass: The pass relies on SimplexNoise.' );
+
+ }
+
+ var simplex = new SimplexNoise();
+
+ var size = width * height;
+ var data = new Float32Array( size * 4 );
+
+ for ( var i = 0; i < size; i ++ ) {
+
+ var stride = i * 4;
+
+ var x = ( Math.random() * 2 ) - 1;
+ var y = ( Math.random() * 2 ) - 1;
+ var z = 0;
+
+ var noise = simplex.noise3d( x, y, z );
+
+ data[ stride ] = noise;
+ data[ stride + 1 ] = noise;
+ data[ stride + 2 ] = noise;
+ data[ stride + 3 ] = 1;
+
+ }
+
+ this.noiseTexture = new DataTexture( data, width, height, RGBAFormat, FloatType );
+ this.noiseTexture.wrapS = RepeatWrapping;
+ this.noiseTexture.wrapT = RepeatWrapping;
+ this.noiseTexture.needsUpdate = true;
+
+ }
+
+} );
+
+SSAOPass.OUTPUT = {
+ 'Default': 0,
+ 'SSAO': 1,
+ 'Blur': 2,
+ 'Beauty': 3,
+ 'Depth': 4,
+ 'Normal': 5
+};
+
+export { SSAOPass };
diff --git a/utils/modularize.js b/utils/modularize.js
index a10f1b0235a6c4..59015d7f2d0fb6 100644
--- a/utils/modularize.js
+++ b/utils/modularize.js
@@ -61,6 +61,8 @@ var files = [
{ path: 'loaders/TGALoader.js', dependencies: [], ignoreList: [] },
{ path: 'loaders/VRMLLoader.js', dependencies: [], ignoreList: [] },
+ { path: 'math/SimplexNoise.js', dependencies: [], ignoreList: [] },
+
{ path: 'objects/Lensflare.js', dependencies: [], ignoreList: [] },
{ path: 'objects/Reflector.js', dependencies: [], ignoreList: [] },
{ path: 'objects/Refractor.js', dependencies: [], ignoreList: [] },
@@ -87,6 +89,7 @@ var files = [
{ path: 'postprocessing/ShaderPass.js', dependencies: [ { name: 'Pass', path: 'postprocessing/Pass.js' } ], ignoreList: [] },
{ path: 'postprocessing/SMAAPass.js', dependencies: [ { name: 'Pass', path: 'postprocessing/Pass.js' }, { name: 'SMAAEdgesShader', path: 'shaders/SMAAShader.js' }, { name: 'SMAAWeightsShader', path: 'shaders/SMAAShader.js' }, { name: 'SMAABlendShader', path: 'shaders/SMAAShader.js' } ], ignoreList: [] },
{ path: 'postprocessing/SSAARenderPass.js', dependencies: [ { name: 'Pass', path: 'postprocessing/Pass.js' }, { name: 'CopyShader', path: 'shaders/CopyShader.js' } ], ignoreList: [] },
+ { path: 'postprocessing/SSAOPass.js', dependencies: [ { name: 'Pass', path: 'postprocessing/Pass.js' }, { name: 'SimplexNoise', path: 'math/SimplexNoise.js' }, { name: 'SSAOShader', path: 'shaders/SSAOShader.js' }, { name: 'SSAOBlurShader', path: 'shaders/SSAOShader.js' }, { name: 'SSAODepthShader', path: 'shaders/SSAOShader.js' }, { name: 'CopyShader', path: 'shaders/CopyShader.js' } ], ignoreList: [] },
{ path: 'postprocessing/TAARenderPass.js', dependencies: [ { name: 'SSAARenderPass', path: 'postprocessing/SSAARenderPass.js' } ], ignoreList: [] },
{ path: 'postprocessing/TexturePass.js', dependencies: [ { name: 'Pass', path: 'postprocessing/Pass.js' }, { name: 'CopyShader', path: 'shaders/CopyShader.js' } ], ignoreList: [] },
{ path: 'postprocessing/UnrealBloomPass.js', dependencies: [ { name: 'Pass', path: 'postprocessing/Pass.js' }, { name: 'CopyShader', path: 'shaders/CopyShader.js' }, { name: 'LuminosityHighPassShader', path: 'shaders/LuminosityHighPassShader.js' } ], ignoreList: [] },