浏览代码

added ocean2 shader demo. Fixes #4622

ttmike 11 年之前
父节点
当前提交
9f3efe07db
共有 3 个文件被更改,包括 1043 次插入0 次删除
  1. 381 0
      examples/js/Ocean.js
  2. 388 0
      examples/js/shaders/OceanShaders.js
  3. 274 0
      examples/webgl_shaders_ocean2.html

+ 381 - 0
examples/js/Ocean.js

@@ -0,0 +1,381 @@
+THREE.Ocean = function (renderer, camera, scene, options) {
+    // flag used to trigger parameter changes
+    this.changed = true;
+    this.initial = true;
+    
+    // Assign required parameters as object properties
+    this.oceanCamera = new THREE.OrthographicCamera(); //camera.clone();
+    this.oceanCamera.position.z = 1;
+    this.renderer = renderer;
+    this.renderer.clearColor(new THREE.Color(1.0, 1.0, 1.0, 1.0));
+    
+    this.scene = new THREE.Scene();
+
+    // Initialise helper objects
+    this.helpers();
+
+    //this.fullscreenVertexBuffer = this.renderer.context.createBuffer();
+    
+    // Enable necessary extensions
+    this.renderer.context.getExtension('OES_texture_float');
+    this.renderer.context.getExtension('OES_texture_float_linear');
+    
+    // Assign optional parameters as variables and object properties
+    function optionalParameter(value, defaultValue) {
+        return value !== undefined ? value : defaultValue;
+    };
+    options = options || {};
+    this.clearColor = optionalParameter(options.CLEAR_COLOR, [1.0, 1.0, 1.0, 0.0]);
+    this.geometryOrigin = optionalParameter(options.GEOMETRY_ORIGIN, [-1000.0, -1000.0]);
+    this.sunDirectionX = optionalParameter(options.SUN_DIRECTION[0], -1.0);
+    this.sunDirectionY = optionalParameter(options.SUN_DIRECTION[1], 1.0);
+    this.sunDirectionZ = optionalParameter(options.SUN_DIRECTION[2], 1.0);
+    this.oceanColor = optionalParameter(options.OCEAN_COLOR, new THREE.Vector3(0.004, 0.016, 0.047));
+    this.skyColor = optionalParameter(options.SKY_COLOR, new THREE.Vector3(3.2, 9.6, 12.8));
+    this.exposure = optionalParameter(options.EXPOSURE, 0.35);
+    this.geometryResolution = optionalParameter(options.GEOMETRY_RESOLUTION, 32);
+    this.geometrySize = optionalParameter(options.GEOMETRY_SIZE, 2000);
+    this.resolution = optionalParameter(options.RESOLUTION, 64);
+    this.floatSize = optionalParameter(options.SIZE_OF_FLOAT, 4);
+    this.windX = optionalParameter(options.INITIAL_WIND[0], 10.0),
+    this.windY = optionalParameter(options.INITIAL_WIND[1], 10.0),
+    this.size = optionalParameter(options.INITIAL_SIZE, 250.0),
+    this.choppiness = optionalParameter(options.INITIAL_CHOPPINESS, 1.5);
+    
+    // 
+    this.matrixNeedsUpdate = false;
+    
+    // Setup framebuffer pipeline
+    var LinearClampParams = {
+        minFilter: THREE.LinearFilter,
+        magFilter: THREE.LinearFilter,
+        wrapS: THREE.ClampToEdgeWrapping,
+        wrapT: THREE.ClampToEdgeWrapping,
+        format: THREE.RGBAFormat,
+        stencilBuffer: false,
+        depthBuffer: false,
+        premultiplyAlpha: false,
+        type: THREE.FloatType
+    };
+    var NearestClampParams = {
+        minFilter: THREE.NearestFilter,
+        magFilter: THREE.NearestFilter,
+        wrapS: THREE.ClampToEdgeWrapping,
+        wrapT: THREE.ClampToEdgeWrapping,
+        format: THREE.RGBAFormat,
+        stencilBuffer: false,
+        depthBuffer: false,
+        premultiplyAlpha:false,
+        type: THREE.FloatType
+    };
+    var NearestRepeatParams = {
+        minFilter: THREE.NearestFilter,
+        magFilter: THREE.NearestFilter,
+        wrapS: THREE.RepeatWrapping,
+        wrapT: THREE.RepeatWrapping,
+        format: THREE.RGBAFormat,
+        stencilBuffer: false,
+        depthBuffer: false,
+        premultiplyAlpha: false,
+        type: THREE.FloatType
+    };
+    this.initialSpectrumFramebuffer = new THREE.WebGLRenderTarget(this.resolution, this.resolution, NearestRepeatParams);
+    this.spectrumFramebuffer = new THREE.WebGLRenderTarget(this.resolution, this.resolution, NearestClampParams);
+    this.pingPhaseFramebuffer = new THREE.WebGLRenderTarget(this.resolution, this.resolution, NearestClampParams);
+    this.pongPhaseFramebuffer = new THREE.WebGLRenderTarget(this.resolution, this.resolution, NearestClampParams);
+    this.pingTransformFramebuffer = new THREE.WebGLRenderTarget(this.resolution, this.resolution, NearestClampParams);
+    this.pongTransformFramebuffer = new THREE.WebGLRenderTarget(this.resolution, this.resolution, NearestClampParams);
+    this.displacementMapFramebuffer = new THREE.WebGLRenderTarget(this.resolution, this.resolution, LinearClampParams);
+    this.normalMapFramebuffer = new THREE.WebGLRenderTarget(this.resolution, this.resolution, LinearClampParams);
+    
+    // Define shaders and constant uniforms
+    ////////////////////////////////////////
+    
+    // 0 - The vertex shader used in all of the simulation steps
+    var fullscreeenVertexShader = THREE.ShaderLib["ocean_sim_vertex"];
+        
+    // 1 - Horizontal wave vertices used for FFT
+    var oceanHorizontalShader = THREE.ShaderLib["ocean_subtransform"];
+    var oceanHorizontalUniforms = THREE.UniformsUtils.clone(oceanHorizontalShader.uniforms);
+    this.materialOceanHorizontal = new THREE.ShaderMaterial({
+        uniforms: oceanHorizontalUniforms,
+        vertexShader: fullscreeenVertexShader.vertexShader,
+        fragmentShader: "#define HORIZONTAL \n" + oceanHorizontalShader.fragmentShader
+    });
+    this.materialOceanHorizontal.uniforms.u_transformSize = { type: "f", value: this.resolution };
+    this.materialOceanHorizontal.uniforms.u_subtransformSize = { type: "f", value: null };
+    this.materialOceanHorizontal.uniforms.u_input = { type: "t", value: null };
+    this.materialOceanHorizontal.depthTest = false;
+    
+    // 2 - Vertical wave vertices used for FFT
+    var oceanVerticalShader = THREE.ShaderLib["ocean_subtransform"];
+    var oceanVerticalUniforms = THREE.UniformsUtils.clone(oceanVerticalShader.uniforms);
+    this.materialOceanVertical = new THREE.ShaderMaterial({
+        uniforms: oceanVerticalUniforms,
+        vertexShader: fullscreeenVertexShader.vertexShader,
+        fragmentShader: oceanVerticalShader.fragmentShader
+    });
+    this.materialOceanVertical.uniforms.u_transformSize = { type: "f", value: this.resolution };
+    this.materialOceanVertical.uniforms.u_subtransformSize = { type: "f", value: null };
+    this.materialOceanVertical.uniforms.u_input = { type: "t", value: null };
+    this.materialOceanVertical.depthTest = false;
+    
+    // 3 - Initial spectrum used to generate height map
+    var initialSpectrumShader = THREE.ShaderLib["ocean_initial_spectrum"];
+    var initialSpectrumUniforms = THREE.UniformsUtils.clone(initialSpectrumShader.uniforms);
+    this.materialInitialSpectrum = new THREE.ShaderMaterial({
+        uniforms: initialSpectrumUniforms,
+        vertexShader: fullscreeenVertexShader.vertexShader,
+        fragmentShader:initialSpectrumShader.fragmentShader
+    });
+    this.materialInitialSpectrum.uniforms.u_wind = { type: "v2", value: null };
+    this.materialInitialSpectrum.uniforms.u_resolution = { type: "f", value: this.resolution };
+    this.materialInitialSpectrum.depthTest = false;
+    
+    // 4 - Phases used to animate heightmap
+    var phaseShader = THREE.ShaderLib["ocean_phase"];
+    var phaseUniforms = THREE.UniformsUtils.clone(phaseShader.uniforms);
+    this.materialPhase = new THREE.ShaderMaterial({
+        uniforms: phaseUniforms,
+        vertexShader: fullscreeenVertexShader.vertexShader,
+        fragmentShader: phaseShader.fragmentShader
+    });
+    this.materialPhase.uniforms.u_resolution = { type: "f", value: this.resolution };
+    this.materialPhase.depthTest = false;
+    
+    // 5 - Shader used to update spectrum
+    var spectrumShader = THREE.ShaderLib["ocean_spectrum"];
+    var spectrumUniforms = THREE.UniformsUtils.clone(spectrumShader.uniforms);
+    this.materialSpectrum = new THREE.ShaderMaterial({
+        uniforms: spectrumUniforms,
+        vertexShader: fullscreeenVertexShader.vertexShader,
+        fragmentShader: spectrumShader.fragmentShader
+    });
+    this.materialSpectrum.uniforms.u_initialSpectrum = { type: "t", value: null };
+    this.materialSpectrum.uniforms.u_resolution = { type: "f", value: this.resolution };
+    this.materialSpectrum.depthTest = false;
+
+    // 6 - Shader used to update spectrum normals
+    var normalShader = THREE.ShaderLib["ocean_normals"];
+    var normalUniforms = THREE.UniformsUtils.clone(normalShader.uniforms);
+    this.materialNormal = new THREE.ShaderMaterial({
+        uniforms: normalUniforms,
+        vertexShader: fullscreeenVertexShader.vertexShader,
+        fragmentShader: normalShader.fragmentShader
+    });
+    this.materialNormal.uniforms.u_displacementMap = { type: "t", value: null };
+    this.materialNormal.uniforms.u_resolution = { type: "f", value: this.resolution };
+    this.materialNormal.depthTest = false;
+
+    // 7 - Shader used to update normals
+    var oceanShader = THREE.ShaderLib["ocean_main"];
+    var oceanUniforms = THREE.UniformsUtils.clone(oceanShader.uniforms);
+    var oceanAttributes = THREE.UniformsUtils.clone(oceanShader.attributes);
+    this.materialOcean = new THREE.ShaderMaterial({
+        attributes: oceanAttributes,
+        uniforms: oceanUniforms,
+        vertexShader: oceanShader.vertexShader,
+        fragmentShader: oceanShader.fragmentShader
+    });
+    // this.materialOcean.wireframe = true;
+    this.materialOcean.uniforms.u_geometrySize = { type: "f", value: this.resolution };
+    this.materialOcean.uniforms.u_displacementMap = { type: "t", value: this.displacementMapFramebuffer };
+    this.materialOcean.uniforms.u_normalMap = { type: "t", value: this.normalMapFramebuffer }; 
+    this.materialOcean.uniforms.u_oceanColor = { type: "v3", value: this.oceanColor }; 
+    this.materialOcean.uniforms.u_skyColor = { type: "v3", value: this.skyColor };
+    this.materialOcean.uniforms.u_sunDirection = { type: "v3", value: new THREE.Vector3(this.sunDirectionX,this.sunDirectionY,this.sunDirectionZ )};
+    this.materialOcean.uniforms.u_exposure = { type: "f", value: this.exposure };
+
+    // Disable blending to prevent default premultiplied alpha values
+    this.materialOceanHorizontal.blending = 0;
+    this.materialOceanVertical.blending = 0;
+    this.materialInitialSpectrum.blending = 0;
+    this.materialPhase.blending = 0;
+    this.materialSpectrum.blending = 0;
+    this.materialNormal.blending = 0;
+    this.materialOcean.blending = 0;
+
+    // Seed the simulation
+    var screenGeo = new THREE.PlaneGeometry(2, 2);
+    screenGeo.buffersNeedUpdate = true;
+
+    // Create the simulation plane
+    this.screenQuad = new THREE.Mesh(screenGeo);
+    this.scene.add(this.screenQuad);
+
+    // Initialise spectrum data
+    this.generateSeedPhaseTexture();
+
+    // Generate the ocean mesh
+    this.generateMesh();
+};
+
+THREE.Ocean.prototype.generateMesh = function () {
+    var geo = new THREE.Geometry();
+    // setup vertices
+    var x, z;
+    var listUV = [];
+    var vU = [], vV = [];
+    for (z = 0; z < this.geometryResolution; z++) {
+        for (x = 0; x < this.geometryResolution; x++) {
+            geo.vertices.push(new THREE.Vector3(
+                (x * this.geometrySize) / (this.geometryResolution - 1) + this.geometryOrigin[0],
+                0.0,
+                (z * this.geometrySize) / (this.geometryResolution - 1) + this.geometryOrigin[1]
+            ));
+            vU.push(x / (this.geometryResolution - 1));
+            vV.push(z / (this.geometryResolution - 1));
+        }
+    }
+    listUV.push(vU);
+    listUV.push(vV);
+    // setup faces (each pass draws a square with 2 triangles)
+    for (z = 0; z < this.geometryResolution - 1; z++) {
+        for (x = 0; x < this.geometryResolution - 1; x++) {
+            var topLeft = z * this.geometryResolution + x,
+                topRight = topLeft + 1,
+                bottomLeft = topLeft + this.geometryResolution,
+                bottomRight = bottomLeft + 1;
+            geo.faces.push(new THREE.Face3(topLeft, bottomLeft, bottomRight));
+            geo.faces.push(new THREE.Face3(bottomRight, topRight, topLeft));
+            geo.faceVertexUvs[0].push(
+                new Array(
+                    new THREE.Vector2(listUV[0][topLeft], listUV[1][topLeft]),
+                    new THREE.Vector2(listUV[0][bottomLeft], listUV[1][bottomLeft]),
+                    new THREE.Vector2(listUV[0][bottomRight], listUV[1][bottomRight])
+                )
+            );
+            geo.faceVertexUvs[0].push(
+                new Array(
+                    new THREE.Vector2(listUV[0][bottomRight], listUV[1][bottomRight]),
+                    new THREE.Vector2(listUV[0][topRight], listUV[1][topRight]),
+                    new THREE.Vector2(listUV[0][topLeft], listUV[1][topLeft])
+                )
+            );
+        }
+    }
+
+    this.oceanMesh = new THREE.Mesh(geo, this.materialOcean);
+};
+
+THREE.Ocean.prototype.render = function () {
+    this.scene.overrideMaterial = null;
+    
+    if (this.changed)
+        this.renderInitialSpectrum();
+    
+    this.renderWavePhase();
+    this.renderSpectrum();
+    this.renderSpectrumFFT();
+    this.renderNormalMap();
+    this.scene.overrideMaterial = null;
+};
+
+THREE.Ocean.prototype.generateSeedPhaseTexture = function() {
+    // Setup the seed texture
+    this.pingPhase = true;
+    var phaseArray = new window.Float32Array(this.resolution * this.resolution * 4);
+    for (var i = 0; i < this.resolution; i++) {
+        for (var j = 0; j < this.resolution; j++) {
+            phaseArray[i * this.resolution * 4 + j * 4] =  Math.random() * 2.0 * Math.PI;
+            phaseArray[i * this.resolution * 4 + j * 4 + 1] = 0.0;
+            phaseArray[i * this.resolution * 4 + j * 4 + 2] = 0.0;
+            phaseArray[i * this.resolution * 4 + j * 4 + 3] = 0.0;
+        }
+    }
+    
+    this.pingPhaseTexture = new THREE.DataTexture(phaseArray, this.resolution, this.resolution, THREE.RGBAFormat);
+    this.pingPhaseTexture.minFilter = THREE.NearestFilter;
+    this.pingPhaseTexture.magFilter = THREE.NearestFilter;
+    this.pingPhaseTexture.wrapS = THREE.ClampToEdgeWrapping;
+    this.pingPhaseTexture.wrapT = THREE.ClampToEdgeWrapping;
+    this.pingPhaseTexture.type = THREE.FloatType;
+    this.pingPhaseTexture.needsUpdate = true;
+};
+
+THREE.Ocean.prototype.renderInitialSpectrum = function () {
+    this.scene.overrideMaterial = this.materialInitialSpectrum;
+    this.materialInitialSpectrum.uniforms.u_wind.value = new THREE.Vector2(this.windX, this.windY);
+    this.materialInitialSpectrum.uniforms.u_size.value = this.size;
+    this.renderer.render(this.scene, this.oceanCamera, this.initialSpectrumFramebuffer, true);
+};
+
+THREE.Ocean.prototype.renderWavePhase = function () {
+    this.scene.overrideMaterial = this.materialPhase;
+    this.screenQuad.material = this.materialPhase;
+    if (this.initial) {
+        this.materialPhase.uniforms.u_phases.value = this.pingPhaseTexture;
+        this.initial = false;
+    }else {
+        this.materialPhase.uniforms.u_phases.value = this.pingPhase ? this.pingPhaseFramebuffer  : this.pongPhaseFramebuffer;
+    }
+    this.materialPhase.uniforms.u_deltaTime.value = this.deltaTime;
+    this.materialPhase.uniforms.u_size.value = this.size;
+    this.renderer.render(this.scene, this.oceanCamera, this.pingPhase ? this.pongPhaseFramebuffer : this.pingPhaseFramebuffer);
+    this.pingPhase = !this.pingPhase;
+};
+
+THREE.Ocean.prototype.renderSpectrum = function () {
+    this.scene.overrideMaterial = this.materialSpectrum;
+    this.materialSpectrum.uniforms.u_initialSpectrum.value = this.initialSpectrumFramebuffer;
+    this.materialSpectrum.uniforms.u_phases.value = this.pingPhase ? this.pingPhaseFramebuffer : this.pongPhaseFramebuffer;
+    this.materialSpectrum.uniforms.u_choppiness.value = this.choppiness ;
+    this.materialSpectrum.uniforms.u_size.value = this.size ;
+    this.renderer.render(this.scene, this.oceanCamera, this.spectrumFramebuffer);
+};
+
+THREE.Ocean.prototype.renderSpectrumFFT = function() {
+    //GPU FFT using Stockham formulation
+    var iterations = this.log2(this.resolution);
+    
+    this.scene.overrideMaterial = this.materialOceanHorizontal;
+    var i;
+    for (i = 0; i < iterations; i++) {
+        if (i === 0) {
+            this.materialOceanHorizontal.uniforms.u_input.value = this.spectrumFramebuffer;
+            this.materialOceanHorizontal.uniforms.u_subtransformSize.value = Math.pow(2, (i % (iterations)) + 1);
+            this.renderer.render(this.scene, this.oceanCamera, this.pingTransformFramebuffer);
+        } 
+        else if (i % 2 === 1) {
+            this.materialOceanHorizontal.uniforms.u_input.value = this.pingTransformFramebuffer;
+            this.materialOceanHorizontal.uniforms.u_subtransformSize.value = Math.pow(2, (i % (iterations)) + 1);
+            this.renderer.render(this.scene, this.oceanCamera, this.pongTransformFramebuffer);
+        }
+        else {
+            this.materialOceanHorizontal.uniforms.u_input.value = this.pongTransformFramebuffer;
+            this.materialOceanHorizontal.uniforms.u_subtransformSize.value = Math.pow(2, (i % (iterations)) + 1);
+            this.renderer.render(this.scene, this.oceanCamera, this.pingTransformFramebuffer);
+        }
+    }
+    this.scene.overrideMaterial = this.materialOceanVertical;
+    for (i = iterations; i < iterations*2; i++) {
+        if (i === iterations * 2 - 1) {
+            this.materialOceanVertical.uniforms.u_input.value = (iterations % 2 === 0) ? this.pingTransformFramebuffer : this.pongTransformFramebuffer;
+            this.materialOceanVertical.uniforms.u_subtransformSize.value = Math.pow(2, (i % (iterations)) + 1);
+            this.renderer.render(this.scene, this.oceanCamera, this.displacementMapFramebuffer);
+        }
+        else if (i % 2 === 1) {
+            this.materialOceanVertical.uniforms.u_input.value = this.pingTransformFramebuffer;
+            this.materialOceanVertical.uniforms.u_subtransformSize.value = Math.pow(2, (i % (iterations)) + 1);
+            this.renderer.render(this.scene, this.oceanCamera, this.pongTransformFramebuffer);
+        }
+        else {
+            this.materialOceanVertical.uniforms.u_input.value = this.pongTransformFramebuffer;
+            this.materialOceanVertical.uniforms.u_subtransformSize.value = Math.pow(2, (i % (iterations)) + 1);
+            this.renderer.render(this.scene, this.oceanCamera, this.pingTransformFramebuffer);
+        }
+    }
+};
+
+THREE.Ocean.prototype.renderNormalMap = function () {
+    this.scene.overrideMaterial = this.materialNormal;
+    if (this.changed) this.materialNormal.uniforms.u_size.value = this.size;
+    this.materialNormal.uniforms.u_displacementMap.value = this.displacementMapFramebuffer;
+    this.renderer.render(this.scene, this.oceanCamera, this.normalMapFramebuffer, true);
+};
+
+THREE.Ocean.prototype.helpers = function () {
+    this.log2 = function (number) {
+        return Math.log(number) / Math.log(2);
+    };
+};

