Przeglądaj źródła

Merge pull request #16547 from Mugen87/dev32

JSM: Added more modules and TS files for "objects".
Michael Herzog 6 lat temu
rodzic
commit
fbb6ed6296

+ 5 - 0
docs/manual/en/introduction/Import-via-modules.html

@@ -153,6 +153,11 @@
 						<li>Lensflare</li>
 						<li>Lensflare</li>
 						<li>Reflector</li>
 						<li>Reflector</li>
 						<li>Refractor</li>
 						<li>Refractor</li>
+						<li>ReflectorRTT</li>
+						<li>ShadowMesh</li>
+						<li>Sky</li>
+						<li>Water</li>
+						<li>Water2</li>
 					</ul>
 					</ul>
 				</li>
 				</li>
 				<li>pmrem
 				<li>pmrem

+ 4 - 0
examples/js/objects/ReflectorRTT.js

@@ -1,3 +1,7 @@
+/**
+ * RTT version
+ */
+
 THREE.ReflectorRTT = function ( geometry, options ) {
 THREE.ReflectorRTT = function ( geometry, options ) {
 
 
 	THREE.Reflector.call( this, geometry, options );
 	THREE.Reflector.call( this, geometry, options );

+ 10 - 10
examples/js/objects/ShadowMesh.js

@@ -42,23 +42,23 @@ THREE.ShadowMesh.prototype.update = function () {
 
 
 		var sme = shadowMatrix.elements;
 		var sme = shadowMatrix.elements;
 
 
-		sme[ 0 ]  = dot - lightPosition4D.x * plane.normal.x;
-		sme[ 4 ]  = - lightPosition4D.x * plane.normal.y;
-		sme[ 8 ]  = - lightPosition4D.x * plane.normal.z;
+		sme[ 0 ] = dot - lightPosition4D.x * plane.normal.x;
+		sme[ 4 ] = - lightPosition4D.x * plane.normal.y;
+		sme[ 8 ] = - lightPosition4D.x * plane.normal.z;
 		sme[ 12 ] = - lightPosition4D.x * - plane.constant;
 		sme[ 12 ] = - lightPosition4D.x * - plane.constant;
 
 
-		sme[ 1 ]  = - lightPosition4D.y * plane.normal.x;
-		sme[ 5 ]  = dot - lightPosition4D.y * plane.normal.y;
-		sme[ 9 ]  = - lightPosition4D.y * plane.normal.z;
+		sme[ 1 ] = - lightPosition4D.y * plane.normal.x;
+		sme[ 5 ] = dot - lightPosition4D.y * plane.normal.y;
+		sme[ 9 ] = - lightPosition4D.y * plane.normal.z;
 		sme[ 13 ] = - lightPosition4D.y * - plane.constant;
 		sme[ 13 ] = - lightPosition4D.y * - plane.constant;
 
 
-		sme[ 2 ]  = - lightPosition4D.z * plane.normal.x;
-		sme[ 6 ]  = - lightPosition4D.z * plane.normal.y;
+		sme[ 2 ] = - lightPosition4D.z * plane.normal.x;
+		sme[ 6 ] = - lightPosition4D.z * plane.normal.y;
 		sme[ 10 ] = dot - lightPosition4D.z * plane.normal.z;
 		sme[ 10 ] = dot - lightPosition4D.z * plane.normal.z;
 		sme[ 14 ] = - lightPosition4D.z * - plane.constant;
 		sme[ 14 ] = - lightPosition4D.z * - plane.constant;
 
 
-		sme[ 3 ]  = - lightPosition4D.w * plane.normal.x;
-		sme[ 7 ]  = - lightPosition4D.w * plane.normal.y;
+		sme[ 3 ] = - lightPosition4D.w * plane.normal.x;
+		sme[ 7 ] = - lightPosition4D.w * plane.normal.y;
 		sme[ 11 ] = - lightPosition4D.w * plane.normal.z;
 		sme[ 11 ] = - lightPosition4D.w * plane.normal.z;
 		sme[ 15 ] = dot - lightPosition4D.w * - plane.constant;
 		sme[ 15 ] = dot - lightPosition4D.w * - plane.constant;
 
 

+ 6 - 0
examples/jsm/objects/ReflectorRTT.d.ts

@@ -0,0 +1,6 @@
+import { BufferGeometry } from '../../../src/Three';
+import { Reflector, ReflectorOptions } from './Reflector';
+
+export class ReflectorRTT extends Reflector {
+  constructor(geometry?: BufferGeometry, options?: ReflectorOptions);
+}

+ 18 - 0
examples/jsm/objects/ReflectorRTT.js

@@ -0,0 +1,18 @@
+/**
+ * RTT version
+ */
+
+
+import { Reflector } from "../objects/Reflector.js";
+
+var ReflectorRTT = function ( geometry, options ) {
+
+	Reflector.call( this, geometry, options );
+
+	this.geometry.setDrawRange( 0, 0 ); // avoid rendering geometry
+
+};
+
+ReflectorRTT.prototype = Object.create( Reflector.prototype );
+
+export { ReflectorRTT };

+ 11 - 0
examples/jsm/objects/ShadowMesh.d.ts

@@ -0,0 +1,11 @@
+import {
+  Mesh,
+  Plane,
+  Vector4
+} from '../../../src/Three';
+
+export class ShadowMesh extends Mesh {
+  constructor();
+
+  update(plane: Plane, lightPosition4D: Vector4): void;
+}

+ 77 - 0
examples/jsm/objects/ShadowMesh.js

@@ -0,0 +1,77 @@
+/**
+ * @author erichlof /  http://github.com/erichlof
+ *
+ * A shadow Mesh that follows a shadow-casting Mesh in the scene, but is confined to a single plane.
+ */
+
+import {
+	Matrix4,
+	Mesh,
+	MeshBasicMaterial
+} from "../../../build/three.module.js";
+
+var ShadowMesh = function ( mesh ) {
+
+	var shadowMaterial = new MeshBasicMaterial( {
+
+		color: 0x000000,
+		transparent: true,
+		opacity: 0.6,
+		depthWrite: false
+
+	} );
+
+	Mesh.call( this, mesh.geometry, shadowMaterial );
+
+	this.meshMatrix = mesh.matrixWorld;
+
+	this.frustumCulled = false;
+	this.matrixAutoUpdate = false;
+
+};
+
+ShadowMesh.prototype = Object.create( Mesh.prototype );
+ShadowMesh.prototype.constructor = ShadowMesh;
+
+ShadowMesh.prototype.update = function () {
+
+	var shadowMatrix = new Matrix4();
+
+	return function ( plane, lightPosition4D ) {
+
+		// based on https://www.opengl.org/archives/resources/features/StencilTalk/tsld021.htm
+
+		var dot = plane.normal.x * lightPosition4D.x +
+			  plane.normal.y * lightPosition4D.y +
+			  plane.normal.z * lightPosition4D.z +
+			  - plane.constant * lightPosition4D.w;
+
+		var sme = shadowMatrix.elements;
+
+		sme[ 0 ] = dot - lightPosition4D.x * plane.normal.x;
+		sme[ 4 ] = - lightPosition4D.x * plane.normal.y;
+		sme[ 8 ] = - lightPosition4D.x * plane.normal.z;
+		sme[ 12 ] = - lightPosition4D.x * - plane.constant;
+
+		sme[ 1 ] = - lightPosition4D.y * plane.normal.x;
+		sme[ 5 ] = dot - lightPosition4D.y * plane.normal.y;
+		sme[ 9 ] = - lightPosition4D.y * plane.normal.z;
+		sme[ 13 ] = - lightPosition4D.y * - plane.constant;
+
+		sme[ 2 ] = - lightPosition4D.z * plane.normal.x;
+		sme[ 6 ] = - lightPosition4D.z * plane.normal.y;
+		sme[ 10 ] = dot - lightPosition4D.z * plane.normal.z;
+		sme[ 14 ] = - lightPosition4D.z * - plane.constant;
+
+		sme[ 3 ] = - lightPosition4D.w * plane.normal.x;
+		sme[ 7 ] = - lightPosition4D.w * plane.normal.y;
+		sme[ 11 ] = - lightPosition4D.w * plane.normal.z;
+		sme[ 15 ] = dot - lightPosition4D.w * - plane.constant;
+
+		this.matrix.multiplyMatrices( shadowMatrix, this.meshMatrix );
+
+	};
+
+}();
+
+export { ShadowMesh };

+ 9 - 0
examples/jsm/objects/Sky.d.ts

@@ -0,0 +1,9 @@
+import {
+  Mesh
+} from '../../../src/Three';
+
+export class Sky extends Mesh {
+  constructor();
+
+  static SkyShader: object;
+}

+ 232 - 0
examples/jsm/objects/Sky.js

@@ -0,0 +1,232 @@
+/**
+ * @author zz85 / https://github.com/zz85
+ *
+ * Based on "A Practical Analytic Model for Daylight"
+ * aka The Preetham Model, the de facto standard analytic skydome model
+ * http://www.cs.utah.edu/~shirley/papers/sunsky/sunsky.pdf
+ *
+ * First implemented by Simon Wallner
+ * http://www.simonwallner.at/projects/atmospheric-scattering
+ *
+ * Improved by Martin Upitis
+ * http://blenderartists.org/forum/showthread.php?245954-preethams-sky-impementation-HDR
+ *
+ * Three.js integration by zz85 http://twitter.com/blurspline
+*/
+
+import {
+	BackSide,
+	BoxBufferGeometry,
+	Mesh,
+	ShaderMaterial,
+	UniformsUtils,
+	Vector3
+} from "../../../build/three.module.js";
+
+var Sky = function () {
+
+	var shader = Sky.SkyShader;
+
+	var material = new ShaderMaterial( {
+		fragmentShader: shader.fragmentShader,
+		vertexShader: shader.vertexShader,
+		uniforms: UniformsUtils.clone( shader.uniforms ),
+		side: BackSide
+	} );
+
+	Mesh.call( this, new BoxBufferGeometry( 1, 1, 1 ), material );
+
+};
+
+Sky.prototype = Object.create( Mesh.prototype );
+
+Sky.SkyShader = {
+
+	uniforms: {
+		"luminance": { value: 1 },
+		"turbidity": { value: 2 },
+		"rayleigh": { value: 1 },
+		"mieCoefficient": { value: 0.005 },
+		"mieDirectionalG": { value: 0.8 },
+		"sunPosition": { value: new Vector3() }
+	},
+
+	vertexShader: [
+		'uniform vec3 sunPosition;',
+		'uniform float rayleigh;',
+		'uniform float turbidity;',
+		'uniform float mieCoefficient;',
+
+		'varying vec3 vWorldPosition;',
+		'varying vec3 vSunDirection;',
+		'varying float vSunfade;',
+		'varying vec3 vBetaR;',
+		'varying vec3 vBetaM;',
+		'varying float vSunE;',
+
+		'const vec3 up = vec3( 0.0, 1.0, 0.0 );',
+
+		// constants for atmospheric scattering
+		'const float e = 2.71828182845904523536028747135266249775724709369995957;',
+		'const float pi = 3.141592653589793238462643383279502884197169;',
+
+		// wavelength of used primaries, according to preetham
+		'const vec3 lambda = vec3( 680E-9, 550E-9, 450E-9 );',
+		// this pre-calcuation replaces older TotalRayleigh(vec3 lambda) function:
+		// (8.0 * pow(pi, 3.0) * pow(pow(n, 2.0) - 1.0, 2.0) * (6.0 + 3.0 * pn)) / (3.0 * N * pow(lambda, vec3(4.0)) * (6.0 - 7.0 * pn))
+		'const vec3 totalRayleigh = vec3( 5.804542996261093E-6, 1.3562911419845635E-5, 3.0265902468824876E-5 );',
+
+		// mie stuff
+		// K coefficient for the primaries
+		'const float v = 4.0;',
+		'const vec3 K = vec3( 0.686, 0.678, 0.666 );',
+		// MieConst = pi * pow( ( 2.0 * pi ) / lambda, vec3( v - 2.0 ) ) * K
+		'const vec3 MieConst = vec3( 1.8399918514433978E14, 2.7798023919660528E14, 4.0790479543861094E14 );',
+
+		// earth shadow hack
+		// cutoffAngle = pi / 1.95;
+		'const float cutoffAngle = 1.6110731556870734;',
+		'const float steepness = 1.5;',
+		'const float EE = 1000.0;',
+
+		'float sunIntensity( float zenithAngleCos ) {',
+		'	zenithAngleCos = clamp( zenithAngleCos, -1.0, 1.0 );',
+		'	return EE * max( 0.0, 1.0 - pow( e, -( ( cutoffAngle - acos( zenithAngleCos ) ) / steepness ) ) );',
+		'}',
+
+		'vec3 totalMie( float T ) {',
+		'	float c = ( 0.2 * T ) * 10E-18;',
+		'	return 0.434 * c * MieConst;',
+		'}',
+
+		'void main() {',
+
+		'	vec4 worldPosition = modelMatrix * vec4( position, 1.0 );',
+		'	vWorldPosition = worldPosition.xyz;',
+
+		'	gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );',
+		'	gl_Position.z = gl_Position.w;', // set z to camera.far
+
+		'	vSunDirection = normalize( sunPosition );',
+
+		'	vSunE = sunIntensity( dot( vSunDirection, up ) );',
+
+		'	vSunfade = 1.0 - clamp( 1.0 - exp( ( sunPosition.y / 450000.0 ) ), 0.0, 1.0 );',
+
+		'	float rayleighCoefficient = rayleigh - ( 1.0 * ( 1.0 - vSunfade ) );',
+
+		// extinction (absorbtion + out scattering)
+		// rayleigh coefficients
+		'	vBetaR = totalRayleigh * rayleighCoefficient;',
+
+		// mie coefficients
+		'	vBetaM = totalMie( turbidity ) * mieCoefficient;',
+
+		'}'
+	].join( '\n' ),
+
+	fragmentShader: [
+		'varying vec3 vWorldPosition;',
+		'varying vec3 vSunDirection;',
+		'varying float vSunfade;',
+		'varying vec3 vBetaR;',
+		'varying vec3 vBetaM;',
+		'varying float vSunE;',
+
+		'uniform float luminance;',
+		'uniform float mieDirectionalG;',
+
+		'const vec3 cameraPos = vec3( 0.0, 0.0, 0.0 );',
+
+		// constants for atmospheric scattering
+		'const float pi = 3.141592653589793238462643383279502884197169;',
+
+		'const float n = 1.0003;', // refractive index of air
+		'const float N = 2.545E25;', // number of molecules per unit volume for air at 288.15K and 1013mb (sea level -45 celsius)
+
+		// optical length at zenith for molecules
+		'const float rayleighZenithLength = 8.4E3;',
+		'const float mieZenithLength = 1.25E3;',
+		'const vec3 up = vec3( 0.0, 1.0, 0.0 );',
+		// 66 arc seconds -> degrees, and the cosine of that
+		'const float sunAngularDiameterCos = 0.999956676946448443553574619906976478926848692873900859324;',
+
+		// 3.0 / ( 16.0 * pi )
+		'const float THREE_OVER_SIXTEENPI = 0.05968310365946075;',
+		// 1.0 / ( 4.0 * pi )
+		'const float ONE_OVER_FOURPI = 0.07957747154594767;',
+
+		'float rayleighPhase( float cosTheta ) {',
+		'	return THREE_OVER_SIXTEENPI * ( 1.0 + pow( cosTheta, 2.0 ) );',
+		'}',
+
+		'float hgPhase( float cosTheta, float g ) {',
+		'	float g2 = pow( g, 2.0 );',
+		'	float inverse = 1.0 / pow( 1.0 - 2.0 * g * cosTheta + g2, 1.5 );',
+		'	return ONE_OVER_FOURPI * ( ( 1.0 - g2 ) * inverse );',
+		'}',
+
+		// Filmic ToneMapping http://filmicgames.com/archives/75
+		'const float A = 0.15;',
+		'const float B = 0.50;',
+		'const float C = 0.10;',
+		'const float D = 0.20;',
+		'const float E = 0.02;',
+		'const float F = 0.30;',
+
+		'const float whiteScale = 1.0748724675633854;', // 1.0 / Uncharted2Tonemap(1000.0)
+
+		'vec3 Uncharted2Tonemap( vec3 x ) {',
+		'	return ( ( x * ( A * x + C * B ) + D * E ) / ( x * ( A * x + B ) + D * F ) ) - E / F;',
+		'}',
+
+
+		'void main() {',
+		// optical length
+		// cutoff angle at 90 to avoid singularity in next formula.
+		'	float zenithAngle = acos( max( 0.0, dot( up, normalize( vWorldPosition - cameraPos ) ) ) );',
+		'	float inverse = 1.0 / ( cos( zenithAngle ) + 0.15 * pow( 93.885 - ( ( zenithAngle * 180.0 ) / pi ), -1.253 ) );',
+		'	float sR = rayleighZenithLength * inverse;',
+		'	float sM = mieZenithLength * inverse;',
+
+		// combined extinction factor
+		'	vec3 Fex = exp( -( vBetaR * sR + vBetaM * sM ) );',
+
+		// in scattering
+		'	float cosTheta = dot( normalize( vWorldPosition - cameraPos ), vSunDirection );',
+
+		'	float rPhase = rayleighPhase( cosTheta * 0.5 + 0.5 );',
+		'	vec3 betaRTheta = vBetaR * rPhase;',
+
+		'	float mPhase = hgPhase( cosTheta, mieDirectionalG );',
+		'	vec3 betaMTheta = vBetaM * mPhase;',
+
+		'	vec3 Lin = pow( vSunE * ( ( betaRTheta + betaMTheta ) / ( vBetaR + vBetaM ) ) * ( 1.0 - Fex ), vec3( 1.5 ) );',
+		'	Lin *= mix( vec3( 1.0 ), pow( vSunE * ( ( betaRTheta + betaMTheta ) / ( vBetaR + vBetaM ) ) * Fex, vec3( 1.0 / 2.0 ) ), clamp( pow( 1.0 - dot( up, vSunDirection ), 5.0 ), 0.0, 1.0 ) );',
+
+		// nightsky
+		'	vec3 direction = normalize( vWorldPosition - cameraPos );',
+		'	float theta = acos( direction.y ); // elevation --> y-axis, [-pi/2, pi/2]',
+		'	float phi = atan( direction.z, direction.x ); // azimuth --> x-axis [-pi/2, pi/2]',
+		'	vec2 uv = vec2( phi, theta ) / vec2( 2.0 * pi, pi ) + vec2( 0.5, 0.0 );',
+		'	vec3 L0 = vec3( 0.1 ) * Fex;',
+
+		// composition + solar disc
+		'	float sundisk = smoothstep( sunAngularDiameterCos, sunAngularDiameterCos + 0.00002, cosTheta );',
+		'	L0 += ( vSunE * 19000.0 * Fex ) * sundisk;',
+
+		'	vec3 texColor = ( Lin + L0 ) * 0.04 + vec3( 0.0, 0.0003, 0.00075 );',
+
+		'	vec3 curr = Uncharted2Tonemap( ( log2( 2.0 / pow( luminance, 4.0 ) ) ) * texColor );',
+		'	vec3 color = curr * whiteScale;',
+
+		'	vec3 retColor = pow( color, vec3( 1.0 / ( 1.2 + ( 1.2 * vSunfade ) ) ) );',
+
+		'	gl_FragColor = vec4( retColor, 1.0 );',
+
+		'}'
+	].join( '\n' )
+
+};
+
+export { Sky };

+ 29 - 0
examples/jsm/objects/Water.d.ts

@@ -0,0 +1,29 @@
+import {
+  BufferGeometry,
+  Color,
+  Geometry,
+  Mesh,
+  Side,
+  Texture,
+  Vector3
+} from '../../../src/Three';
+
+export interface WaterOptions {
+  textureWidth?: number;
+  textureHeight?: number;
+  clipBias?: number;
+  alpha?: number;
+  time?: number;
+  waterNormals?: Texture;
+  sunDirection?: Vector3;
+  sunColor?: Color | string | number;
+  waterColor?: Color | string | number;
+  eye?: Vector3;
+  distortionScale?: number;
+  side?: Side;
+  fog?: boolean;
+}
+
+export class Water extends Mesh {
+  constructor(geometry: Geometry | BufferGeometry, options: WaterOptions);
+}

+ 331 - 0
examples/jsm/objects/Water.js

@@ -0,0 +1,331 @@
+/**
+ * @author jbouny / https://github.com/jbouny
+ *
+ * Work based on :
+ * @author Slayvin / http://slayvin.net : Flat mirror for three.js
+ * @author Stemkoski / http://www.adelphi.edu/~stemkoski : An implementation of water shader based on the flat mirror
+ * @author Jonas Wagner / http://29a.ch/ && http://29a.ch/slides/2012/webglwater/ : Water shader explanations in WebGL
+ */
+
+import {
+	Color,
+	FrontSide,
+	LinearFilter,
+	Math as _Math,
+	Matrix4,
+	Mesh,
+	PerspectiveCamera,
+	Plane,
+	RGBFormat,
+	ShaderChunk,
+	ShaderMaterial,
+	UniformsLib,
+	UniformsUtils,
+	Vector3,
+	Vector4,
+	WebGLRenderTarget
+} from "../../../build/three.module.js";
+
+var Water = function ( geometry, options ) {
+
+	Mesh.call( this, geometry );
+
+	var scope = this;
+
+	options = options || {};
+
+	var textureWidth = options.textureWidth !== undefined ? options.textureWidth : 512;
+	var textureHeight = options.textureHeight !== undefined ? options.textureHeight : 512;
+
+	var clipBias = options.clipBias !== undefined ? options.clipBias : 0.0;
+	var alpha = options.alpha !== undefined ? options.alpha : 1.0;
+	var time = options.time !== undefined ? options.time : 0.0;
+	var normalSampler = options.waterNormals !== undefined ? options.waterNormals : null;
+	var sunDirection = options.sunDirection !== undefined ? options.sunDirection : new Vector3( 0.70707, 0.70707, 0.0 );
+	var sunColor = new Color( options.sunColor !== undefined ? options.sunColor : 0xffffff );
+	var waterColor = new Color( options.waterColor !== undefined ? options.waterColor : 0x7F7F7F );
+	var eye = options.eye !== undefined ? options.eye : new Vector3( 0, 0, 0 );
+	var distortionScale = options.distortionScale !== undefined ? options.distortionScale : 20.0;
+	var side = options.side !== undefined ? options.side : FrontSide;
+	var fog = options.fog !== undefined ? options.fog : false;
+
+	//
+
+	var mirrorPlane = new Plane();
+	var normal = new Vector3();
+	var mirrorWorldPosition = new Vector3();
+	var cameraWorldPosition = new Vector3();
+	var rotationMatrix = new Matrix4();
+	var lookAtPosition = new Vector3( 0, 0, - 1 );
+	var clipPlane = new Vector4();
+
+	var view = new Vector3();
+	var target = new Vector3();
+	var q = new Vector4();
+
+	var textureMatrix = new Matrix4();
+
+	var mirrorCamera = new PerspectiveCamera();
+
+	var parameters = {
+		minFilter: LinearFilter,
+		magFilter: LinearFilter,
+		format: RGBFormat,
+		stencilBuffer: false
+	};
+
+	var renderTarget = new WebGLRenderTarget( textureWidth, textureHeight, parameters );
+
+	if ( ! _Math.isPowerOfTwo( textureWidth ) || ! _Math.isPowerOfTwo( textureHeight ) ) {
+
+		renderTarget.texture.generateMipmaps = false;
+
+	}
+
+	var mirrorShader = {
+
+		uniforms: UniformsUtils.merge( [
+			UniformsLib[ 'fog' ],
+			UniformsLib[ 'lights' ],
+			{
+				"normalSampler": { value: null },
+				"mirrorSampler": { value: null },
+				"alpha": { value: 1.0 },
+				"time": { value: 0.0 },
+				"size": { value: 1.0 },
+				"distortionScale": { value: 20.0 },
+				"textureMatrix": { value: new Matrix4() },
+				"sunColor": { value: new Color( 0x7F7F7F ) },
+				"sunDirection": { value: new Vector3( 0.70707, 0.70707, 0 ) },
+				"eye": { value: new Vector3() },
+				"waterColor": { value: new Color( 0x555555 ) }
+			}
+		] ),
+
+		vertexShader: [
+			'uniform mat4 textureMatrix;',
+			'uniform float time;',
+
+			'varying vec4 mirrorCoord;',
+			'varying vec4 worldPosition;',
+
+			ShaderChunk[ 'fog_pars_vertex' ],
+			ShaderChunk[ 'shadowmap_pars_vertex' ],
+
+			'void main() {',
+			'	mirrorCoord = modelMatrix * vec4( position, 1.0 );',
+			'	worldPosition = mirrorCoord.xyzw;',
+			'	mirrorCoord = textureMatrix * mirrorCoord;',
+			'	vec4 mvPosition =  modelViewMatrix * vec4( position, 1.0 );',
+			'	gl_Position = projectionMatrix * mvPosition;',
+
+			ShaderChunk[ 'fog_vertex' ],
+			ShaderChunk[ 'shadowmap_vertex' ],
+
+			'}'
+		].join( '\n' ),
+
+		fragmentShader: [
+			'uniform sampler2D mirrorSampler;',
+			'uniform float alpha;',
+			'uniform float time;',
+			'uniform float size;',
+			'uniform float distortionScale;',
+			'uniform sampler2D normalSampler;',
+			'uniform vec3 sunColor;',
+			'uniform vec3 sunDirection;',
+			'uniform vec3 eye;',
+			'uniform vec3 waterColor;',
+
+			'varying vec4 mirrorCoord;',
+			'varying vec4 worldPosition;',
+
+			'vec4 getNoise( vec2 uv ) {',
+			'	vec2 uv0 = ( uv / 103.0 ) + vec2(time / 17.0, time / 29.0);',
+			'	vec2 uv1 = uv / 107.0-vec2( time / -19.0, time / 31.0 );',
+			'	vec2 uv2 = uv / vec2( 8907.0, 9803.0 ) + vec2( time / 101.0, time / 97.0 );',
+			'	vec2 uv3 = uv / vec2( 1091.0, 1027.0 ) - vec2( time / 109.0, time / -113.0 );',
+			'	vec4 noise = texture2D( normalSampler, uv0 ) +',
+			'		texture2D( normalSampler, uv1 ) +',
+			'		texture2D( normalSampler, uv2 ) +',
+			'		texture2D( normalSampler, uv3 );',
+			'	return noise * 0.5 - 1.0;',
+			'}',
+
+			'void sunLight( const vec3 surfaceNormal, const vec3 eyeDirection, float shiny, float spec, float diffuse, inout vec3 diffuseColor, inout vec3 specularColor ) {',
+			'	vec3 reflection = normalize( reflect( -sunDirection, surfaceNormal ) );',
+			'	float direction = max( 0.0, dot( eyeDirection, reflection ) );',
+			'	specularColor += pow( direction, shiny ) * sunColor * spec;',
+			'	diffuseColor += max( dot( sunDirection, surfaceNormal ), 0.0 ) * sunColor * diffuse;',
+			'}',
+
+			ShaderChunk[ 'common' ],
+			ShaderChunk[ 'packing' ],
+			ShaderChunk[ 'bsdfs' ],
+			ShaderChunk[ 'fog_pars_fragment' ],
+			ShaderChunk[ 'lights_pars_begin' ],
+			ShaderChunk[ 'shadowmap_pars_fragment' ],
+			ShaderChunk[ 'shadowmask_pars_fragment' ],
+
+			'void main() {',
+			'	vec4 noise = getNoise( worldPosition.xz * size );',
+			'	vec3 surfaceNormal = normalize( noise.xzy * vec3( 1.5, 1.0, 1.5 ) );',
+
+			'	vec3 diffuseLight = vec3(0.0);',
+			'	vec3 specularLight = vec3(0.0);',
+
+			'	vec3 worldToEye = eye-worldPosition.xyz;',
+			'	vec3 eyeDirection = normalize( worldToEye );',
+			'	sunLight( surfaceNormal, eyeDirection, 100.0, 2.0, 0.5, diffuseLight, specularLight );',
+
+			'	float distance = length(worldToEye);',
+
+			'	vec2 distortion = surfaceNormal.xz * ( 0.001 + 1.0 / distance ) * distortionScale;',
+			'	vec3 reflectionSample = vec3( texture2D( mirrorSampler, mirrorCoord.xy / mirrorCoord.w + distortion ) );',
+
+			'	float theta = max( dot( eyeDirection, surfaceNormal ), 0.0 );',
+			'	float rf0 = 0.3;',
+			'	float reflectance = rf0 + ( 1.0 - rf0 ) * pow( ( 1.0 - theta ), 5.0 );',
+			'	vec3 scatter = max( 0.0, dot( surfaceNormal, eyeDirection ) ) * waterColor;',
+			'	vec3 albedo = mix( ( sunColor * diffuseLight * 0.3 + scatter ) * getShadowMask(), ( vec3( 0.1 ) + reflectionSample * 0.9 + reflectionSample * specularLight ), reflectance);',
+			'	vec3 outgoingLight = albedo;',
+			'	gl_FragColor = vec4( outgoingLight, alpha );',
+
+			ShaderChunk[ 'tonemapping_fragment' ],
+			ShaderChunk[ 'fog_fragment' ],
+
+			'}'
+		].join( '\n' )
+
+	};
+
+	var material = new ShaderMaterial( {
+		fragmentShader: mirrorShader.fragmentShader,
+		vertexShader: mirrorShader.vertexShader,
+		uniforms: UniformsUtils.clone( mirrorShader.uniforms ),
+		transparent: true,
+		lights: true,
+		side: side,
+		fog: fog
+	} );
+
+	material.uniforms[ "mirrorSampler" ].value = renderTarget.texture;
+	material.uniforms[ "textureMatrix" ].value = textureMatrix;
+	material.uniforms[ "alpha" ].value = alpha;
+	material.uniforms[ "time" ].value = time;
+	material.uniforms[ "normalSampler" ].value = normalSampler;
+	material.uniforms[ "sunColor" ].value = sunColor;
+	material.uniforms[ "waterColor" ].value = waterColor;
+	material.uniforms[ "sunDirection" ].value = sunDirection;
+	material.uniforms[ "distortionScale" ].value = distortionScale;
+
+	material.uniforms[ "eye" ].value = eye;
+
+	scope.material = material;
+
+	scope.onBeforeRender = function ( renderer, scene, camera ) {
+
+		mirrorWorldPosition.setFromMatrixPosition( scope.matrixWorld );
+		cameraWorldPosition.setFromMatrixPosition( camera.matrixWorld );
+
+		rotationMatrix.extractRotation( scope.matrixWorld );
+
+		normal.set( 0, 0, 1 );
+		normal.applyMatrix4( rotationMatrix );
+
+		view.subVectors( mirrorWorldPosition, cameraWorldPosition );
+
+		// Avoid rendering when mirror is facing away
+
+		if ( view.dot( normal ) > 0 ) return;
+
+		view.reflect( normal ).negate();
+		view.add( mirrorWorldPosition );
+
+		rotationMatrix.extractRotation( camera.matrixWorld );
+
+		lookAtPosition.set( 0, 0, - 1 );
+		lookAtPosition.applyMatrix4( rotationMatrix );
+		lookAtPosition.add( cameraWorldPosition );
+
+		target.subVectors( mirrorWorldPosition, lookAtPosition );
+		target.reflect( normal ).negate();
+		target.add( mirrorWorldPosition );
+
+		mirrorCamera.position.copy( view );
+		mirrorCamera.up.set( 0, 1, 0 );
+		mirrorCamera.up.applyMatrix4( rotationMatrix );
+		mirrorCamera.up.reflect( normal );
+		mirrorCamera.lookAt( target );
+
+		mirrorCamera.far = camera.far; // Used in WebGLBackground
+
+		mirrorCamera.updateMatrixWorld();
+		mirrorCamera.projectionMatrix.copy( camera.projectionMatrix );
+
+		// Update the texture matrix
+		textureMatrix.set(
+			0.5, 0.0, 0.0, 0.5,
+			0.0, 0.5, 0.0, 0.5,
+			0.0, 0.0, 0.5, 0.5,
+			0.0, 0.0, 0.0, 1.0
+		);
+		textureMatrix.multiply( mirrorCamera.projectionMatrix );
+		textureMatrix.multiply( mirrorCamera.matrixWorldInverse );
+
+		// Now update projection matrix with new clip plane, implementing code from: http://www.terathon.com/code/oblique.html
+		// Paper explaining this technique: http://www.terathon.com/lengyel/Lengyel-Oblique.pdf
+		mirrorPlane.setFromNormalAndCoplanarPoint( normal, mirrorWorldPosition );
+		mirrorPlane.applyMatrix4( mirrorCamera.matrixWorldInverse );
+
+		clipPlane.set( mirrorPlane.normal.x, mirrorPlane.normal.y, mirrorPlane.normal.z, mirrorPlane.constant );
+
+		var projectionMatrix = mirrorCamera.projectionMatrix;
+
+		q.x = ( Math.sign( clipPlane.x ) + projectionMatrix.elements[ 8 ] ) / projectionMatrix.elements[ 0 ];
+		q.y = ( Math.sign( clipPlane.y ) + projectionMatrix.elements[ 9 ] ) / projectionMatrix.elements[ 5 ];
+		q.z = - 1.0;
+		q.w = ( 1.0 + projectionMatrix.elements[ 10 ] ) / projectionMatrix.elements[ 14 ];
+
+		// Calculate the scaled plane vector
+		clipPlane.multiplyScalar( 2.0 / clipPlane.dot( q ) );
+
+		// Replacing the third row of the projection matrix
+		projectionMatrix.elements[ 2 ] = clipPlane.x;
+		projectionMatrix.elements[ 6 ] = clipPlane.y;
+		projectionMatrix.elements[ 10 ] = clipPlane.z + 1.0 - clipBias;
+		projectionMatrix.elements[ 14 ] = clipPlane.w;
+
+		eye.setFromMatrixPosition( camera.matrixWorld );
+
+		//
+
+		var currentRenderTarget = renderer.getRenderTarget();
+
+		var currentVrEnabled = renderer.vr.enabled;
+		var currentShadowAutoUpdate = renderer.shadowMap.autoUpdate;
+
+		scope.visible = false;
+
+		renderer.vr.enabled = false; // Avoid camera modification and recursion
+		renderer.shadowMap.autoUpdate = false; // Avoid re-computing shadows
+
+		renderer.setRenderTarget( renderTarget );
+		renderer.clear();
+		renderer.render( scene, mirrorCamera );
+
+		scope.visible = true;
+
+		renderer.vr.enabled = currentVrEnabled;
+		renderer.shadowMap.autoUpdate = currentShadowAutoUpdate;
+
+		renderer.setRenderTarget( currentRenderTarget );
+
+	};
+
+};
+
+Water.prototype = Object.create( Mesh.prototype );
+Water.prototype.constructor = Water;
+
+export { Water };

+ 27 - 0
examples/jsm/objects/Water2.d.ts

@@ -0,0 +1,27 @@
+import {
+  BufferGeometry,
+  Color,
+  Geometry,
+  Mesh,
+  Texture,
+  Vector2
+} from '../../../src/Three';
+
+export interface WaterOptions {
+  color?: Color | string | number;
+  textureWidth?: number;
+  textureHeight?: number;
+  clipBias?: number;
+  flowDirection?: Vector2;
+  flowSpeed?: number;
+  reflectivity?: number;
+  scale?: number;
+  shader?: object;
+  flowMap?: Texture;
+  normalMap0?: Texture;
+  normalMap1?: Texture;
+}
+
+export class Water extends Mesh {
+  constructor(geometry: Geometry | BufferGeometry, options: WaterOptions);
+}

+ 356 - 0
examples/jsm/objects/Water2.js

@@ -0,0 +1,356 @@
+/**
+ * @author Mugen87 / https://github.com/Mugen87
+ *
+ * References:
+ *	http://www.valvesoftware.com/publications/2010/siggraph2010_vlachos_waterflow.pdf
+ * 	http://graphicsrunner.blogspot.de/2010/08/water-using-flow-maps.html
+ *
+ */
+
+import {
+	Clock,
+	Color,
+	Matrix4,
+	Mesh,
+	RepeatWrapping,
+	ShaderMaterial,
+	TextureLoader,
+	UniformsLib,
+	UniformsUtils,
+	Vector2,
+	Vector4
+} from "../../../build/three.module.js";
+import { Reflector } from "../objects/Reflector.js";
+import { Refractor } from "../objects/Refractor.js";
+
+var Water = function ( geometry, options ) {
+
+	Mesh.call( this, geometry );
+
+	this.type = 'Water';
+
+	var scope = this;
+
+	options = options || {};
+
+	var color = ( options.color !== undefined ) ? new Color( options.color ) : new Color( 0xFFFFFF );
+	var textureWidth = options.textureWidth || 512;
+	var textureHeight = options.textureHeight || 512;
+	var clipBias = options.clipBias || 0;
+	var flowDirection = options.flowDirection || new Vector2( 1, 0 );
+	var flowSpeed = options.flowSpeed || 0.03;
+	var reflectivity = options.reflectivity || 0.02;
+	var scale = options.scale || 1;
+	var shader = options.shader || Water.WaterShader;
+
+	var textureLoader = new TextureLoader();
+
+	var flowMap = options.flowMap || undefined;
+	var normalMap0 = options.normalMap0 || textureLoader.load( 'textures/water/Water_1_M_Normal.jpg' );
+	var normalMap1 = options.normalMap1 || textureLoader.load( 'textures/water/Water_2_M_Normal.jpg' );
+
+	var cycle = 0.15; // a cycle of a flow map phase
+	var halfCycle = cycle * 0.5;
+	var textureMatrix = new Matrix4();
+	var clock = new Clock();
+
+	// internal components
+
+	if ( Reflector === undefined ) {
+
+		console.error( 'THREE.Water: Required component Reflector not found.' );
+		return;
+
+	}
+
+	if ( Refractor === undefined ) {
+
+		console.error( 'THREE.Water: Required component Refractor not found.' );
+		return;
+
+	}
+
+	var reflector = new Reflector( geometry, {
+		textureWidth: textureWidth,
+		textureHeight: textureHeight,
+		clipBias: clipBias
+	} );
+
+	var refractor = new Refractor( geometry, {
+		textureWidth: textureWidth,
+		textureHeight: textureHeight,
+		clipBias: clipBias
+	} );
+
+	reflector.matrixAutoUpdate = false;
+	refractor.matrixAutoUpdate = false;
+
+	// material
+
+	this.material = new ShaderMaterial( {
+		uniforms: UniformsUtils.merge( [
+			UniformsLib[ 'fog' ],
+			shader.uniforms
+		] ),
+		vertexShader: shader.vertexShader,
+		fragmentShader: shader.fragmentShader,
+		transparent: true,
+		fog: true
+	} );
+
+	if ( flowMap !== undefined ) {
+
+		this.material.defines.USE_FLOWMAP = '';
+		this.material.uniforms[ "tFlowMap" ] = {
+			type: 't',
+			value: flowMap
+		};
+
+	} else {
+
+		this.material.uniforms[ "flowDirection" ] = {
+			type: 'v2',
+			value: flowDirection
+		};
+
+	}
+
+	// maps
+
+	normalMap0.wrapS = normalMap0.wrapT = RepeatWrapping;
+	normalMap1.wrapS = normalMap1.wrapT = RepeatWrapping;
+
+	this.material.uniforms[ "tReflectionMap" ].value = reflector.getRenderTarget().texture;
+	this.material.uniforms[ "tRefractionMap" ].value = refractor.getRenderTarget().texture;
+	this.material.uniforms[ "tNormalMap0" ].value = normalMap0;
+	this.material.uniforms[ "tNormalMap1" ].value = normalMap1;
+
+	// water
+
+	this.material.uniforms[ "color" ].value = color;
+	this.material.uniforms[ "reflectivity" ].value = reflectivity;
+	this.material.uniforms[ "textureMatrix" ].value = textureMatrix;
+
+	// inital values
+
+	this.material.uniforms[ "config" ].value.x = 0; // flowMapOffset0
+	this.material.uniforms[ "config" ].value.y = halfCycle; // flowMapOffset1
+	this.material.uniforms[ "config" ].value.z = halfCycle; // halfCycle
+	this.material.uniforms[ "config" ].value.w = scale; // scale
+
+	// functions
+
+	function updateTextureMatrix( camera ) {
+
+		textureMatrix.set(
+			0.5, 0.0, 0.0, 0.5,
+			0.0, 0.5, 0.0, 0.5,
+			0.0, 0.0, 0.5, 0.5,
+			0.0, 0.0, 0.0, 1.0
+		);
+
+		textureMatrix.multiply( camera.projectionMatrix );
+		textureMatrix.multiply( camera.matrixWorldInverse );
+		textureMatrix.multiply( scope.matrixWorld );
+
+	}
+
+	function updateFlow() {
+
+		var delta = clock.getDelta();
+		var config = scope.material.uniforms[ "config" ];
+
+		config.value.x += flowSpeed * delta; // flowMapOffset0
+		config.value.y = config.value.x + halfCycle; // flowMapOffset1
+
+		// Important: The distance between offsets should be always the value of "halfCycle".
+		// Moreover, both offsets should be in the range of [ 0, cycle ].
+		// This approach ensures a smooth water flow and avoids "reset" effects.
+
+		if ( config.value.x >= cycle ) {
+
+			config.value.x = 0;
+			config.value.y = halfCycle;
+
+		} else if ( config.value.y >= cycle ) {
+
+			config.value.y = config.value.y - cycle;
+
+		}
+
+	}
+
+	//
+
+	this.onBeforeRender = function ( renderer, scene, camera ) {
+
+		updateTextureMatrix( camera );
+		updateFlow();
+
+		scope.visible = false;
+
+		reflector.matrixWorld.copy( scope.matrixWorld );
+		refractor.matrixWorld.copy( scope.matrixWorld );
+
+		reflector.onBeforeRender( renderer, scene, camera );
+		refractor.onBeforeRender( renderer, scene, camera );
+
+		scope.visible = true;
+
+	};
+
+};
+
+Water.prototype = Object.create( Mesh.prototype );
+Water.prototype.constructor = Water;
+
+Water.WaterShader = {
+
+	uniforms: {
+
+		'color': {
+			type: 'c',
+			value: null
+		},
+
+		'reflectivity': {
+			type: 'f',
+			value: 0
+		},
+
+		'tReflectionMap': {
+			type: 't',
+			value: null
+		},
+
+		'tRefractionMap': {
+			type: 't',
+			value: null
+		},
+
+		'tNormalMap0': {
+			type: 't',
+			value: null
+		},
+
+		'tNormalMap1': {
+			type: 't',
+			value: null
+		},
+
+		'textureMatrix': {
+			type: 'm4',
+			value: null
+		},
+
+		'config': {
+			type: 'v4',
+			value: new Vector4()
+		}
+
+	},
+
+	vertexShader: [
+
+		'#include <fog_pars_vertex>',
+
+		'uniform mat4 textureMatrix;',
+
+		'varying vec4 vCoord;',
+		'varying vec2 vUv;',
+		'varying vec3 vToEye;',
+
+		'void main() {',
+
+		'	vUv = uv;',
+		'	vCoord = textureMatrix * vec4( position, 1.0 );',
+
+		'	vec4 worldPosition = modelMatrix * vec4( position, 1.0 );',
+		'	vToEye = cameraPosition - worldPosition.xyz;',
+
+		'	vec4 mvPosition =  viewMatrix * worldPosition;', // used in fog_vertex
+		'	gl_Position = projectionMatrix * mvPosition;',
+
+		'	#include <fog_vertex>',
+
+		'}'
+
+	].join( '\n' ),
+
+	fragmentShader: [
+
+		'#include <common>',
+		'#include <fog_pars_fragment>',
+
+		'uniform sampler2D tReflectionMap;',
+		'uniform sampler2D tRefractionMap;',
+		'uniform sampler2D tNormalMap0;',
+		'uniform sampler2D tNormalMap1;',
+
+		'#ifdef USE_FLOWMAP',
+		'	uniform sampler2D tFlowMap;',
+		'#else',
+		'	uniform vec2 flowDirection;',
+		'#endif',
+
+		'uniform vec3 color;',
+		'uniform float reflectivity;',
+		'uniform vec4 config;',
+
+		'varying vec4 vCoord;',
+		'varying vec2 vUv;',
+		'varying vec3 vToEye;',
+
+		'void main() {',
+
+		'	float flowMapOffset0 = config.x;',
+		'	float flowMapOffset1 = config.y;',
+		'	float halfCycle = config.z;',
+		'	float scale = config.w;',
+
+		'	vec3 toEye = normalize( vToEye );',
+
+		// determine flow direction
+		'	vec2 flow;',
+		'	#ifdef USE_FLOWMAP',
+		'		flow = texture2D( tFlowMap, vUv ).rg * 2.0 - 1.0;',
+		'	#else',
+		'		flow = flowDirection;',
+		'	#endif',
+		'	flow.x *= - 1.0;',
+
+		// sample normal maps (distort uvs with flowdata)
+		'	vec4 normalColor0 = texture2D( tNormalMap0, ( vUv * scale ) + flow * flowMapOffset0 );',
+		'	vec4 normalColor1 = texture2D( tNormalMap1, ( vUv * scale ) + flow * flowMapOffset1 );',
+
+		// linear interpolate to get the final normal color
+		'	float flowLerp = abs( halfCycle - flowMapOffset0 ) / halfCycle;',
+		'	vec4 normalColor = mix( normalColor0, normalColor1, flowLerp );',
+
+		// calculate normal vector
+		'	vec3 normal = normalize( vec3( normalColor.r * 2.0 - 1.0, normalColor.b,  normalColor.g * 2.0 - 1.0 ) );',
+
+		// calculate the fresnel term to blend reflection and refraction maps
+		'	float theta = max( dot( toEye, normal ), 0.0 );',
+		'	float reflectance = reflectivity + ( 1.0 - reflectivity ) * pow( ( 1.0 - theta ), 5.0 );',
+
+		// calculate final uv coords
+		'	vec3 coord = vCoord.xyz / vCoord.w;',
+		'	vec2 uv = coord.xy + coord.z * normal.xz * 0.05;',
+
+		'	vec4 reflectColor = texture2D( tReflectionMap, vec2( 1.0 - uv.x, uv.y ) );',
+		'	vec4 refractColor = texture2D( tRefractionMap, uv );',
+
+		// multiply water color with the mix of both textures
+		'	gl_FragColor = vec4( color, 1.0 ) * mix( refractColor, reflectColor, reflectance );',
+
+		'	#include <tonemapping_fragment>',
+		'	#include <encodings_fragment>',
+		'	#include <fog_fragment>',
+
+		'}'
+
+	].join( '\n' )
+};
+
+export { Water };

+ 1 - 1
examples/webgl_shaders_sky.html

@@ -80,7 +80,7 @@
 
 
 				/// GUI
 				/// GUI
 
 
-				var effectController  = {
+				var effectController = {
 					turbidity: 10,
 					turbidity: 10,
 					rayleigh: 2,
 					rayleigh: 2,
 					mieCoefficient: 0.005,
 					mieCoefficient: 0.005,

+ 5 - 0
utils/modularize.js

@@ -66,6 +66,11 @@ var files = [
 	{ path: 'objects/Lensflare.js', dependencies: [], ignoreList: [] },
 	{ path: 'objects/Lensflare.js', dependencies: [], ignoreList: [] },
 	{ path: 'objects/Reflector.js', dependencies: [], ignoreList: [] },
 	{ path: 'objects/Reflector.js', dependencies: [], ignoreList: [] },
 	{ path: 'objects/Refractor.js', dependencies: [], ignoreList: [] },
 	{ path: 'objects/Refractor.js', dependencies: [], ignoreList: [] },
+	{ path: 'objects/ReflectorRTT.js', dependencies: [ { name: 'Reflector', path: 'objects/Reflector.js' } ], ignoreList: [] },
+	{ path: 'objects/ShadowMesh.js', dependencies: [], ignoreList: [] },
+	{ path: 'objects/Sky.js', dependencies: [], ignoreList: [] },
+	{ path: 'objects/Water.js', dependencies: [], ignoreList: [] },
+	{ path: 'objects/Water2.js', dependencies: [ { name: 'Reflector', path: 'objects/Reflector.js' }, { name: 'Refractor', path: 'objects/Refractor.js' } ], ignoreList: [] },
 
 
 	{ path: 'pmrem/PMREMCubeUVPacker.js', dependencies: [], ignoreList: [] },
 	{ path: 'pmrem/PMREMCubeUVPacker.js', dependencies: [], ignoreList: [] },
 	{ path: 'pmrem/PMREMGenerator.js', dependencies: [], ignoreList: [] },
 	{ path: 'pmrem/PMREMGenerator.js', dependencies: [], ignoreList: [] },