2
0
Эх сурвалжийг харах

Merge remote-tracking branch 'zz85/ocean' into dev

Mr.doob 11 жил өмнө
parent
commit
cb1eefa8c9

+ 1 - 0
examples/index.html

@@ -243,6 +243,7 @@
 				"webgl_sandbox",
 				"webgl_shader",
 				"webgl_shader2",
+				"webgl_shaders_ocean",
 				"webgl_shader_lava",
 				"webgl_shading_physical",
 				"webgl_shadowmap",

+ 272 - 0
examples/js/WaterShader.js

@@ -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;
+};

BIN
examples/textures/waternormals.jpg


+ 191 - 0
examples/webgl_shaders_ocean.html

@@ -0,0 +1,191 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgl - geometry - terrain</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 {
+				color: #000;
+				font-family:Monospace;
+				font-size:13px;
+				text-align:center;
+				margin: 0px;
+				overflow: hidden;
+			}
+
+			#info {
+				position: absolute;
+				top: 0px; width: 100%;
+				padding: 5px;
+			}
+
+			a {
+
+				color: #a06851;
+			}
+
+		</style>
+	</head>
+	<body>
+
+		<div id="info"><a href="http://threejs.org" target="_blank">three.js</a> - webgl ocean demo</div>
+
+		<script src="../build/three.min.js"></script>
+
+		<script src="js/controls/OrbitControls.js"></script>
+		<script src="js/Mirror.js"></script>
+		<script src="js/WaterShader.js"></script>
+
+		<script src="js/Detector.js"></script>
+		<script src="js/libs/stats.min.js"></script>
+
+		<script>
+
+			if ( ! Detector.webgl ) {
+
+				Detector.addGetWebGLMessage();
+				document.getElementById( 'container' ).innerHTML = "";
+
+			}
+
+			var container, stats;
+			var camera, scene, renderer;
+
+			var parameters = {
+				width: 2000,
+				height: 2000,
+				widthSegments: 250,
+				heightSegments: 250,
+				depth: 1500,
+				param: 4,
+				filterparam: 1
+			}
+			
+			var waterNormals;
+
+			init();
+			animate();
+
+			function init() {
+
+				container = document.createElement( 'div' );
+				document.body.appendChild( container );
+
+				renderer = new THREE.WebGLRenderer();
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				container.appendChild( renderer.domElement );
+
+				scene = new THREE.Scene();
+				camera = new THREE.PerspectiveCamera( 55, window.innerWidth / window.innerHeight, 0.5, 3000000 );
+
+				// resize(innerWidth, innerHeight);
+
+				camera.position.set( 0, Math.max( parameters.width * 1.5, parameters.height ) / 8, -parameters.height );
+				camera.lookAt( new THREE.Vector3( 0, 0, 0 ) );
+
+				controls = new THREE.OrbitControls( camera, renderer.domElement );
+				controls.userPan = false;
+				controls.userPanSpeed = 0.0;
+				controls.maxDistance = 5000.0;
+				controls.maxPolarAngle = Math.PI * 0.495;
+
+				directionalLight = new THREE.DirectionalLight( 0xffff55, 1 );
+				directionalLight.position.set( -600, 300, 600 );
+				scene.add( directionalLight );
+
+				
+				waterNormals = new THREE.ImageUtils.loadTexture( 'textures/waternormals.jpg' );
+				waterNormals.wrapS = waterNormals.wrapT = THREE.RepeatWrapping; 
+
+				water = new THREE.Water( renderer, camera, scene, {
+					textureWidth: 512, 
+					textureHeight: 512,
+					waterNormals: waterNormals,
+					alpha: 	1.0,
+					sunDirection: directionalLight.position.normalize(),
+					sunColor: 0xffffff,
+					waterColor: 0x001e0f,
+					distortionScale: 50.0,
+				} );
+
+
+				mirrorMesh = new THREE.Mesh(
+					new THREE.PlaneGeometry( parameters.width * 500, parameters.height * 500, 50, 50 ), 
+					water.material
+				);
+				
+
+				mirrorMesh.add( water );
+				mirrorMesh.rotation.x = - Math.PI * 0.5;
+				scene.add( mirrorMesh );
+
+
+				// load skybox
+
+				var path = "textures/cube/skybox/";
+
+				var cubeMap = THREE.ImageUtils.loadTextureCube( [
+					path + 'px.jpg',
+					path + 'nx.jpg',
+					path + 'py.jpg',
+					path + 'ny.jpg',
+					path + 'pz.jpg',
+					path + 'nz.jpg'
+				] );
+				cubeMap.format = THREE.RGBFormat;
+
+				var cubeShader = THREE.ShaderLib['cube'];
+				cubeShader.uniforms['tCube'].value = cubeMap;
+
+				var skyBoxMaterial = new THREE.ShaderMaterial( {
+					fragmentShader: cubeShader.fragmentShader,
+					vertexShader: cubeShader.vertexShader,
+					uniforms: cubeShader.uniforms,
+					depthWrite: false,
+					side: THREE.BackSide
+				});
+
+				var skyBox = new THREE.Mesh(
+					new THREE.CubeGeometry( 1000000, 1000000, 1000000 ),
+					skyBoxMaterial
+				);
+				
+				scene.add( skyBox );
+
+			}
+
+			//
+
+			function animate() {
+
+				requestAnimationFrame( animate );
+
+				
+
+				render();
+
+			}
+
+			function render() {
+
+				water.material.uniforms.time.value += 1.0 / 60.0;
+				controls.update();
+				water.render();
+				renderer.render( scene, camera );
+
+			}
+
+			function resize(width, height) {
+
+				camera.aspect =  width / height;
+				camera.updateProjectionMatrix();
+				renderer.setSize( width, height );
+			
+			}
+
+
+		</script>
+
+	</body>
+</html>