forked from mrdoob/three.js
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Importing @jbouny Ocean/Water example which is based on the Mirror Sh…
…ader (see mrdoob#3856)
- Loading branch information
Showing
4 changed files
with
464 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,272 @@ | ||
/** | ||
* @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.ShaderLib['water'] = { | ||
|
||
uniforms: { "normalSampler": { type: "t", value: null }, | ||
"mirrorSampler": { type: "t", value: null }, | ||
"alpha": { type: "f", value: 1.0 }, | ||
"time": { type: "f", value: 0.0 }, | ||
"distortionScale": { type: "f", value: 20.0 }, | ||
"textureMatrix" : { type: "m4", value: new THREE.Matrix4() }, | ||
"sunColor": { type: "c", value: new THREE.Color( 0x7F7F7F ) }, | ||
"sunDirection": { type: "v3", value: new THREE.Vector3( 0.70707, 0.70707, 0 ) }, | ||
"eye": { type: "v3", value: new THREE.Vector3( 0, 0, 0 ) }, | ||
"waterColor": { type: "c", value: new THREE.Color( 0x555555 ) } | ||
}, | ||
|
||
vertexShader: [ | ||
'uniform mat4 textureMatrix;', | ||
'uniform float time;', | ||
|
||
'varying vec4 mirrorCoord;', | ||
'varying vec3 worldPosition;', | ||
|
||
'void main()', | ||
'{', | ||
' mirrorCoord = modelMatrix * vec4( position, 1.0 );', | ||
' worldPosition = mirrorCoord.xyz;', | ||
' mirrorCoord = textureMatrix * mirrorCoord;', | ||
' gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );', | ||
'}' | ||
].join('\n'), | ||
|
||
fragmentShader: [ | ||
'precision highp float;', | ||
|
||
'uniform sampler2D mirrorSampler;', | ||
'uniform float alpha;', | ||
'uniform float time;', | ||
'uniform float distortionScale;', | ||
'uniform sampler2D normalSampler;', | ||
'uniform vec3 sunColor;', | ||
'uniform vec3 sunDirection;', | ||
'uniform vec3 eye;', | ||
'uniform vec3 waterColor;', | ||
|
||
'varying vec4 mirrorCoord;', | ||
'varying vec3 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;', | ||
'}', | ||
|
||
'void main()', | ||
'{', | ||
' vec4 noise = getNoise( worldPosition.xz );', | ||
' 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;', | ||
' 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, ( vec3( 0.1 ) + reflectionSample * 0.9 + reflectionSample * specularLight ), reflectance );', | ||
' gl_FragColor = vec4( albedo, alpha );', | ||
'}' | ||
].join('\n') | ||
|
||
}; | ||
|
||
THREE.Water = function ( renderer, camera, scene, options ) { | ||
|
||
THREE.Object3D.call( this ); | ||
this.name = 'water_' + this.id; | ||
|
||
function isPowerOfTwo ( value ) { | ||
return ( value & ( value - 1 ) ) === 0; | ||
}; | ||
function optionalParameter ( value, defaultValue ) { | ||
return value !== undefined ? value : defaultValue; | ||
}; | ||
|
||
options = options || {}; | ||
|
||
this.matrixNeedsUpdate = true; | ||
|
||
var width = optionalParameter( options.textureWidth, 512 ); | ||
var height = optionalParameter( options.textureHeight, 512 ); | ||
this.clipBias = optionalParameter( options.clipBias, 0.0 ); | ||
this.alpha = optionalParameter( options.alpha, 1.0 ); | ||
this.time = optionalParameter( options.time, 0.0 ); | ||
this.normalSampler = optionalParameter( options.waterNormals, null ); | ||
this.sunDirection = optionalParameter( options.sunDirection, new THREE.Vector3( 0.70707, 0.70707, 0.0 ) ); | ||
this.sunColor = new THREE.Color( optionalParameter( options.sunColor, 0xffffff ) ); | ||
this.waterColor = new THREE.Color( optionalParameter( options.waterColor, 0x7F7F7F ) ); | ||
this.eye = optionalParameter( options.eye, new THREE.Vector3( 0, 0, 0 ) ); | ||
this.distortionScale = optionalParameter( options.distortionScale, 20.0 ); | ||
|
||
this.renderer = renderer; | ||
this.scene = scene; | ||
this.mirrorPlane = new THREE.Plane(); | ||
this.normal = new THREE.Vector3( 0, 0, 1 ); | ||
this.mirrorWorldPosition = new THREE.Vector3(); | ||
this.cameraWorldPosition = new THREE.Vector3(); | ||
this.rotationMatrix = new THREE.Matrix4(); | ||
this.lookAtPosition = new THREE.Vector3( 0, 0, -1 ); | ||
this.clipPlane = new THREE.Vector4(); | ||
|
||
if ( camera instanceof THREE.PerspectiveCamera ) | ||
this.camera = camera; | ||
else | ||
{ | ||
this.camera = new THREE.PerspectiveCamera(); | ||
console.log(this.name + ': camera is not a Perspective Camera!') | ||
} | ||
|
||
this.textureMatrix = new THREE.Matrix4(); | ||
|
||
this.mirrorCamera = this.camera.clone(); | ||
|
||
this.texture = new THREE.WebGLRenderTarget( width, height ); | ||
this.tempTexture = new THREE.WebGLRenderTarget( width, height ); | ||
|
||
var mirrorShader = THREE.ShaderLib[ "water" ]; | ||
var mirrorUniforms = THREE.UniformsUtils.clone( mirrorShader.uniforms ); | ||
|
||
this.material = new THREE.ShaderMaterial( { | ||
fragmentShader: mirrorShader.fragmentShader, | ||
vertexShader: mirrorShader.vertexShader, | ||
uniforms: mirrorUniforms, | ||
transparent: true | ||
} ); | ||
|
||
this.material.uniforms.mirrorSampler.value = this.texture; | ||
this.material.uniforms.textureMatrix.value = this.textureMatrix; | ||
this.material.uniforms.alpha.value = this.alpha; | ||
this.material.uniforms.time.value = this.time; | ||
this.material.uniforms.normalSampler.value = this.normalSampler; | ||
this.material.uniforms.sunColor.value = this.sunColor; | ||
this.material.uniforms.waterColor.value = this.waterColor; | ||
this.material.uniforms.sunDirection.value = this.sunDirection; | ||
this.material.uniforms.distortionScale.value = this.distortionScale; | ||
|
||
this.material.uniforms.eye.value = this.eye; | ||
|
||
if ( !isPowerOfTwo(width) || !isPowerOfTwo(height) ) | ||
{ | ||
this.texture.generateMipmaps = false; | ||
this.tempTexture.generateMipmaps = false; | ||
} | ||
|
||
this.updateTextureMatrix(); | ||
this.render(); | ||
}; | ||
|
||
THREE.Water.prototype = Object.create( THREE.Mirror.prototype ); | ||
|
||
|
||
THREE.Water.prototype.updateTextureMatrix = function () { | ||
|
||
function sign(x) { return x ? x < 0 ? -1 : 1 : 0; } | ||
|
||
this.updateMatrixWorld(); | ||
this.camera.updateMatrixWorld(); | ||
|
||
this.mirrorWorldPosition.setFromMatrixPosition( this.matrixWorld ); | ||
this.cameraWorldPosition.setFromMatrixPosition( this.camera.matrixWorld ); | ||
|
||
this.rotationMatrix.extractRotation( this.matrixWorld ); | ||
|
||
this.normal.set( 0, 0, 1 ); | ||
this.normal.applyMatrix4( this.rotationMatrix ); | ||
|
||
var view = this.mirrorWorldPosition.clone().sub( this.cameraWorldPosition ); | ||
var reflectView = view.reflect( this.normal ); | ||
reflectView.add( this.mirrorWorldPosition ); | ||
|
||
this.rotationMatrix.extractRotation( this.camera.matrixWorld ); | ||
|
||
this.lookAtPosition.set(0, 0, -1); | ||
this.lookAtPosition.applyMatrix4( this.rotationMatrix ); | ||
this.lookAtPosition.add( this.cameraWorldPosition ); | ||
|
||
var target = this.mirrorWorldPosition.clone().sub( this.lookAtPosition ); | ||
var reflectTarget = target.reflect( this.normal ); | ||
reflectTarget.add( this.mirrorWorldPosition ); | ||
|
||
this.up.set(0, -1, 0); | ||
this.up.applyMatrix4( this.rotationMatrix ); | ||
var reflectUp = this.up.reflect( this.normal ); | ||
|
||
this.mirrorCamera.position.copy(reflectView); | ||
this.mirrorCamera.up = reflectUp; | ||
this.mirrorCamera.lookAt(reflectTarget); | ||
this.mirrorCamera.aspect = this.camera.aspect; | ||
|
||
this.mirrorCamera.updateProjectionMatrix(); | ||
this.mirrorCamera.updateMatrixWorld(); | ||
this.mirrorCamera.matrixWorldInverse.getInverse(this.mirrorCamera.matrixWorld); | ||
|
||
// Update the texture matrix | ||
this.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 ); | ||
this.textureMatrix.multiply(this.mirrorCamera.projectionMatrix); | ||
this.textureMatrix.multiply(this.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 | ||
this.mirrorPlane.setFromNormalAndCoplanarPoint( this.normal, this.mirrorWorldPosition ); | ||
this.mirrorPlane.applyMatrix4(this.mirrorCamera.matrixWorldInverse); | ||
|
||
this.clipPlane.set(this.mirrorPlane.normal.x, this.mirrorPlane.normal.y, this.mirrorPlane.normal.z, this.mirrorPlane.constant ); | ||
|
||
var q = new THREE.Vector4(); | ||
var projectionMatrix = this.mirrorCamera.projectionMatrix; | ||
|
||
q.x = (sign(this.clipPlane.x) + projectionMatrix.elements[8]) / projectionMatrix.elements[0]; | ||
q.y = (sign(this.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 | ||
var c = new THREE.Vector4(); | ||
c = this.clipPlane.multiplyScalar( 2.0 / this.clipPlane.dot(q) ); | ||
|
||
// Replacing the third row of the projection matrix | ||
projectionMatrix.elements[2] = c.x; | ||
projectionMatrix.elements[6] = c.y; | ||
projectionMatrix.elements[10] = c.z + 1.0 - this.clipBias; | ||
projectionMatrix.elements[14] = c.w; | ||
|
||
var worldCoordinates = new THREE.Vector3(); | ||
worldCoordinates.setFromMatrixPosition( this.camera.matrixWorld ); | ||
this.eye = worldCoordinates; | ||
this.material.uniforms.eye.value = this.eye; | ||
}; |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Oops, something went wrong.