+ 388 - 0
examples/js/shaders/OceanShaders.js

@@ -0,0 +1,388 @@
+// Author: Aleksandr Albert
+// Website: www.routter.co.tt
+
+// Description: A deep water ocean shader set
+// based on an implementation of a Tessendorf Waves
+// originally presented by David Li ( www.david.li/waves )
+
+// The general method is to apply shaders to simulation Framebuffers
+// and then sample these framebuffers when rendering the ocean mesh
+
+// The set uses 7 shaders:
+
+// -- Simulation shaders
+// [1] ocean_sim_vertex         -> Vertex shader used to set up a 2x2 simulation plane centered at (0,0)
+// [2] ocean_subtransform       -> Fragment shader used to subtransform the mesh (generates the displacement map)
+// [3] ocean_initial_spectrum   -> Fragment shader used to set intitial wave frequency at a texel coordinate
+// [4] ocean_phase              -> Fragment shader used to set wave phase at a texel coordinate
+// [5] ocean_spectrum           -> Fragment shader used to set current wave frequency at a texel coordinate
+// [6] ocean_normal             -> Fragment shader used to set face normals at a texel coordinate
+
+// -- Rendering Shader
+// [7] ocean_main               -> Vertex and Fragment shader used to create the final render
+
+
+THREE.ShaderLib['ocean_sim_vertex'] = {
+    varying: {
+        "vUV": { type: "v2" }
+    },
+    vertexShader: [
+        'varying vec2 vUV;',
+
+        'void main (void) {',
+            'vUV = position.xy * 0.5 + 0.5;',
+            'gl_Position = vec4(position, 1.0 );',
+        '}'
+    ].join('\n')
+};
+THREE.ShaderLib['ocean_subtransform'] = {
+    uniforms: {
+        "u_input": { type: "t", value: null },
+        "u_transformSize": { type: "f", value: 512.0 },
+        "u_subtransformSize": { type: "f", value: 250.0 }
+    },
+    varying: {
+        "vUV": { type: "v2" }
+    },
+    fragmentShader: [
+		//GPU FFT using a Stockham formulation
+        'precision highp float;',
+
+		'const float PI = 3.14159265359;',
+
+		'uniform sampler2D u_input;',
+        'uniform float u_transformSize;',
+        'uniform float u_subtransformSize;',
+
+        'varying vec2 vUV;',
+        
+		'vec2 multiplyComplex (vec2 a, vec2 b) {',
+			'return vec2(a[0] * b[0] - a[1] * b[1], a[1] * b[0] + a[0] * b[1]);',
+		'}',
+
+		'void main (void) {',
+			'#ifdef HORIZONTAL',
+            'float index = vUV.x * u_transformSize - 0.5;',
+            '#else',
+            'float index = vUV.y * u_transformSize - 0.5;',
+            '#endif',
+
+            'float evenIndex = floor(index / u_subtransformSize) * (u_subtransformSize * 0.5) + mod(index, u_subtransformSize * 0.5);',
+
+            //transform two complex sequences simultaneously
+            '#ifdef HORIZONTAL',
+            'vec4 even = texture2D(u_input, vec2(evenIndex + 0.5, gl_FragCoord.y) / u_transformSize).rgba;',
+            'vec4 odd = texture2D(u_input, vec2(evenIndex + u_transformSize * 0.5 + 0.5, gl_FragCoord.y) / u_transformSize).rgba;',
+            '#else',
+            'vec4 even = texture2D(u_input, vec2(gl_FragCoord.x, evenIndex + 0.5) / u_transformSize).rgba;',
+            'vec4 odd = texture2D(u_input, vec2(gl_FragCoord.x, evenIndex + u_transformSize * 0.5 + 0.5) / u_transformSize).rgba;',
+            '#endif',
+
+            'float twiddleArgument = -2.0 * PI * (index / u_subtransformSize);',
+            'vec2 twiddle = vec2(cos(twiddleArgument), sin(twiddleArgument));',
+
+            'vec2 outputA = even.xy + multiplyComplex(twiddle, odd.xy);',
+            'vec2 outputB = even.zw + multiplyComplex(twiddle, odd.zw);',
+
+            'gl_FragColor = vec4(outputA, outputB);',
+		'}'
+    ].join('\n')
+};
+THREE.ShaderLib['ocean_initial_spectrum'] = {
+    uniforms: {
+        "u_wind": { type: "v2", value: new THREE.Vector2(10.0, 10.0) },
+        "u_resolution": { type: "f", value: 512.0 },
+        "u_size": { type: "f", value: 250.0 },
+    },
+    fragmentShader: [
+        'precision highp float;',
+
+        'const float PI = 3.14159265359;',
+        'const float G = 9.81;',
+        'const float KM = 370.0;',
+        'const float CM = 0.23;',
+
+        'uniform vec2 u_wind;',
+        'uniform float u_resolution;',
+        'uniform float u_size;',
+        
+        'float square (float x) {',
+            'return x * x;',
+        '}',
+
+        'float omega (float k) {',
+            'return sqrt(G * k * (1.0 + square(k / KM)));',
+        '}',
+
+        'float tanh (float x) {',
+            'return (1.0 - exp(-2.0 * x)) / (1.0 + exp(-2.0 * x));',
+        '}',
+
+        'void main (void) {',
+            'vec2 coordinates = gl_FragCoord.xy - 0.5;',
+            
+            'float n = (coordinates.x < u_resolution * 0.5) ? coordinates.x : coordinates.x - u_resolution;',
+            'float m = (coordinates.y < u_resolution * 0.5) ? coordinates.y : coordinates.y - u_resolution;',
+            
+            'vec2 K = (2.0 * PI * vec2(n, m)) / u_size;',
+            'float k = length(K);',
+            
+            'float l_wind = length(u_wind);',
+
+            'float Omega = 0.84;',
+            'float kp = G * square(Omega / l_wind);',
+
+            'float c = omega(k) / k;',
+            'float cp = omega(kp) / kp;',
+
+            'float Lpm = exp(-1.25 * square(kp / k));',
+            'float gamma = 1.7;',
+            'float sigma = 0.08 * (1.0 + 4.0 * pow(Omega, -3.0));',
+            'float Gamma = exp(-square(sqrt(k / kp) - 1.0) / 2.0 * square(sigma));',
+            'float Jp = pow(gamma, Gamma);',
+            'float Fp = Lpm * Jp * exp(-Omega / sqrt(10.0) * (sqrt(k / kp) - 1.0));',
+            'float alphap = 0.006 * sqrt(Omega);',
+            'float Bl = 0.5 * alphap * cp / c * Fp;',
+
+            'float z0 = 0.000037 * square(l_wind) / G * pow(l_wind / cp, 0.9);',
+            'float uStar = 0.41 * l_wind / log(10.0 / z0);',
+            'float alpham = 0.01 * ((uStar < CM) ? (1.0 + log(uStar / CM)) : (1.0 + 3.0 * log(uStar / CM)));',
+            'float Fm = exp(-0.25 * square(k / KM - 1.0));',
+            'float Bh = 0.5 * alpham * CM / c * Fm * Lpm;',
+
+            'float a0 = log(2.0) / 4.0;',
+            'float am = 0.13 * uStar / CM;',
+            'float Delta = tanh(a0 + 4.0 * pow(c / cp, 2.5) + am * pow(CM / c, 2.5));',
+
+            'float cosPhi = dot(normalize(u_wind), normalize(K));',
+
+            'float S = (1.0 / (2.0 * PI)) * pow(k, -4.0) * (Bl + Bh) * (1.0 + Delta * (2.0 * cosPhi * cosPhi - 1.0));',
+
+            'float dk = 2.0 * PI / u_size;',
+            'float h = sqrt(S / 2.0) * dk;',
+
+            'if (K.x == 0.0 && K.y == 0.0) {',
+                'h = 0.0;', //no DC term
+            '}',
+            'gl_FragColor = vec4(h, 0.0, 0.0, 0.0);',
+        '}'
+    ].join('\n')
+};
+THREE.ShaderLib['ocean_phase'] = {
+    uniforms: {
+        "u_phases": { type: "t", value: null },
+        "u_deltaTime": { type: "f", value: null },
+        "u_resolution": { type: "f", value: null },
+        "u_size": { type: "f", value: null },
+    },
+    varying: {
+        "vUV": { type: "v2" }
+    },
+    fragmentShader: [
+        'precision highp float;',
+
+        'const float PI = 3.14159265359;',
+        'const float G = 9.81;',
+        'const float KM = 370.0;',
+
+        'varying vec2 vUV;',
+
+        'uniform sampler2D u_phases;',
+        'uniform float u_deltaTime;',
+        'uniform float u_resolution;',
+        'uniform float u_size;',
+
+        'float omega (float k) {',
+            'return sqrt(G * k * (1.0 + k * k / KM * KM));',
+        '}',
+
+        'void main (void) {',
+            'float deltaTime = 1.0 / 60.0;',
+            'vec2 coordinates = gl_FragCoord.xy - 0.5;',
+            'float n = (coordinates.x < u_resolution * 0.5) ? coordinates.x : coordinates.x - u_resolution;',
+            'float m = (coordinates.y < u_resolution * 0.5) ? coordinates.y : coordinates.y - u_resolution;',
+            'vec2 waveVector = (2.0 * PI * vec2(n, m)) / u_size;',
+
+            'float phase = texture2D(u_phases, vUV).r;',
+            'float deltaPhase = omega(length(waveVector)) * u_deltaTime;',
+            'phase = mod(phase + deltaPhase, 2.0 * PI);',
+        
+            'gl_FragColor = vec4(phase, 0.0, 0.0, 0.0);',
+        '}'
+    ].join('\n')
+};
+THREE.ShaderLib['ocean_spectrum'] = {
+    uniforms: {
+        "u_size": { type: "f", value: null },
+        "u_resolution": { type: "f", value: null },
+        "u_choppiness": { type: "f", value: null },
+        "u_phases": { type: "t", value: null },
+        "u_initialSpectrum": { type: "t", value: null },
+    },
+    varying: {
+        "vUV": { type: "v2" }
+    },
+    fragmentShader: [
+        'precision highp float;',
+
+        'const float PI = 3.14159265359;',
+        'const float G = 9.81;',
+        'const float KM = 370.0;',
+
+        'varying vec2 vUV;',
+
+        'uniform float u_size;',
+        'uniform float u_resolution;',
+        'uniform float u_choppiness;',
+        'uniform sampler2D u_phases;',
+        'uniform sampler2D u_initialSpectrum;',
+
+        'vec2 multiplyComplex (vec2 a, vec2 b) {',
+            'return vec2(a[0] * b[0] - a[1] * b[1], a[1] * b[0] + a[0] * b[1]);',
+        '}',
+
+        'vec2 multiplyByI (vec2 z) {',
+            'return vec2(-z[1], z[0]);',
+        '}',
+
+        'float omega (float k) {',
+            'return sqrt(G * k * (1.0 + k * k / KM * KM));',
+        '}',
+
+        'void main (void) {',
+            'vec2 coordinates = gl_FragCoord.xy - 0.5;',
+            'float n = (coordinates.x < u_resolution * 0.5) ? coordinates.x : coordinates.x - u_resolution;',
+            'float m = (coordinates.y < u_resolution * 0.5) ? coordinates.y : coordinates.y - u_resolution;',
+            'vec2 waveVector = (2.0 * PI * vec2(n, m)) / u_size;',
+
+            'float phase = texture2D(u_phases, vUV).r;',
+            'vec2 phaseVector = vec2(cos(phase), sin(phase));',
+
+            'vec2 h0 = texture2D(u_initialSpectrum, vUV).rg;',
+            'vec2 h0Star = texture2D(u_initialSpectrum, vec2(1.0 - vUV + 1.0 / u_resolution)).rg;',
+            'h0Star.y *= -1.0;',
+
+            'vec2 h = multiplyComplex(h0, phaseVector) + multiplyComplex(h0Star, vec2(phaseVector.x, -phaseVector.y));',
+
+            'vec2 hX = -multiplyByI(h * (waveVector.x / length(waveVector))) * u_choppiness;',
+            'vec2 hZ = -multiplyByI(h * (waveVector.y / length(waveVector))) * u_choppiness;',
+
+            //no DC term
+            'if (waveVector.x == 0.0 && waveVector.y == 0.0) {',
+                'h = vec2(0.0);',
+                'hX = vec2(0.0);',
+                'hZ = vec2(0.0);',
+            '}',
+        
+            'gl_FragColor = vec4(hX + multiplyByI(h), hZ);',
+        '}'
+    ].join('\n')
+};
+THREE.ShaderLib['ocean_normals'] = {
+    uniforms: {
+        "u_displacementMap": { type: "t", value: null },
+        "u_resolution": { type: "f", value: null },
+        "u_size": { type: "f", value: null },
+    },
+    varying: {
+        "vUV": { type: "v2" }
+    },
+    fragmentShader: [
+        'precision highp float;',
+
+        'varying vec2 vUV;',
+        
+        'uniform sampler2D u_displacementMap;',
+        'uniform float u_resolution;',
+        'uniform float u_size;',
+
+        'void main (void) {',
+            'float texel = 1.0 / u_resolution;',
+            'float texelSize = u_size / u_resolution;',
+
+            'vec3 center = texture2D(u_displacementMap, vUV).rgb;',
+            'vec3 right = vec3(texelSize, 0.0, 0.0) + texture2D(u_displacementMap, vUV + vec2(texel, 0.0)).rgb - center;',
+            'vec3 left = vec3(-texelSize, 0.0, 0.0) + texture2D(u_displacementMap, vUV + vec2(-texel, 0.0)).rgb - center;',
+            'vec3 top = vec3(0.0, 0.0, -texelSize) + texture2D(u_displacementMap, vUV + vec2(0.0, -texel)).rgb - center;',
+            'vec3 bottom = vec3(0.0, 0.0, texelSize) + texture2D(u_displacementMap, vUV + vec2(0.0, texel)).rgb - center;',
+
+            'vec3 topRight = cross(right, top);',
+            'vec3 topLeft = cross(top, left);',
+            'vec3 bottomLeft = cross(left, bottom);',
+            'vec3 bottomRight = cross(bottom, right);',
+        
+            'gl_FragColor = vec4(normalize(topRight + topLeft + bottomLeft + bottomRight), 1.0);',
+        '}'
+    ].join('\n')
+};
+THREE.ShaderLib['ocean_main'] = {
+    uniforms: {
+        "u_displacementMap": { type: "t", value: null },
+        "u_normalMap": { type: "t", value: null },
+        "u_geometrySize": { type: "f", value: null },
+        "u_size": { type: "f", value: null },
+        "u_projectionMatrix": { type: "m4", value: null },
+        "u_viewMatrix": { type: "m4", value: null },
+        "u_cameraPosition": { type: "v3", value: null },
+        "u_skyColor": { type: "v3", value: null },
+        "u_oceanColor": { type: "v3", value: null },
+        "u_sunDirection": { type: "v3", value: null },
+        "u_exposure": { type: "f", value: null },
+    },
+    varying: {
+        "vPos": { type: "v3" },
+        "vUV": { type: "v2" }
+    },
+    vertexShader: [
+        'precision highp float;',
+        
+        'varying vec3 vPos;',
+        'varying vec2 vUV;',
+
+        'uniform mat4 u_projectionMatrix;',
+        'uniform mat4 u_viewMatrix;',
+        'uniform float u_size;',
+        'uniform float u_geometrySize;',
+        'uniform sampler2D u_displacementMap;',
+
+        'void main (void) {',
+            'vec3 newPos = position + texture2D(u_displacementMap, uv).rgb * (u_geometrySize / u_size);',
+            'vPos = newPos;',
+            'vUV = uv;',
+            'gl_Position = u_projectionMatrix * u_viewMatrix * vec4(newPos, 1.0);',
+        '}'
+    ].join('\n'),
+    fragmentShader: [
+        'precision highp float;',
+
+        'varying vec3 vPos;',
+        'varying vec2 vUV;',
+
+        'uniform sampler2D u_displacementMap;',
+        'uniform sampler2D u_normalMap;',
+        'uniform vec3 u_cameraPosition;',
+        'uniform vec3 u_oceanColor;',
+        'uniform vec3 u_skyColor;',
+        'uniform vec3 u_sunDirection;',
+        'uniform float u_exposure;',
+
+        'vec3 hdr (vec3 color, float exposure) {',
+            'return 1.0 - exp(-color * exposure);',
+        '}',
+
+        'void main (void) {',
+            'vec3 normal = texture2D(u_normalMap, vUV).rgb;',
+
+            'vec3 view = normalize(u_cameraPosition - vPos);',
+            'float fresnel = 0.02 + 0.98 * pow(1.0 - dot(normal, view), 5.0);',
+            'vec3 sky = fresnel * u_skyColor;',
+
+            'float diffuse = clamp(dot(normal, normalize(u_sunDirection)), 0.0, 1.0);',
+            'vec3 water = (1.0 - fresnel) * u_oceanColor * u_skyColor * diffuse;',
+
+            'vec3 color = sky + water;',
+
+            'gl_FragColor = vec4(hdr(color, u_exposure), 1.0);',
+        '}'
+    ].join('\n')
+};

