diff --git a/README.md b/README.md
index 06fcfd4..f9ec215 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,46 @@
+Fully Featured Globe:
+
+
+
+Animated Butterfly Function:
+
+
+
+-------------------------------------------------------------------------------
+CIS565: Project 5: WebGL
+-------------------------------------------------------------------------------
+
+Part1:
+Implemented:
+* Sine and Simplex waves
+* Animated butterfly function.
+ * A description of the butterfly function can be found here: http://mathworld.wolfram.com/ButterflyFunction.html
+ * Video can be found here: http://youtu.be/lUHQKqqMGtc
+
+Part2:
+Implemented:
+* Bump mapped terrain
+* Rim lighting to simulate atmosphere
+* Night-time lights on the dark side of the globe
+* Specular mapping
+* Moving clouds
+Additionally:
+* Procedural water rendering and animation using noise
+ * This was a pretty good attempt but it could definitely be more physically motivated.
+ It leads to some interesting artifacts but also some great looking bits.
+
+Video of my globe can be found here: http://youtu.be/C0MUYax2qEw
+
+Images showing water effect:
+
+
+
+
+
+
+
+
+
-------------------------------------------------------------------------------
CIS565: Project 5: WebGL
-------------------------------------------------------------------------------
diff --git a/part1/butterfly.html b/part1/butterfly.html
new file mode 100644
index 0000000..d98a2f3
--- /dev/null
+++ b/part1/butterfly.html
@@ -0,0 +1,65 @@
+
+
+
+Butterfly
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/part1/simplex.html b/part1/simplex.html
new file mode 100644
index 0000000..80cd63b
--- /dev/null
+++ b/part1/simplex.html
@@ -0,0 +1,100 @@
+
+
+
+Simplex
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/part1/vert_wave.html b/part1/vert_wave.html
index 57107ca..4337eee 100644
--- a/part1/vert_wave.html
+++ b/part1/vert_wave.html
@@ -12,22 +12,39 @@
diff --git a/part1/vert_wave.js b/part1/vert_wave.js
index b90b9cf..b0ec324 100644
--- a/part1/vert_wave.js
+++ b/part1/vert_wave.js
@@ -3,8 +3,8 @@
/*global window,document,Float32Array,Uint16Array,mat4,vec3,snoise*/
/*global getShaderSource,createWebGLContext,createProgram*/
- var NUM_WIDTH_PTS = 32;
- var NUM_HEIGHT_PTS = 32;
+ var NUM_WIDTH_PTS = 200;
+ var NUM_HEIGHT_PTS = 200;
var message = document.getElementById("message");
var canvas = document.getElementById("canvas");
@@ -31,6 +31,9 @@
var positionLocation = 0;
var heightLocation = 1;
var u_modelViewPerspectiveLocation;
+ var u_timeLocation;
+ //var u_color = vec3(0.0, 0.0, 0.0);
+ var u_time = 0.0;
(function initializeShader() {
var program;
@@ -40,6 +43,8 @@
var program = createProgram(context, vs, fs, message);
context.bindAttribLocation(program, positionLocation, "position");
u_modelViewPerspectiveLocation = context.getUniformLocation(program,"u_modelViewPerspective");
+ u_timeLocation = context.getUniformLocation(program, "u_time");
+ //u_color = context.getUniformLocation(program, "u_color");
context.useProgram(program);
})();
@@ -138,11 +143,14 @@
var mvp = mat4.create();
mat4.multiply(persp, mv, mvp);
+ u_time += 0.01;
+
///////////////////////////////////////////////////////////////////////////
// Render
context.clear(context.COLOR_BUFFER_BIT | context.DEPTH_BUFFER_BIT);
-
+
context.uniformMatrix4fv(u_modelViewPerspectiveLocation, false, mvp);
+ context.uniform1f( u_timeLocation, u_time );
context.drawElements(context.LINES, numberOfIndices, context.UNSIGNED_SHORT,0);
window.requestAnimFrame(animate);
diff --git a/part2/frag_globe.html b/part2/frag_globe.html
index 6aa5609..5b47381 100644
--- a/part2/frag_globe.html
+++ b/part2/frag_globe.html
@@ -69,6 +69,8 @@
varying vec3 v_Position; // position in camera coordinates
varying vec3 v_positionMC; // position in model coordinates
+ float genwave( vec2 pos, float time );
+ float simplexNoise(vec2 v);
mat3 eastNorthUpToEyeCoordinates(vec3 positionMC, vec3 normalEC);
void main(void)
@@ -77,21 +79,85 @@
vec3 normal = normalize(v_Normal);
// normalized eye-to-position vector in camera coordinates
vec3 eyeToPosition = normalize(v_Position);
-
- float diffuse = clamp(dot(u_CameraSpaceDirLight, normal), 0.0, 1.0);
+
+ float diff = dot(u_CameraSpaceDirLight, normal);
vec3 toReflectedLight = reflect(-u_CameraSpaceDirLight, normal);
float specular = max(dot(toReflectedLight, -eyeToPosition), 0.0);
- specular = pow(specular, 20.0);
-
+ specular = pow(specular, 40.0);
+
+ // isOcean mask
+ float isOcean = texture2D( u_EarthSpec, v_Texcoord ).r;
+ // Remove specular if we are on land because its not shiny
+ vec3 bnormal;
+ if ( isOcean < 1.0 ) {
+ specular = 0.0;
+
+ // Bump mapping
+ float center = texture2D( u_Bump, v_Texcoord ).r;
+ float right = texture2D( u_Bump, vec2(v_Texcoord.x+1.0/1024.0, v_Texcoord.y)).r;
+ float top = texture2D( u_Bump, vec2(v_Texcoord.x, v_Texcoord.y+1.0/512.0) ).r;
+ bnormal = normalize(vec3(center - right, center - top, 0.1));
+ bnormal = normalize(eastNorthUpToEyeCoordinates( v_positionMC, -eyeToPosition)*bnormal);
+ } else {
+
+ // Ocean waves and such
+ float xfreq =200.0;
+ float yfreq = 400.0;
+ float wave_scl = 0.4;
+ float scl = 0.5;
+
+ float pnoise = 100.0*simplexNoise( vec2( 0.05*u_time, v_Texcoord.y*v_Texcoord.x) );
+
+ float cnoise = scl*simplexNoise( vec2(u_time + v_Texcoord.x*xfreq, v_Texcoord.y*yfreq) );
+ float rnoise = scl*simplexNoise( vec2(u_time + (v_Texcoord.x+1.0/1024.0)*xfreq, v_Texcoord.y*yfreq) );
+ float tnoise = scl*simplexNoise( vec2(u_time + v_Texcoord.x*xfreq, (v_Texcoord.y+1.0/512.0)*yfreq) );
+ // Primary waves
+ /*
+ float center = sin( min(xfreq*v_Texcoord.x + 50.0*u_time + cnoise );
+ float right = sin( xfreq*(v_Texcoord.x + 1.0/1024.0) + 50.0*u_time + rnoise);
+ float top = sin( xfreq*v_Texcoord.x + 50.0*u_time + tnoise);
+ */
+ float tscl = 10.0;
+ float center = cnoise + wave_scl*sin( xfreq*v_Texcoord.y + tscl*(u_time+pnoise) + cnoise );
+ float right = rnoise + wave_scl*sin( xfreq*(v_Texcoord.y + 1.0/1024.0) + tscl*(u_time+pnoise) + rnoise);
+ float top = tnoise + wave_scl*sin( xfreq*v_Texcoord.y + tscl*(u_time+pnoise) + tnoise);
+
+ // Secondary waves
+
+
+ bnormal = normalize(vec3(center - right, center - top, 2.0));
+ bnormal = normalize(eastNorthUpToEyeCoordinates( v_positionMC, -eyeToPosition)*bnormal);
+ }
+
+ float cloudDiffuse = clamp( diff, 0.0, 1.0 );
+ float diffuse = clamp( cloudDiffuse - dot(u_CameraSpaceDirLight, bnormal), 0.0, 1.0);
+
+ vec3 dayColor = texture2D( u_DayDiffuse, v_Texcoord ).rgb;
+ vec3 nightColor = texture2D( u_Night, v_Texcoord ).rgb;
+
+ // Read in cloud data
+ vec2 cloudCoord = vec2(v_Texcoord.x + 25.0*u_time/1024.0, v_Texcoord.y);
+ vec3 cloudColor = texture2D( u_Cloud, cloudCoord ).rgb;
+ vec3 cloudTrans = texture2D( u_CloudTrans, cloudCoord ).rgb;
+
float gammaCorrect = 1.0/1.2; //gamma correct by 1/1.2
-
- vec3 dayColor = texture2D(u_DayDiffuse, v_Texcoord).rgb;
- vec3 nightColor = texture2D(u_Night, v_Texcoord).rgb;
//apply gamma correction to nighttime texture
nightColor = pow(nightColor,vec3(gammaCorrect));
- vec3 color = ((0.6 * diffuse) + (0.4 * specular)) * dayColor;
+ // Mix blending factor
+ float range = 0.6;
+ float alpha = clamp(0.5*(diff + 1.0)/range, 0.0, 1.0);
+ vec3 color = mix( nightColor, ((0.6 * diffuse) + (0.4 * specular)) * dayColor, alpha );
+ // Mix in cloud colors
+ color = mix( cloudDiffuse*cloudColor, color, cloudTrans );
+
+ // Apply Rim Lighting to create an atmosphere effect
+ float rim_factor = dot( v_Normal, v_Position ) + 1.0;
+ // If rim_factor is greater than zero then add some atmospheric blue
+ if ( rim_factor > 0.0 )
+ color += vec3( rim_factor/4.0, rim_factor/2.0, rim_factor/1.0 );
+
gl_FragColor = vec4(color, 1.0);
}
@@ -109,6 +175,46 @@
bitangentEC.x, bitangentEC.y, bitangentEC.z,
normalEC.x, normalEC.y, normalEC.z);
}
+
+ vec3 permute(vec3 x) {
+ x = ((x*34.0)+1.0)*x;
+ return x - floor(x * (1.0 / 289.0)) * 289.0;
+ }
+ // Simple Simplex noise implementation
+ float simplexNoise(vec2 v)
+ {
+ const vec4 C = vec4(0.211324865405187, 0.366025403784439, -0.577350269189626, 0.024390243902439);
+
+ vec2 i = floor(v + dot(v, C.yy) );
+ vec2 x0 = v - i + dot(i, C.xx);
+
+ vec2 i1;
+ i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
+
+ vec4 x12 = x0.xyxy + C.xxzz;
+ x12.xy -= i1;
+
+ i = i - floor(i * (1.0 / 289.0)) * 289.0;
+
+ vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0 ))
+ + i.x + vec3(0.0, i1.x, 1.0 ));
+
+ vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), dot(x12.zw,x12.zw)), 0.0);
+ m = m*m ;
+ m = m*m ;
+
+ vec3 x = 2.0 * fract(p * C.www) - 1.0;
+ vec3 h = abs(x) - 0.5;
+ vec3 ox = floor(x + 0.5);
+ vec3 a0 = x - ox;
+
+ m *= inversesqrt( a0*a0 + h*h );
+
+ vec3 g;
+ g.x = a0.x * x0.x + h.x * x0.y;
+ g.yz = a0.yz * x12.xz + h.yz * x12.yw;
+ return 130.0 * dot(m, g);
+ }
diff --git a/part2/frag_globe.js b/part2/frag_globe.js
index 1d8a877..110cc64 100644
--- a/part2/frag_globe.js
+++ b/part2/frag_globe.js
@@ -17,6 +17,7 @@
var message = document.getElementById("message");
var canvas = document.getElementById("canvas");
var gl = createWebGLContext(canvas, message);
+ //var gl = canvas.getContext("webgl", {antialias: true});
if (!gl) {
return;
}
@@ -55,6 +56,7 @@
var u_EarthSpecLocation;
var u_BumpLocation;
var u_timeLocation;
+ var u_time;
(function initializeShader() {
var vs = getShaderSource(document.getElementById("vs"));
@@ -108,7 +110,8 @@
gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);
gl.vertexAttribPointer(positionLocation, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(positionLocation);
-
+
+
// Normals
var normalsName = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, normalsName);
@@ -250,12 +253,16 @@
mat4.inverse(mv, invTrans);
mat4.transpose(invTrans);
- var lightdir = vec3.create([1.0, 0.0, 1.0]);
+ var lightdir = vec3.create([0.0, 0.0, 1.0]);
var lightdest = vec4.create();
vec3.normalize(lightdir);
mat4.multiplyVec4(view, [lightdir[0], lightdir[1], lightdir[2], 0.0], lightdest);
lightdir = vec3.createFrom(lightdest[0],lightdest[1],lightdest[2]);
vec3.normalize(lightdir);
+
+
+ // Time
+ gl.uniform1f( u_timeLocation, time );
///////////////////////////////////////////////////////////////////////////
// Render
@@ -288,6 +295,7 @@
gl.uniform1i(u_EarthSpecLocation, 5);
gl.drawElements(gl.TRIANGLES, numberOfIndices, gl.UNSIGNED_SHORT,0);
+ // Update time
time += 0.001;
window.requestAnimFrame(animate);
}
diff --git a/part2/index.html b/part2/index.html
new file mode 100644
index 0000000..2aea6fc
--- /dev/null
+++ b/part2/index.html
@@ -0,0 +1,245 @@
+
+
+
+Fragment Globe
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/part2/noise3D.js b/part2/noise3D.js
new file mode 100644
index 0000000..d519f47
--- /dev/null
+++ b/part2/noise3D.js
@@ -0,0 +1,316 @@
+(function(exports) {
+ "use strict";
+ /*global window,vec3*/
+
+ exports = exports || window;
+
+ function step(edge, x) {
+ return [
+ (x[0] < edge[0]) ? 0.0 : 1.0,
+ (x[1] < edge[1]) ? 0.0 : 1.0,
+ (x[2] < edge[2]) ? 0.0 : 1.0
+ ];
+ }
+
+ function step_vec4(edge, x) {
+ return [
+ (x[0] < edge[0]) ? 0.0 : 1.0,
+ (x[1] < edge[1]) ? 0.0 : 1.0,
+ (x[2] < edge[2]) ? 0.0 : 1.0,
+ (x[3] < edge[3]) ? 0.0 : 1.0
+ ];
+ }
+
+ function min(x, y) {
+ return [
+ y[0] < x[0] ? y[0] : x[0],
+ y[1] < x[1] ? y[1] : x[1],
+ y[2] < x[2] ? y[2] : x[2]
+ ];
+ }
+
+ function max(x, y) {
+ return [
+ y[0] > x[0] ? y[0] : x[0],
+ y[1] > x[1] ? y[1] : x[1],
+ y[2] > x[2] ? y[2] : x[2]
+ ];
+ }
+
+ function max_vec4(x, y) {
+ return [
+ y[0] > x[0] ? y[0] : x[0],
+ y[1] > x[1] ? y[1] : x[1],
+ y[2] > x[2] ? y[2] : x[2],
+ y[3] > x[3] ? y[3] : x[3]
+ ];
+ }
+
+ function vec4_dot(left, right) {
+ return left[0] * right[0] +
+ left[1] * right[1] +
+ left[2] * right[2] +
+ left[3] * right[3];
+ }
+
+ //
+ // Description : Array and textureless GLSL 2D/3D/4D simplex
+ // noise functions.
+ // Author : Ian McEwan, Ashima Arts.
+ // Maintainer : ijm
+ // Lastmod : 20110822 (ijm)
+ // License : Copyright (C) 2011 Ashima Arts. All rights reserved.
+ // Distributed under the MIT License. See LICENSE file.
+ // https://github.com/ashima/webgl-noise
+ //
+ function mod289_vec3(x) {
+ var temp = (1.0 / 289.0);
+ return [
+ x[0] - Math.floor(x[0] * temp) * 289.0,
+ x[1] - Math.floor(x[1] * temp) * 289.0,
+ x[2] - Math.floor(x[2] * temp) * 289.0
+ ];
+ }
+
+ function mod289_vec4(x) {
+ var temp = (1.0 / 289.0);
+ return [
+ x[0] - Math.floor(x[0] * temp) * 289.0,
+ x[1] - Math.floor(x[1] * temp) * 289.0,
+ x[2] - Math.floor(x[2] * temp) * 289.0,
+ x[3] - Math.floor(x[3] * temp) * 289.0
+ ];
+ }
+
+ function permute_vec4(x) {
+ return mod289_vec4([
+ ((x[0]*34.0)+1.0)*x[0],
+ ((x[1]*34.0)+1.0)*x[1],
+ ((x[2]*34.0)+1.0)*x[2],
+ ((x[3]*34.0)+1.0)*x[3]
+ ]);
+ }
+
+ function taylorInvSqrt_vec4(r) {
+ return [
+ 1.79284291400159 - 0.85373472095314 * r[0],
+ 1.79284291400159 - 0.85373472095314 * r[1],
+ 1.79284291400159 - 0.85373472095314 * r[2],
+ 1.79284291400159 - 0.85373472095314 * r[3]
+ ];
+ }
+
+ exports.snoise = function(v) {
+ // const vec2 C = vec2(1.0f/6.0f, 1.0f/3.0f) ;
+ // const vec4 D = vec4(0.0f, 0.5f, 1.0f, 2.0f);
+ var C = [1.0/6.0, 1.0/3.0];
+ var D = [0.0, 0.5, 1.0, 2.0];
+
+ // vec3 i = floor(v + dot(v, vec3(C.y, C.y, C.y)) );
+ // vec3 x0 = v - i + dot(i, vec3(C.x, C.x, C.x) );
+ var temp0 = vec3.create();
+ var temp3 = vec3.dot(v, [C[1], C[1], C[1]]);
+ vec3.add(v, [temp3, temp3, temp3], temp0);
+ var i = [Math.floor(temp0[0]), Math.floor(temp0[1]), Math.floor(temp0[2])];
+ var temp1 = vec3.create();
+ vec3.subtract(v, i, temp1);
+ var temp2 = vec3.dot(i, [C[0], C[0], C[0]]);
+ var x0 = vec3.create();
+ vec3.add(temp1, [temp2, temp2, temp2], x0);
+
+ // vec3 g = step(vec3(x0.y, x0.z, x0.x), vec3(x0.x, x0.y, x0.z));
+ // vec3 l = 1.0f - g;
+ // vec3 i1 = min( vec3(g.x, g.y, g.z), vec3(l.z, l.x, l.y) );
+ // vec3 i2 = max( vec3(g.x, g.y, g.z), vec3(l.z, l.x, l.y) );
+ var g = step([x0[1], x0[2], x0[0]], [x0[0], x0[1], x0[2]]);
+ var l = [1.0 - g[0], 1.0 - g[1], 1.0 - g[2]];
+ var i1 = min([g[0], g[1], g[2]], [l[2], l[0], l[1]]);
+ var i2 = max([g[0], g[1], g[2]], [l[2], l[0], l[1]]);
+
+ // vec3 x1 = x0 - i1 + vec3(C.x, C.x, C.x);
+ // vec3 x2 = x0 - i2 + vec3(C.y, C.y, C.y); // 2.0*C.x = 1/3 = C.y
+ // vec3 x3 = x0 - vec3(D.y, D.y, D.y); // -1.0+3.0*C.x = -0.5 = -D.y
+ var temp4 = vec3.create();
+ vec3.subtract(x0, i1, temp4);
+ var x1 = vec3.create();
+ vec3.add(temp4, [C[0], C[0], C[0]], x1);
+ var temp5 = vec3.create();
+ vec3.subtract(x0, i2, temp5);
+ var x2 = vec3.create();
+ vec3.add(temp5, [C[1], C[1], C[1]], x2);
+ var x3 = vec3.create();
+ vec3.subtract(x0, [D[1], D[1], D[1]], x3);
+
+ // i = mod289(i);
+ // vec4 p = permute( permute( permute(
+ // i.z + vec4(0.0, i1.z, i2.z, 1.0 ))
+ // + i.y + vec4(0.0, i1.y, i2.y, 1.0 ))
+ // + i.x + vec4(0.0, i1.x, i2.x, 1.0 ));
+ i = mod289_vec3(i);
+ var p = permute_vec4([i[2] + 0.0, i[2] + i1[2], i[2] + i2[2], i[2] + 1.0]);
+ p[0] += i[1] + 0.0;
+ p[1] += i[1] + i1[1];
+ p[2] += i[1] + i2[1];
+ p[3] += i[1] + 1.0;
+ p = permute_vec4(p);
+ p[0] += i[0] + 0.0;
+ p[1] += i[0] + i1[0];
+ p[2] += i[0] + i2[0];
+ p[3] += i[0] + 1.0;
+ p = permute_vec4(p);
+
+ // float n_ = 0.142857142857f; // 1.0/7.0
+ // vec3 ns = n_ * vec3(D.w, D.y, D.z) - vec3(D.x, D.z, D.x);
+// var n_ = 0.142857142857; // 1.0/7.0
+// var ns = [
+// n_ * D[3] - D[0],
+// n_ * D[1] - D[2],
+// n_ * D[2] - D[0]
+// ];
+ var ns = [
+ 0.28571430,
+ -0.92857140,
+ 0.14285715
+ ];
+
+ // vec4 j = p - 49.0f * floor(p * ns.z * ns.z); // mod(p,7*7)
+ var j = [
+ p[0] - 49.0 * Math.floor(p[0] * ns[2] * ns[2]),
+ p[1] - 49.0 * Math.floor(p[1] * ns[2] * ns[2]),
+ p[2] - 49.0 * Math.floor(p[2] * ns[2] * ns[2]),
+ p[3] - 49.0 * Math.floor(p[3] * ns[2] * ns[2])
+ ];
+
+ // vec4 x_ = floor(j * ns.z);
+ // vec4 y_ = floor(j - 7.0f * x_ ); // mod(j,N)
+ var x_ = [
+ Math.floor(j[0] * ns[2]),
+ Math.floor(j[1] * ns[2]),
+ Math.floor(j[2] * ns[2]),
+ Math.floor(j[3] * ns[2])
+ ];
+ var y_ = [
+ Math.floor(j[0] - 7.0 * x_[0] ),
+ Math.floor(j[1] - 7.0 * x_[1] ),
+ Math.floor(j[2] - 7.0 * x_[2] ),
+ Math.floor(j[3] - 7.0 * x_[3] )
+ ];
+
+ // vec4 x = x_ *ns.x + vec4(ns.y, ns.y, ns.y, ns.y);
+ // vec4 y = y_ *ns.x + vec4(ns.y, ns.y, ns.y, ns.y);
+ // vec4 h = 1.0f - abs(x) - abs(y);
+ var x = [
+ x_[0] *ns[0] + ns[1],
+ x_[1] *ns[0] + ns[1],
+ x_[2] *ns[0] + ns[1],
+ x_[3] *ns[0] + ns[1]
+ ];
+ var y = [
+ y_[0] *ns[0] + ns[1],
+ y_[1] *ns[0] + ns[1],
+ y_[2] *ns[0] + ns[1],
+ y_[3] *ns[0] + ns[1]
+ ];
+ var h = [
+ 1.0 - Math.abs(x[0]) - Math.abs(y[0]),
+ 1.0 - Math.abs(x[1]) - Math.abs(y[1]),
+ 1.0 - Math.abs(x[2]) - Math.abs(y[2]),
+ 1.0 - Math.abs(x[3]) - Math.abs(y[3])
+ ];
+
+ // vec4 b0 = vec4( vec2(x.x, x.y), vec2(y.x, y.y) );
+ // vec4 b1 = vec4( vec2(x.z, x.w), vec2(y.z, y.w) );
+ var b0 = [x[0], x[1], y[0], y[1]];
+ var b1 = [x[2], x[3], y[2], y[3]];
+
+ // vec4 s0 = floor(b0)*2.0f + 1.0f;
+ // vec4 s1 = floor(b1)*2.0f + 1.0f;
+ // vec4 sh = -step(h, vec4(0.0));
+
+ var s0 = [
+ Math.floor(b0[0])*2.0 + 1.0,
+ Math.floor(b0[1])*2.0 + 1.0,
+ Math.floor(b0[2])*2.0 + 1.0,
+ Math.floor(b0[3])*2.0 + 1.0
+ ];
+ var s1 = [
+ Math.floor(b1[0])*2.0 + 1.0,
+ Math.floor(b1[1])*2.0 + 1.0,
+ Math.floor(b1[2])*2.0 + 1.0,
+ Math.floor(b1[3])*2.0 + 1.0
+ ];
+ var sh = step_vec4(h, [0.0, 0.0, 0.0, 0.0]);
+ sh[0] = -sh[0];
+ sh[1] = -sh[1];
+ sh[2] = -sh[2];
+ sh[3] = -sh[3];
+
+ // vec4 a0 = vec4(b0.x, b0.z, b0.y, b0.w) + vec4(s0.x, s0.z, s0.y, s0.w) * vec4(sh.x, sh.x, sh.y, sh.y) ;
+ // vec4 a1 = vec4(b1.x, b1.z, b1.y, b1.w) + vec4(s1.x, s1.z, s1.y, s1.w) * vec4(sh.z, sh.z, sh.w, sh.w) ;
+ var a0 = [
+ b0[0] + s0[0] * sh[0],
+ b0[2] + s0[2] * sh[0],
+ b0[1] + s0[1] * sh[1],
+ b0[3] + s0[3] * sh[1]
+ ];
+ var a1 = [
+ b1[0] + s1[0] * sh[2],
+ b1[2] + s1[2] * sh[2],
+ b1[1] + s1[1] * sh[3],
+ b1[3] + s1[3] * sh[3]
+ ];
+
+ // vec3 p0 = vec3(a0.x, a0.y, h.x);
+ // vec3 p1 = vec3(a0.z, a0.w, h.y);
+ // vec3 p2 = vec3(a1.x, a1.y, h.z);
+ // vec3 p3 = vec3(a1.z, a1.w, h.w);
+ var p0 = [a0[0], a0[1], h[0]];
+ var p1 = [a0[2], a0[3], h[1]];
+ var p2 = [a1[0], a1[1], h[2]];
+ var p3 = [a1[2], a1[3], h[3]];
+
+ // vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3)));
+ // p0 *= norm.x;
+ // p1 *= norm.y;
+ // p2 *= norm.z;
+ // p3 *= norm.w;
+ var norm = taylorInvSqrt_vec4([vec3.dot(p0,p0), vec3.dot(p1,p1), vec3.dot(p2, p2), vec3.dot(p3,p3)]);
+ p0 = [p0[0]*norm[0], p0[1]*norm[0], p0[2]*norm[0]];
+ p1 = [p1[0]*norm[1], p1[1]*norm[1], p1[2]*norm[1]];
+ p2 = [p2[0]*norm[2], p2[1]*norm[2], p2[2]*norm[2]];
+ p3 = [p3[0]*norm[3], p3[1]*norm[3], p3[2]*norm[3]];
+
+ // vec4 m = max(0.6f - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);
+ // m = m * m;
+ var m = max_vec4([
+ 0.6 - vec3.dot(x0,x0),
+ 0.6 - vec3.dot(x1,x1),
+ 0.6 - vec3.dot(x2,x2),
+ 0.6 - vec3.dot(x3,x3)
+ ], [
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0
+ ]);
+ m[0] *= m[0];
+ m[1] *= m[1];
+ m[2] *= m[2];
+ m[3] *= m[3];
+
+ // return 42.0f * dot( m*m, vec4( dot(p0,x0), dot(p1,x1),
+ // dot(p2,x2), dot(p3,x3) ) );
+ return 42.0 * vec4_dot([
+ m[0] * m[0],
+ m[1] * m[1],
+ m[2] * m[2],
+ m[3] * m[3]
+ ], [
+ vec3.dot(p0,x0),
+ vec3.dot(p1,x1),
+ vec3.dot(p2,x2),
+ vec3.dot(p3,x3)
+ ]);
+ };
+
+}());
diff --git a/resources/butterfly0.png b/resources/butterfly0.png
new file mode 100644
index 0000000..01818e6
Binary files /dev/null and b/resources/butterfly0.png differ
diff --git a/resources/butterfly1.png b/resources/butterfly1.png
new file mode 100644
index 0000000..dbfcd98
Binary files /dev/null and b/resources/butterfly1.png differ
diff --git a/resources/fully_featured_globe.png b/resources/fully_featured_globe.png
new file mode 100644
index 0000000..1d0dca1
Binary files /dev/null and b/resources/fully_featured_globe.png differ
diff --git a/resources/globe_best_water.png b/resources/globe_best_water.png
new file mode 100644
index 0000000..0dca5d4
Binary files /dev/null and b/resources/globe_best_water.png differ
diff --git a/resources/water0.png b/resources/water0.png
new file mode 100644
index 0000000..ebc47b7
Binary files /dev/null and b/resources/water0.png differ
diff --git a/resources/water1.png b/resources/water1.png
new file mode 100644
index 0000000..c5653de
Binary files /dev/null and b/resources/water1.png differ
diff --git a/resources/water2.png b/resources/water2.png
new file mode 100644
index 0000000..a48afdf
Binary files /dev/null and b/resources/water2.png differ