提交 e9d3b672 编写于 作者: M Mugen87

Water: Initial Commit

上级 f5f68614
......@@ -256,7 +256,8 @@ var files = {
"webgl_test_memory2",
"webgl_tonemapping",
"webgl_trails",
"webgl_video_panorama_equirectangular"
"webgl_video_panorama_equirectangular",
"webgl_water"
],
"webgl / advanced": [
"webgl_buffergeometry",
......
......@@ -63,9 +63,9 @@ THREE.Mirror = function ( width, height, options ) {
material.uniforms.color.value = color;
material.uniforms.textureMatrix.value = textureMatrix;
scope.material = material;
this.material = material;
scope.onBeforeRender = function ( renderer, scene, camera ) {
this.onBeforeRender = function ( renderer, scene, camera ) {
if ( 'recursion' in camera.userData ) {
......@@ -190,6 +190,12 @@ THREE.Mirror = function ( width, height, options ) {
};
this.getRenderTarget = function () {
return renderTarget;
};
};
THREE.Mirror.prototype = Object.create( THREE.Mesh.prototype );
......
......@@ -253,6 +253,12 @@ THREE.Refractor = function ( width, height, options ) {
};
this.getRenderTarget = function () {
return renderTarget;
};
};
THREE.Refractor.prototype = Object.create( THREE.Mesh.prototype );
......
/**
* @author Mugen87 / https://github.com/Mugen87
*
*/
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 ) !== 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 speed = options.speed || 0.03; // water flow speed
var reflectivity = options.reflectivity || 0.02; // water reflectivity
var segments = options.segments || 4; // the amount of segments of the water geometry
var shader = options.shader || THREE.Water.WaterShader;
var textureLoader = new THREE.TextureLoader();
var flowMap = options.flowMap || textureLoader.load( 'textures/water/Water_1_M_Flow.jpg' );
var noiseMap = options.noiseMap || textureLoader.load( 'textures/water/Water_1_M_Noise.jpg' );
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
var mirror = new THREE.Mirror( width, height, {
color: color,
textureWidth: textureWidth,
textureHeight: textureHeight,
clipBias: clipBias
} );
var refractor = new THREE.Refractor( width, height, {
color: color,
textureWidth: textureWidth,
textureHeight: textureHeight,
clipBias: clipBias
} );
mirror.matrixAutoUpdate = false;
refractor.matrixAutoUpdate = false;
// material
this.material = new THREE.ShaderMaterial( {
uniforms: THREE.UniformsUtils.clone( shader.uniforms ),
vertexShader: shader.vertexShader,
fragmentShader: shader.fragmentShader,
transparent: true
} );
// maps
normalMap0.wrapS = normalMap0.wrapT = THREE.RepeatWrapping;
normalMap1.wrapS = normalMap1.wrapT = THREE.RepeatWrapping;
this.material.uniforms.tReflectionMap.value = mirror.getRenderTarget().texture;
this.material.uniforms.tRefractionMap.value = refractor.getRenderTarget().texture;
this.material.uniforms.tFlowMap.value = flowMap;
this.material.uniforms.tNoiseMap.value = noiseMap;
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 = segments; // segments
// 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( delta ) {
var config = scope.material.uniforms.config;
config.value.x += speed * delta; // flowMapOffset0
config.value.y += speed * delta; // flowMapOffset1
// reset properties if necessary
if ( config.value.x >= cycle ) {
config.value.x = 0;
// avoid 'reset' effect when both offset are set to zero
if ( config.value.y >= cycle ) {
config.value.y = halfCycle;
return;
}
}
if ( config.value.y >= cycle ) {
config.value.y = 0;
}
}
//
this.onBeforeRender = function ( renderer, scene, camera ) {
var delta = clock.getDelta();
updateTextureMatrix( camera );
updateFlow( delta );
scope.visible = false;
mirror.matrixWorld.copy( scope.matrixWorld );
refractor.matrixWorld.copy( scope.matrixWorld );
mirror.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
},
'tFlowMap': {
type: 't',
value: null
},
'tNoiseMap': {
type: 't',
value: null
},
'tNormalMap0': {
type: 't',
value: null
},
'tNormalMap1': {
type: 't',
value: null
},
'config': {
type: 'v4',
value: new THREE.Vector4()
},
'textureMatrix': {
type: 'm4',
value: null
}
},
vertexShader: [
'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;',
' gl_Position = projectionMatrix * viewMatrix * worldPosition;',
'}'
].join( '\n' ),
fragmentShader: [
'uniform sampler2D tReflectionMap;',
'uniform sampler2D tRefractionMap;',
'uniform sampler2D tFlowMap;',
'uniform sampler2D tNoiseMap;',
'uniform sampler2D tNormalMap0;',
'uniform sampler2D tNormalMap1;',
'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 segments = config.w;',
' vec3 toEye = normalize( vToEye );',
// sample flow map
' vec2 flow = texture2D( tFlowMap, vUv ).rg * 2.0 - 1.0;',
' flow.r *= -1.0;',
// sample noise map
' float cycleOffset = texture2D( tNoiseMap, vUv ).r;',
// calculate current phases
' float phase0 = cycleOffset * 0.5 + flowMapOffset0;',
' float phase1 = cycleOffset * 0.5 + flowMapOffset1;',
// sample normal maps
' vec4 normalColor0 = texture2D( tNormalMap0, ( vUv * segments ) + flow * phase0 );',
' vec4 normalColor1 = texture2D( tNormalMap1, ( vUv * segments ) + flow * phase1 );',
// 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 ) );',
// fresnel effect
' float theta = max( dot( toEye, normal ), 0.0 );',
' float reflectance = reflectivity + ( 1.0 - reflectivity ) * pow( ( 1.0 - theta ), 5.0 );',
// sample textures
' 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. then add lighting
' gl_FragColor = vec4( color, 1.0 ) * mix( refractColor, reflectColor, reflectance );',
'}'
].join( '\n' )
};
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js water</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<style>
body {
background:#777;
padding:0;
margin:0;
font-weight: bold;
overflow:hidden;
}
#info {
position: absolute;
top: 0px;
width: 100%;
color: #ffffff;
padding: 5px;
font-family:Monospace;
font-size:13px;
text-align:center;
}
a {
color: #ffffff;
}
</style>
<script src="../build/three.js"></script>
<script src="js/controls/OrbitControls.js"></script>
<script src="js/objects/Mirror.js"></script>
<script src="js/objects/Refractor.js"></script>
<script src="js/objects/Water2.js"></script>
<script src="js/Detector.js"></script>
</head>
<body>
<div id="container"></div>
<div id="info">
<a href="https://threejs.org" target="_blank" rel="noopener noreferrer">three.js</a> water
</div>
<script>
if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
var scene, camera, clock, renderer, water;
var torusKnot;
init();
animate();
function init() {
// scene
scene = new THREE.Scene();
// camera
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.1, 1000 );
camera.position.set( 10, 7, - 15 );
camera.lookAt( scene.position );
// clock
clock = new THREE.Clock();
// mesh
var torusKnotGeometry = new THREE.TorusKnotBufferGeometry( 3, 1, 256, 32 );
var torusKnotMaterial = new THREE.MeshNormalMaterial();
torusKnot = new THREE.Mesh( torusKnotGeometry, torusKnotMaterial );
torusKnot.position.y = 4;
torusKnot.scale.set( 0.5, 0.5, 0.5 );
scene.add( torusKnot );
var textureLoader = new THREE.TextureLoader();
var groundGeometry = new THREE.PlaneBufferGeometry( 20, 20 );
var groundMaterial = new THREE.MeshStandardMaterial( { roughness: 0.7 } );
var ground = new THREE.Mesh( groundGeometry, groundMaterial );
ground.rotation.x = Math.PI * - 0.5;
scene.add( ground );
textureLoader.load( 'textures/hardwood2_diffuse.jpg', function( map ) {
map.wrapS = THREE.RepeatWrapping;
map.wrapT = THREE.RepeatWrapping;
map.anisotropy = 16;
map.repeat.set( 4, 4 );
groundMaterial.map = map;
groundMaterial.needsUpdate = true;
} );
// water
water = new THREE.Water( 20, 20, {
textureWidth: 1024,
textureHeight: 1024
} );
water.position.y = 1;
water.rotation.x = Math.PI * - 0.5;
scene.add( water );
// skybox
var cubeTextureLoader = new THREE.CubeTextureLoader();
cubeTextureLoader.setPath( 'textures/cube/skybox/' );
var cubeTexture = cubeTextureLoader.load( [
'px.jpg', 'nx.jpg',
'py.jpg', 'ny.jpg',
'pz.jpg', 'nz.jpg',
] );
var cubeShader = THREE.ShaderLib[ 'cube' ];
cubeShader.uniforms[ 'tCube' ].value = cubeTexture;
var skyBoxMaterial = new THREE.ShaderMaterial( {
fragmentShader: cubeShader.fragmentShader,
vertexShader: cubeShader.vertexShader,
uniforms: cubeShader.uniforms,
side: THREE.BackSide
} );
var skyBox = new THREE.Mesh( new THREE.BoxBufferGeometry( 1000, 1000, 1000 ), skyBoxMaterial );
scene.add( skyBox );
// light
var ambientLight = new THREE.AmbientLight( 0xcccccc, 0.4 );
scene.add( ambientLight );
var directionalLight = new THREE.DirectionalLight( 0xffffff, 0.6 );
directionalLight.position.set( 1, 1, - 1 );
scene.add( directionalLight );
// renderer
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setPixelRatio( window.devicePixelRatio );
document.body.appendChild( renderer.domElement );
//
controls = new THREE.OrbitControls( camera, renderer.domElement );
//
window.addEventListener( 'resize', onResize, false );
}
function onResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
requestAnimationFrame( animate );
render();
}
function render() {
var delta = clock.getDelta();
torusKnot.rotation.x += delta;
torusKnot.rotation.y += delta * 0.5;
renderer.render( scene, camera );
}
</script>
</body>
</html>
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册