mirror of https://gitee.com/cf-fz/WebCAD.git
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,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…
Reference in new issue