添加未同步到threejs的文件.

pull/7/head
ChenX 7 years ago
parent 3b9271fc60
commit 483823e733

@ -0,0 +1,262 @@
/**
* @author Slayvin / http://slayvin.net
*/
THREE.Reflector = function ( width, height, options ) {
THREE.Mesh.call( this, new THREE.PlaneBufferGeometry( width, height ) );
this.type = 'Reflector';
var scope = this;
options = options || {};
var color = ( options.color !== undefined ) ? new THREE.Color( options.color ) : new THREE.Color( 0x7F7F7F );
var textureWidth = options.textureWidth || 512;
var textureHeight = options.textureHeight || 512;
var clipBias = options.clipBias || 0;
var shader = options.shader || THREE.Reflector.ReflectorShader;
var recursion = options.recursion !== undefined ? options.recursion : 0;
//
var reflectorPlane = new THREE.Plane();
var normal = new THREE.Vector3();
var reflectorWorldPosition = new THREE.Vector3();
var cameraWorldPosition = new THREE.Vector3();
var rotationMatrix = new THREE.Matrix4();
var lookAtPosition = new THREE.Vector3( 0, 0, - 1 );
var clipPlane = new THREE.Vector4();
var viewport = new THREE.Vector4();
var view = new THREE.Vector3();
var target = new THREE.Vector3();
var q = new THREE.Vector4();
var textureMatrix = new THREE.Matrix4();
var virtualCamera = new THREE.PerspectiveCamera();
var parameters = {
minFilter: THREE.LinearFilter,
magFilter: THREE.LinearFilter,
format: THREE.RGBFormat,
stencilBuffer: false
};
var renderTarget = new THREE.WebGLRenderTarget( textureWidth, textureHeight, parameters );
if ( ! THREE.Math.isPowerOfTwo( textureWidth ) || ! THREE.Math.isPowerOfTwo( textureHeight ) ) {
renderTarget.texture.generateMipmaps = false;
}
var material = new THREE.ShaderMaterial( {
uniforms: THREE.UniformsUtils.clone( shader.uniforms ),
fragmentShader: shader.fragmentShader,
vertexShader: shader.vertexShader,
} );
material.uniforms.tDiffuse.value = renderTarget.texture;
material.uniforms.color.value = color;
material.uniforms.textureMatrix.value = textureMatrix;
this.material = material;
this.onBeforeRender = function ( renderer, scene, camera ) {
if ( 'recursion' in camera.userData ) {
if ( camera.userData.recursion === recursion ) return;
camera.userData.recursion ++;
}
reflectorWorldPosition.setFromMatrixPosition( scope.matrixWorld );
cameraWorldPosition.setFromMatrixPosition( camera.matrixWorld );
rotationMatrix.extractRotation( scope.matrixWorld );
normal.set( 0, 0, 1 );
normal.applyMatrix4( rotationMatrix );
view.subVectors( reflectorWorldPosition, cameraWorldPosition );
// Avoid rendering when reflector is facing away
if ( view.dot( normal ) > 0 ) return;
view.reflect( normal ).negate();
view.add( reflectorWorldPosition );
rotationMatrix.extractRotation( camera.matrixWorld );
lookAtPosition.set( 0, 0, - 1 );
lookAtPosition.applyMatrix4( rotationMatrix );
lookAtPosition.add( cameraWorldPosition );
target.subVectors( reflectorWorldPosition, lookAtPosition );
target.reflect( normal ).negate();
target.add( reflectorWorldPosition );
virtualCamera.position.copy( view );
virtualCamera.up.set( 0, 1, 0 );
virtualCamera.up.applyMatrix4( rotationMatrix );
virtualCamera.up.reflect( normal );
virtualCamera.lookAt( target );
virtualCamera.far = camera.far; // Used in WebGLBackground
virtualCamera.updateMatrixWorld();
virtualCamera.projectionMatrix.copy( camera.projectionMatrix );
virtualCamera.userData.recursion = 0;
// 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( virtualCamera.projectionMatrix );
textureMatrix.multiply( virtualCamera.matrixWorldInverse );
textureMatrix.multiply( scope.matrixWorld );
// 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
reflectorPlane.setFromNormalAndCoplanarPoint( normal, reflectorWorldPosition );
reflectorPlane.applyMatrix4( virtualCamera.matrixWorldInverse );
clipPlane.set( reflectorPlane.normal.x, reflectorPlane.normal.y, reflectorPlane.normal.z, reflectorPlane.constant );
var projectionMatrix = virtualCamera.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;
// Render
scope.visible = false;
var currentRenderTarget = renderer.getRenderTarget();
var currentVrEnabled = renderer.vr.enabled;
var currentShadowAutoUpdate = renderer.shadowMap.autoUpdate;
renderer.vr.enabled = false; // Avoid camera modification and recursion
renderer.shadowMap.autoUpdate = false; // Avoid re-computing shadows
renderer.render( scene, virtualCamera, renderTarget, true );
renderer.vr.enabled = currentVrEnabled;
renderer.shadowMap.autoUpdate = currentShadowAutoUpdate;
renderer.setRenderTarget( currentRenderTarget );
// Restore viewport
var bounds = camera.bounds;
if ( bounds !== undefined ) {
var size = renderer.getSize();
var pixelRatio = renderer.getPixelRatio();
viewport.x = bounds.x * size.width * pixelRatio;
viewport.y = bounds.y * size.height * pixelRatio;
viewport.z = bounds.z * size.width * pixelRatio;
viewport.w = bounds.w * size.height * pixelRatio;
renderer.state.viewport( viewport );
}
scope.visible = true;
};
this.getRenderTarget = function () {
return renderTarget;
};
};
THREE.Reflector.prototype = Object.create( THREE.Mesh.prototype );
THREE.Reflector.prototype.constructor = THREE.Reflector;
THREE.Reflector.ReflectorShader = {
uniforms: {
'color': {
type: 'c',
value: null
},
'tDiffuse': {
type: 't',
value: null
},
'textureMatrix': {
type: 'm4',
value: null
}
},
vertexShader: [
'uniform mat4 textureMatrix;',
'varying vec4 vUv;',
'void main() {',
' vUv = textureMatrix * vec4( position, 1.0 );',
' gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );',
'}'
].join( '\n' ),
fragmentShader: [
'uniform vec3 color;',
'uniform sampler2D tDiffuse;',
'varying vec4 vUv;',
'float blendOverlay( float base, float blend ) {',
' return( base < 0.5 ? ( 2.0 * base * blend ) : ( 1.0 - 2.0 * ( 1.0 - base ) * ( 1.0 - blend ) ) );',
'}',
'vec3 blendOverlay( vec3 base, vec3 blend ) {',
' return vec3( blendOverlay( base.r, blend.r ), blendOverlay( base.g, blend.g ), blendOverlay( base.b, blend.b ) );',
'}',
'void main() {',
' vec4 base = texture2DProj( tDiffuse, vUv );',
' gl_FragColor = vec4( blendOverlay( base.rgb, color ), 1.0 );',
'}'
].join( '\n' )
};