+ 274 - 0
examples/webgl_shaders_ocean2.html

@@ -0,0 +1,274 @@
+<!DOCTYPE html>
+<html lang="en">	
+    <head>
+        <meta charset="utf-8" />
+        <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"/>        
+	    <style type="text/css">
+	        html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video{margin:0;padding:0;border:0;font:inherit;font-size:100%;vertical-align:baseline}html{line-height:1}ol,ul{list-style:none}table{border-collapse:collapse;border-spacing:0}caption,th,td{text-align:left;font-weight:normal;vertical-align:middle}q,blockquote{quotes:none}q:before,q:after,blockquote:before,blockquote:after{content:"";content:none}a img{border:none}article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section,summary{display:block}body{width:100%;height:100%}body #body_container{width:100%;height:100%;background-color:#000000}
+	    </style>        
+    </head>
+
+
+	<body>
+		<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
+		<script src="../build/three.min.js"></script>		
+		<script src="js/libs/dat.gui.min.js"></script>
+		<script src="js/controls/OrbitControls.js"></script>
+		<script src="js/shaders/ocean_shader_lib.js"></script>
+		<script src="js/shaders/ocean_shader_lib.js"></script>		
+
+		<script>
+			if (!window.requestAnimationFrame) {
+				window.requestAnimationFrame = (function () {
+				    return window.webkitRequestAnimationFrame ||
+					window.mozRequestAnimationFrame || // comment out if FF4 is slow (it caps framerate at ~30fps: https://bugzilla.mozilla.org/show_bug.cgi?id=630127)
+					window.oRequestAnimationFrame ||
+					window.msRequestAnimationFrame ||
+					function ( /* function FrameRequestCallback */ callback, /* DOMElement Element */ element) {
+					    window.setTimeout(callback, 1000 / 60);
+					};
+				})();
+			}
+
+			var lastTime = (new Date()).getTime();
+
+			var WINDOW =
+			{
+			    ms_Width: 0,
+		    	ms_Height: 0,
+		    	ms_Callbacks: 70: "WINDOW.ToggleFullScreen()",
+			    Initialize: function () {
+			        this.UpdateSize();
+
+			        // Create callbacks from keyboard
+			        $(document).keydown(function (inEvent) { WINDOW.CallAction(inEvent.keyCode); });
+			        $(window).resize(function (inEvent) {
+			            WINDOW.UpdateSize();
+			            WINDOW.ResizeCallback(WINDOW.ms_Width, WINDOW.ms_Height);
+			        });
+			    },
+			    UpdateSize: function () {
+			        this.ms_Width = $(window).width();
+			        this.ms_Height = $(window).height() - 4;
+			    },
+			    CallAction: function (inId) {
+			        if (inId in this.ms_Callbacks) {
+			            eval(this.ms_Callbacks[inId]);
+			            return false;
+			        }
+			    },
+			    ToggleFullScreen: function () {
+			        if (!document.fullscreenElement && !document.mozFullScreenElement && !document.webkitFullscreenElement) {
+			            if (document.documentElement.requestFullscreen)
+			                document.documentElement.requestFullscreen();
+			            else if (document.documentElement.mozRequestFullScreen)
+			                document.documentElement.mozRequestFullScreen();
+			            else if (document.documentElement.webkitRequestFullscreen)
+			                document.documentElement.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
+			        }
+			        else {
+			            if (document.cancelFullScreen)
+			                document.cancelFullScreen();
+			            else if (document.mozCancelFullScreen)
+			                document.mozCancelFullScreen();
+			            else if (document.webkitCancelFullScreen)
+			                document.webkitCancelFullScreen();
+			        }
+			    },
+			    ResizeCallback: function (inWidth, inHeight) {},
+			};
+
+			var DEMO =
+			{
+			    ms_Canvas: null,
+			    ms_Renderer: null,
+			    ms_Camera: null,
+			    ms_Scene: null,
+			    ms_Controls: null,
+			    ms_Ocean: null,
+
+			    Enable: (function () {
+			        try {
+			            var aCanvas = document.createElement('canvas');
+			            return !!window.WebGLRenderingContext && (aCanvas.getContext('webgl') || aCanvas.getContext('experimental-webgl'));
+			        }
+			        catch (e) { return false; }
+			    })(),
+
+			    Initialize: function (inIdCanvas) {
+			        
+			        this.ms_Canvas = $('#' + inIdCanvas);
+
+			        // Initialize Renderer, Camera and Scene
+			        this.ms_Renderer =
+			            this.Enable ?
+			                new THREE.WebGLRenderer({ premultipliedAlpha: false, antialias: true })
+			                : new THREE.CanvasRenderer({ premultipliedAlpha: false, antialias: true });
+			        
+			        this.ms_Renderer.shadowMapEnabled = true;
+			        this.ms_Renderer.context.getExtension('OES_texture_float');
+			        this.ms_Renderer.context.getExtension('OES_texture_float_linear');
+			        this.ms_Renderer.autoClear = true;
+			        this.ms_Canvas.html(this.ms_Renderer.domElement);
+			        this.ms_Scene = new THREE.Scene();
+
+			        this.ms_Camera = new THREE.PerspectiveCamera(55.0, WINDOW.ms_Width / WINDOW.ms_Height, 0.5, 300000);
+			        this.ms_Camera.position.set(900, 700, 900);
+			        this.ms_Camera.lookAt(new THREE.Vector3(0,0,0));
+
+			        // Initialize Orbit control		
+			        this.ms_Controls = new THREE.OrbitControls(this.ms_Camera, this.ms_Renderer.domElement);
+			        this.ms_Controls.userPan = false;
+			        this.ms_Controls.userPanSpeed = 0.0;
+			        this.ms_Controls.minDistance = 0;
+			        this.ms_Controls.maxDistance = 2000.0;
+			        this.ms_Controls.minPolarAngle = 0;
+			        this.ms_Controls.maxPolarAngle = Math.PI * 0.495;
+			        
+			        this.debugaxis = function (axisLength) {
+			            //Shorten the vertex function
+			            function v(a, b, c) {
+			                return new THREE.Vector3(a, b, c);
+			            }
+
+			            //Create axis (point1, point2, colour)
+			            function createAxis(p1, p2, color,scene) {
+			                var line, lineGeometry = new THREE.Geometry(),
+			                lineMat = new THREE.LineBasicMaterial({ color: color, lineWidth: 1 });
+			                lineGeometry.vertices.push(p1, p2);
+			                line = new THREE.Line(lineGeometry, lineMat);
+			                scene.add(line);
+			            }
+
+			            createAxis(v(-axisLength, 0, 0), v(axisLength, 0, 0), 0xFF0000, this.ms_Scene);
+			            createAxis(v(0, -axisLength, 0), v(0, axisLength, 0), 0x00FF00, this.ms_Scene);
+			            createAxis(v(0, 0, -axisLength), v(0, 0, axisLength), 0xFFFF00, this.ms_Scene);
+			        };
+
+			        //To use enter the axis length
+			        this.debugaxis(20);
+			        			        
+			        var gsize = 512;
+			        var res = 1024;
+			        var gres = res / 2;
+			        var origx = -gsize / 2;
+			        var origz = -gsize / 2;
+			        this.ms_Ocean = new THREE.Ocean(this.ms_Renderer, this.ms_Camera, this.ms_Scene,
+			        {
+			            INITIAL_SIZE : 1000.0,
+			            INITIAL_WIND : [10.0, 10.0],
+			            INITIAL_CHOPPINESS : 1.5,
+			            CLEAR_COLOR : [1.0, 1.0, 1.0, 0.0],
+			            GEOMETRY_ORIGIN : [origx, origz],
+			            SUN_DIRECTION : [-1.0, 1.0, 1.0],
+			            OCEAN_COLOR: new THREE.Vector3(0.004, 0.016, 0.047),
+			            SKY_COLOR: new THREE.Vector3(3.2, 9.6, 12.8),
+			            EXPOSURE : 0.35,
+			            GEOMETRY_RESOLUTION: gres,
+			            GEOMETRY_SIZE : gsize,
+			            RESOLUTION : res
+			        });
+			        this.ms_Ocean.materialOcean.uniforms.u_projectionMatrix = { type: "m4", value: this.ms_Camera.projectionMatrix };
+			        this.ms_Ocean.materialOcean.uniforms.u_viewMatrix = { type: "m4", value: this.ms_Camera.matrixWorldInverse };
+			        this.ms_Ocean.materialOcean.uniforms.u_cameraPosition = { type: "v3", value: this.ms_Camera.position };
+			        this.ms_Scene.add(this.ms_Ocean.oceanMesh);
+
+			        var gui = new dat.GUI();
+			        var c1 = gui.add(this.ms_Ocean, "size",100, 5000);
+			        c1.onChange(function(v) {
+			            this.object.size = v;
+			            this.object.changed = true;
+			        });
+			        var c2 = gui.add(this.ms_Ocean, "choppiness", 0.1, 4);
+			        c2.onChange(function (v) {
+			            this.object.choppiness = v;
+			            this.object.changed = true;
+			        });
+			        var c3 = gui.add(this.ms_Ocean, "windX",-15, 15);
+			        c3.onChange(function (v) {
+			            this.object.windX = v;
+			            this.object.changed = true;
+			        });
+			        var c4 = gui.add(this.ms_Ocean, "windY", -15, 15);
+			        c4.onChange(function (v) {
+			            this.object.windY = v;
+			            this.object.changed = true;
+			        });
+			        var c5 = gui.add(this.ms_Ocean, "sunDirectionX", -1.0, 1.0);
+			        c5.onChange(function (v) {
+			            this.object.sunDirectionX = v;
+			            this.object.changed = true;
+			        });
+			        var c6 = gui.add(this.ms_Ocean, "sunDirectionY", -1.0, 1.0);
+			        c6.onChange(function (v) {
+			            this.object.sunDirectionY = v;
+			            this.object.changed = true;
+			        });
+			        var c7 = gui.add(this.ms_Ocean, "sunDirectionZ", -1.0, 1.0);
+			        c7.onChange(function (v) {
+			            this.object.sunDirectionZ = v;
+			            this.object.changed = true;
+			        });
+			        var c8 = gui.add(this.ms_Ocean, "exposure", 0.0, 0.5);
+			        c8.onChange(function (v) {
+			            this.object.exposure = v;
+			            this.object.changed = true;
+			        });
+			    },
+			    
+			    Display: function () {
+			        this.ms_Renderer.render(this.ms_Scene, this.ms_Camera);
+			    },
+
+			    Update: function () {
+			        this.ms_Renderer.clear();
+			        var currentTime = new Date().getTime();
+			        this.ms_Ocean.deltaTime = (currentTime - lastTime) / 1000 || 0.0;
+			        lastTime = currentTime;
+			        this.ms_Ocean.render(this.ms_Ocean.deltaTime);
+			        this.ms_Ocean.overrideMaterial = this.ms_Ocean.materialOcean;
+			        if (this.ms_Ocean.changed) {
+			            this.ms_Ocean.materialOcean.uniforms.u_size.value = this.ms_Ocean.size;
+			            this.ms_Ocean.materialOcean.uniforms.u_sunDirection.value = new THREE.Vector3(this.ms_Ocean.sunDirectionX, this.ms_Ocean.sunDirectionY, this.ms_Ocean.sunDirectionZ);
+			            this.ms_Ocean.materialOcean.uniforms.u_exposure.value = this.ms_Ocean.exposure;
+			            this.ms_Ocean.changed = false;
+			        }
+			        this.ms_Ocean.materialOcean.uniforms.u_normalMap.value = this.ms_Ocean.normalMapFramebuffer ;
+			        this.ms_Ocean.materialOcean.uniforms.u_displacementMap.value = this.ms_Ocean.displacementMapFramebuffer ;
+			        this.ms_Ocean.materialOcean.uniforms.u_projectionMatrix.value = this.ms_Camera.projectionMatrix ;
+			        this.ms_Ocean.materialOcean.uniforms.u_viewMatrix.value = this.ms_Camera.matrixWorldInverse ;
+			        this.ms_Ocean.materialOcean.uniforms.u_cameraPosition.value = this.ms_Camera.position;
+			        this.ms_Ocean.materialOcean.depthTest = true;
+			        //this.ms_Scene.__lights[1].position.x = this.ms_Scene.__lights[1].position.x + 0.01;
+			        this.ms_Controls.update();
+			        this.Display();
+			    },
+
+			    Resize: function (inWidth, inHeight) {
+			        this.ms_Camera.aspect = inWidth / inHeight;
+			        this.ms_Camera.updateProjectionMatrix();
+			        this.ms_Renderer.setSize(inWidth, inHeight);
+			        this.ms_Canvas.html(this.ms_Renderer.domElement);
+			        this.Display();
+			    }
+			};
+
+			function MainLoop() {
+			    requestAnimationFrame(MainLoop);
+			    DEMO.Update();
+			};
+
+			$(function () {
+
+			    WINDOW.Initialize();
+			    
+			    DEMO.Initialize('main_canvas');
+
+			    WINDOW.ResizeCallback = function (inWidth, inHeight) { DEMO.Resize(inWidth, inHeight); };
+			    DEMO.Resize(WINDOW.ms_Width, WINDOW.ms_Height);
+
+			    MainLoop();
+			});
+		</script>
+	</body>
+</html>