@ -0,0 +1,9 @@
THREE.ReflectorRTT = function ( width, height, options ) {
THREE.Reflector.call( this, width, height, options );
this.geometry.setDrawRange( 0, 0 ); // avoid rendering geometry
};
THREE.ReflectorRTT.prototype = Object.create( THREE.Reflector.prototype );

@ -0,0 +1,332 @@
/**
* @author Mugen87 / https://github.com/Mugen87
*
*/
THREE.Refractor = function ( width, height, options ) {
THREE.Mesh.call( this, new THREE.PlaneBufferGeometry( width, height ) );
this.type = 'Refractor';
var scope = this;
options = options || {};
var color = ( options.color !== undefined ) ? new THREE.Color( options.color ) : new THREE.Color( 0x7F7F7F );
var textureWidth = options.textureWidth || 512;
var textureHeight = options.textureHeight || 512;
var clipBias = options.clipBias || 0;
var shader = options.shader || THREE.Refractor.RefractorShader;
//
var virtualCamera = new THREE.PerspectiveCamera();
virtualCamera.matrixAutoUpdate = false;
virtualCamera.userData.refractor = true;
//
var refractorPlane = new THREE.Plane();
var textureMatrix = new THREE.Matrix4();
// render target
var parameters = {
minFilter: THREE.LinearFilter,
magFilter: THREE.LinearFilter,
format: THREE.RGBFormat,
stencilBuffer: false
};
var renderTarget = new THREE.WebGLRenderTarget( textureWidth, textureHeight, parameters );
if ( ! THREE.Math.isPowerOfTwo( textureWidth ) || ! THREE.Math.isPowerOfTwo( textureHeight ) ) {
renderTarget.texture.generateMipmaps = false;
}
// material
this.material = new THREE.ShaderMaterial( {
uniforms: THREE.UniformsUtils.clone( shader.uniforms ),
vertexShader: shader.vertexShader,
fragmentShader: shader.fragmentShader,
transparent: true // ensures, refractors are drawn from farthest to closest
} );
this.material.uniforms.color.value = color;
this.material.uniforms.tDiffuse.value = renderTarget.texture;
this.material.uniforms.textureMatrix.value = textureMatrix;
// functions
var visible = ( function () {
var refractorWorldPosition = new THREE.Vector3();
var cameraWorldPosition = new THREE.Vector3();
var rotationMatrix = new THREE.Matrix4();
var view = new THREE.Vector3();
var normal = new THREE.Vector3();
return function visible( camera ) {
refractorWorldPosition.setFromMatrixPosition( scope.matrixWorld );
cameraWorldPosition.setFromMatrixPosition( camera.matrixWorld );
view.subVectors( refractorWorldPosition, cameraWorldPosition );
rotationMatrix.extractRotation( scope.matrixWorld );
normal.set( 0, 0, 1 );
normal.applyMatrix4( rotationMatrix );
return view.dot( normal ) < 0;
};
} )();
var updateRefractorPlane = ( function () {
var normal = new THREE.Vector3();
var position = new THREE.Vector3();
var quaternion = new THREE.Quaternion();
var scale = new THREE.Vector3();
return function updateRefractorPlane() {
scope.matrixWorld.decompose( position, quaternion, scale );
normal.set( 0, 0, 1 ).applyQuaternion( quaternion ).normalize();
// flip the normal because we want to cull everything above the plane
normal.negate();
refractorPlane.setFromNormalAndCoplanarPoint( normal, position );
};
} )();
var updateVirtualCamera = ( function () {
var clipPlane = new THREE.Plane();
var clipVector = new THREE.Vector4();
var q = new THREE.Vector4();
return function updateVirtualCamera( camera ) {
virtualCamera.matrixWorld.copy( camera.matrixWorld );
virtualCamera.matrixWorldInverse.getInverse( virtualCamera.matrixWorld );
virtualCamera.projectionMatrix.copy( camera.projectionMatrix );
virtualCamera.far = camera.far; // used in WebGLBackground
// The following code creates an oblique view frustum for clipping.
// see: Lengyel, Eric. “Oblique View Frustum Depth Projection and Clipping”.
// Journal of Game Development, Vol. 1, No. 2 (2005), Charles River Media, pp. 516
clipPlane.copy( refractorPlane );
clipPlane.applyMatrix4( virtualCamera.matrixWorldInverse );
clipVector.set( clipPlane.normal.x, clipPlane.normal.y, clipPlane.normal.z, clipPlane.constant );
// calculate the clip-space corner point opposite the clipping plane and
// transform it into camera space by multiplying it by the inverse of the projection matrix
var projectionMatrix = virtualCamera.projectionMatrix;
q.x = ( Math.sign( clipVector.x ) + projectionMatrix.elements[ 8 ] ) / projectionMatrix.elements[ 0 ];
q.y = ( Math.sign( clipVector.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
clipVector.multiplyScalar( 2.0 / clipVector.dot( q ) );
// replacing the third row of the projection matrix
projectionMatrix.elements[ 2 ] = clipVector.x;
projectionMatrix.elements[ 6 ] = clipVector.y;
projectionMatrix.elements[ 10 ] = clipVector.z + 1.0 - clipBias;
projectionMatrix.elements[ 14 ] = clipVector.w;
};
} )();
// This will update the texture matrix that is used for projective texture mapping in the shader.
// see: http://developer.download.nvidia.com/assets/gamedev/docs/projective_texture_mapping.pdf
function updateTextureMatrix( camera ) {
// this matrix does range mapping to [ 0, 1 ]
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
);
// we use "Object Linear Texgen", so we need to multiply the texture matrix T
// (matrix above) with the projection and view matrix of the virtual camera
// and the model matrix of the refractor
textureMatrix.multiply( camera.projectionMatrix );
textureMatrix.multiply( camera.matrixWorldInverse );
textureMatrix.multiply( scope.matrixWorld );
}
//
var render = ( function () {
var viewport = new THREE.Vector4();
return function render( renderer, scene, camera ) {
scope.visible = false;
var currentRenderTarget = renderer.getRenderTarget();
var currentVrEnabled = renderer.vr.enabled;
var currentShadowAutoUpdate = renderer.shadowMap.autoUpdate;
renderer.vr.enabled = false; // avoid camera modification
renderer.shadowMap.autoUpdate = false; // avoid re-computing shadows
renderer.render( scene, virtualCamera, renderTarget, true );
renderer.vr.enabled = currentVrEnabled;
renderer.shadowMap.autoUpdate = currentShadowAutoUpdate;
renderer.setRenderTarget( currentRenderTarget );
// restore viewport
var bounds = camera.bounds;
if ( bounds !== undefined ) {
var size = renderer.getSize();
var pixelRatio = renderer.getPixelRatio();
viewport.x = bounds.x * size.width * pixelRatio;
viewport.y = bounds.y * size.height * pixelRatio;
viewport.z = bounds.z * size.width * pixelRatio;
viewport.w = bounds.w * size.height * pixelRatio;
renderer.state.viewport( viewport );
}
scope.visible = true;
};
} )();
//
this.onBeforeRender = function ( renderer, scene, camera ) {
// ensure refractors are rendered only once per frame
if ( camera.userData.refractor === true ) return;
// avoid rendering when the refractor is viewed from behind
if ( ! visible( camera ) === true ) return;
// update
updateRefractorPlane();
updateTextureMatrix( camera );
updateVirtualCamera( camera );
render( renderer, scene, camera );
};
this.getRenderTarget = function () {
return renderTarget;
};
};
THREE.Refractor.prototype = Object.create( THREE.Mesh.prototype );
THREE.Refractor.prototype.constructor = THREE.Refractor;
THREE.Refractor.RefractorShader = {
uniforms: {
'color': {
type: 'c',
value: null
},
'tDiffuse': {
type: 't',
value: null
},
'textureMatrix': {
type: 'm4',
value: null
}
},
vertexShader: [
'uniform mat4 textureMatrix;',
'varying vec4 vUv;',
'void main() {',
' vUv = textureMatrix * vec4( position, 1.0 );',
' gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );',
'}'
].join( '\n' ),
fragmentShader: [
'uniform vec3 color;',
'uniform sampler2D tDiffuse;',
'varying vec4 vUv;',
'float blendOverlay( float base, float blend ) {',
' return( base < 0.5 ? ( 2.0 * base * blend ) : ( 1.0 - 2.0 * ( 1.0 - base ) * ( 1.0 - blend ) ) );',
'}',
'vec3 blendOverlay( vec3 base, vec3 blend ) {',
' return vec3( blendOverlay( base.r, blend.r ), blendOverlay( base.g, blend.g ), blendOverlay( base.b, blend.b ) );',
'}',
'void main() {',
' vec4 base = texture2DProj( tDiffuse, vUv );',
' gl_FragColor = vec4( blendOverlay( base.rgb, color ), 1.0 );',
'}'
].join( '\n' )
};

@ -0,0 +1,69 @@
/**
* @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.
*/
THREE.ShadowMesh = function ( mesh ) {
var shadowMaterial = new THREE.MeshBasicMaterial( {
color: 0x000000,
transparent: true,
opacity: 0.6,
depthWrite: false
} );
THREE.Mesh.call( this, mesh.geometry, shadowMaterial );
this.meshMatrix = mesh.matrixWorld;
this.frustumCulled = false;
this.matrixAutoUpdate = false;
};
THREE.ShadowMesh.prototype = Object.create( THREE.Mesh.prototype );
THREE.ShadowMesh.prototype.constructor = THREE.ShadowMesh;
THREE.ShadowMesh.prototype.update = function () {
var shadowMatrix = new THREE.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 );
};
}();

@ -0,0 +1,221 @@
/**
* @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
*/
THREE.Sky = function () {
var shader = THREE.Sky.SkyShader;
var material = new THREE.ShaderMaterial( {
fragmentShader: shader.fragmentShader,
vertexShader: shader.vertexShader,
uniforms: THREE.UniformsUtils.clone( shader.uniforms ),
side: THREE.BackSide
} );
THREE.Mesh.call( this, new THREE.SphereBufferGeometry( 1, 32, 15 ), material );
};
THREE.Sky.prototype = Object.create( THREE.Mesh.prototype );
THREE.Sky.SkyShader = {
uniforms: {
luminance: { value: 1 },
turbidity: { value: 2 },
rayleigh: { value: 1 },
mieCoefficient: { value: 0.005 },
mieDirectionalG: { value: 0.8 },
sunPosition: { value: new THREE.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 );',
' 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' )
};

@ -0,0 +1,309 @@
/**
* @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
*/
THREE.Water = function ( width, height, options ) {
THREE.Mesh.call( this, new THREE.PlaneBufferGeometry( width, height ) );
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 THREE.Vector3( 0.70707, 0.70707, 0.0 );
var sunColor = new THREE.Color( options.sunColor !== undefined ? options.sunColor : 0xffffff );
var waterColor = new THREE.Color( options.waterColor !== undefined ? options.waterColor : 0x7F7F7F );
var eye = options.eye !== undefined ? options.eye : new THREE.Vector3( 0, 0, 0 );
var distortionScale = options.distortionScale !== undefined ? options.distortionScale : 20.0;
var side = options.side !== undefined ? options.side : THREE.FrontSide;
var fog = options.fog !== undefined ? options.fog : false;
//
var mirrorPlane = new THREE.Plane();
var normal = new THREE.Vector3();
var mirrorWorldPosition = new THREE.Vector3();
var cameraWorldPosition = new THREE.Vector3();
var rotationMatrix = new THREE.Matrix4();
var lookAtPosition = new THREE.Vector3( 0, 0, - 1 );
var clipPlane = new THREE.Vector4();
var view = new THREE.Vector3();
var target = new THREE.Vector3();
var q = new THREE.Vector4();
var textureMatrix = new THREE.Matrix4();
var mirrorCamera = new THREE.PerspectiveCamera();
var parameters = {
minFilter: THREE.LinearFilter,
magFilter: THREE.LinearFilter,
format: THREE.RGBFormat,
stencilBuffer: false
};
var renderTarget = new THREE.WebGLRenderTarget( textureWidth, textureHeight, parameters );
if ( ! THREE.Math.isPowerOfTwo( textureWidth ) || ! THREE.Math.isPowerOfTwo( textureHeight ) ) {
renderTarget.texture.generateMipmaps = false;
}
var mirrorShader = {
uniforms: THREE.UniformsUtils.merge( [
THREE.UniformsLib[ 'fog' ],
THREE.UniformsLib[ 'lights' ],
{
normalSampler: { value: null },
mirrorSampler: { value: null },
alpha: { value: 1.0 },
time: { value: 0.0 },
size: { value: 1.0 },
distortionScale: { value: 20.0 },
noiseScale: { value: 1.0 },
textureMatrix: { value: new THREE.Matrix4() },
sunColor: { value: new THREE.Color( 0x7F7F7F ) },
sunDirection: { value: new THREE.Vector3( 0.70707, 0.70707, 0 ) },
eye: { value: new THREE.Vector3() },
waterColor: { value: new THREE.Color( 0x555555 ) }
}
] ),
vertexShader: [
'uniform mat4 textureMatrix;',
'uniform float time;',
'varying vec4 mirrorCoord;',
'varying vec4 worldPosition;',
THREE.ShaderChunk[ 'fog_pars_vertex' ],
THREE.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;',
THREE.ShaderChunk[ 'fog_vertex' ],
THREE.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;',
'}',
THREE.ShaderChunk[ 'common' ],
THREE.ShaderChunk[ 'packing' ],
THREE.ShaderChunk[ 'bsdfs' ],
THREE.ShaderChunk[ 'fog_pars_fragment' ],
THREE.ShaderChunk[ 'lights_pars' ],
THREE.ShaderChunk[ 'shadowmap_pars_fragment' ],
THREE.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.z + 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 );',
THREE.ShaderChunk[ 'tonemapping_fragment' ],
THREE.ShaderChunk[ 'fog_fragment' ],
'}'
].join( '\n' )
};
var material = new THREE.ShaderMaterial( {
fragmentShader: mirrorShader.fragmentShader,
vertexShader: mirrorShader.vertexShader,
uniforms: THREE.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.render( scene, mirrorCamera, renderTarget, true );
scope.visible = true;
renderer.vr.enabled = currentVrEnabled;
renderer.shadowMap.autoUpdate = currentShadowAutoUpdate;
renderer.setRenderTarget( currentRenderTarget );
};
};
THREE.Water.prototype = Object.create( THREE.Mesh.prototype );
THREE.Water.prototype.constructor = THREE.Water;

@ -0,0 +1,337 @@
/**
* @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
*
*/
THREE.Water = function ( width, height, options ) {
THREE.Mesh.call( this, new THREE.PlaneBufferGeometry( width, height ) );
this.type = 'Water';
var scope = this;
options = options || {};
var color = ( options.color !== undefined ) ? new THREE.Color( options.color ) : new THREE.Color( 0xFFFFFF );
var textureWidth = options.textureWidth || 512;
var textureHeight = options.textureHeight || 512;
var clipBias = options.clipBias || 0;
var flowDirection = options.flowDirection || new THREE.Vector2( 1, 0 );
var flowSpeed = options.flowSpeed || 0.03;
var reflectivity = options.reflectivity || 0.02;
var scale = options.scale || 1;
var shader = options.shader || THREE.Water.WaterShader;
var textureLoader = new THREE.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 THREE.Matrix4();
var clock = new THREE.Clock();
// internal components
if ( THREE.Reflector === undefined ) {
console.error( 'THREE.Water: Required component THREE.Reflector not found.' );
return;
}
if ( THREE.Refractor === undefined ) {
console.error( 'THREE.Water: Required component THREE.Refractor not found.' );
return;
}
var reflector = new THREE.Reflector( width, height, {
textureWidth: textureWidth,
textureHeight: textureHeight,
clipBias: clipBias
} );
var refractor = new THREE.Refractor( width, height, {
textureWidth: textureWidth,
textureHeight: textureHeight,
clipBias: clipBias
} );
reflector.matrixAutoUpdate = false;
refractor.matrixAutoUpdate = false;
// material
this.material = new THREE.ShaderMaterial( {
uniforms: THREE.UniformsUtils.merge( [
THREE.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 = THREE.RepeatWrapping;
normalMap1.wrapS = normalMap1.wrapT = THREE.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;
};
};
THREE.Water.prototype = Object.create( THREE.Mesh.prototype );
THREE.Water.prototype.constructor = THREE.Water;
THREE.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 THREE.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 <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, uv );',
' 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' )
};
Loading…
Cancel
Save