DIR : /home/kozerus/public_html/jkwii/js/jsdoit/water/index.js
/home/kozerus/public_html/jkwii/js/jsdoit/water
function initGL(canvas) {
try {
gl = canvas.getContext("experimental-webgl");
gl.viewportWidth = canvas.width;
gl.viewportHeight = canvas.height;
} catch (e) {
}
if (!gl) {
showMessage(err_img_url, 'WebGL is not supported.');
}
}
function showMessage( img_url, msg )
{
document.write('\
<style>body { background-color: #DDDDDD; overflow: hidden; padding: 0px; margin: 0px; }</style>\
<div id="alt">\
<img id="captureImg" src="'+img_url+'"/>\
<div style="position: absolute; top: 350px; left: 0px; margin: 15px; padding: 15px;line-height: 15px;background:#eee;opacity:0.9;\
filter:alpha(opacity=90);">\
<h4 style="margin: 0px; font: 22px Arial;">'+msg+'</h4>\
<a style=" font: 10px Arial;" target="_blank" href="http://learningwebgl.com/cookbook/index.php/WebGL:_Frequently_Asked_Questions">\
<font size="-1">how to use WebGL</font></a>\
</div>\
</div>\
');
}
function getShader(gl, id) {
var shaderScript = document.getElementById(id);
if (!shaderScript) {
return null;
}
var str = "";
var k = shaderScript.firstChild;
while (k) {
if (k.nodeType == 3) {
str += k.textContent;
}
k = k.nextSibling;
}
var shader;
if (shaderScript.type == "x-shader/x-fragment") {
shader = gl.createShader(gl.FRAGMENT_SHADER);
} else if (shaderScript.type == "x-shader/x-vertex") {
shader = gl.createShader(gl.VERTEX_SHADER);
} else {
return null;
}
gl.shaderSource(shader, str);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
alert(gl.getShaderInfoLog(shader));
return null;
}
return shader;
}
function initShaders() {
var fragmentShader = getShader(gl, "shader-fs");
var vertexShader = getShader(gl, "shader-vs");
shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
showMessage(err_img_url, 'Fail to compile.');
alert("Could not initialise shaders");
}
gl.useProgram(shaderProgram);
shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");
gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);
shaderProgram.texture0CoordAttribute = gl.getAttribLocation(shaderProgram, "aTexture0Coord");
gl.enableVertexAttribArray(shaderProgram.texture0CoordAttribute);
shaderProgram.texture1CoordAttribute = gl.getAttribLocation(shaderProgram, "aTexture1Coord");
gl.enableVertexAttribArray(shaderProgram.texture1CoordAttribute);
shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, "uPMatrix");
shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix");
shaderProgram.texture0Uniform = gl.getUniformLocation(shaderProgram, "texture0");
shaderProgram.texture1Uniform = gl.getUniformLocation(shaderProgram, "texture1");
}
function handleLocalTexture(texture, img)
{
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.bindTexture(gl.TEXTURE_2D, null);
}
function initTexture() {
refractionTexture = gl.createTexture();
var img0 = document.getElementById('texture0');
handleLocalTexture(refractionTexture, img0);
reflectionTexture = gl.createTexture();
var img1 = document.getElementById('texture1');
handleLocalTexture(reflectionTexture, img1);
}
function initWaterBuffers()
{
// create vertice buffer
waterVertexPositionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, waterVertexPositionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
waterVertexPositionBuffer.itemSize = 3;
waterVertexPositionBuffer.numItems = vertices.length / 3;
// create uvt buffer
waterVertexTexture0CoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, waterVertexTexture0CoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(uvt), gl.STATIC_DRAW);
waterVertexTexture0CoordBuffer.itemSize = 2;
waterVertexTexture0CoordBuffer.numItems = vertices.length / 3;
// create uvt2 buffer
waterVertexTexture1CoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, waterVertexTexture1CoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(uvt2), gl.STATIC_DRAW);
waterVertexTexture1CoordBuffer.itemSize = 2;
waterVertexTexture1CoordBuffer.numItems = vertices.length / 3;
// create indices buffer
waterVertexIndexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, waterVertexIndexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
waterVertexIndexBuffer.itemSize = 1;
waterVertexIndexBuffer.numItems = indices.length;
}
function mvPushMatrix() {
var copy = mat4.create();
mat4.set(mvMatrix, copy);
mvMatrixStack.push(copy);
}
function mvPopMatrix() {
if (mvMatrixStack.length == 0) {
throw "Invalid popMatrix!";
}
mvMatrix = mvMatrixStack.pop();
}
function setMatrixUniforms() {
gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, false, pMatrix);
gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mvMatrix);
}
function degToRad(degrees) {
return degrees * Math.PI / 180;
}
function animate() {
var timeNow = new Date().getTime();
if (lastTime != 0) {
var elapsed = timeNow - lastTime;
xRot += (90 * elapsed) / 1000.0;
yRot += (90 * elapsed) / 1000.0;
zRot += (90 * elapsed) / 1000.0;
}
lastTime = timeNow;
}
function drawWater()
{
gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
mat4.perspective(45, gl.viewportWidth / gl.viewportHeight, 0.1, 500.0, pMatrix);
mat4.rotate(pMatrix, degToRad(180), [1, 0, 0]);
mat4.identity(mvMatrix);
mat4.translate(mvMatrix, [0.0, 0.0, 150.0]);
mat4.rotate(mvMatrix, degToRad(-viewedAngleV*57.29577951308232), [1, 0, 0]);
mat4.rotate(mvMatrix, degToRad(-viewedAngleH*57.29577951308232), [0, 1, 0]);
// bind position buffer
gl.bindBuffer(gl.ARRAY_BUFFER, waterVertexPositionBuffer);
gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, waterVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
// bind texture0 buffer
gl.bindBuffer(gl.ARRAY_BUFFER, waterVertexTexture0CoordBuffer);
gl.vertexAttribPointer(shaderProgram.texture0CoordAttribute, waterVertexTexture0CoordBuffer.itemSize, gl.FLOAT, false, 0, 0);
// upload texture0 buffer
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, refractionTexture);
gl.uniform1i(shaderProgram.texture0Uniform, 0);
// bind texture1 buffer
gl.bindBuffer(gl.ARRAY_BUFFER, waterVertexTexture1CoordBuffer);
gl.vertexAttribPointer(shaderProgram.texture1CoordAttribute, waterVertexTexture1CoordBuffer.itemSize, gl.FLOAT, false, 0, 0);
// upload texture1 buffer
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, reflectionTexture);
gl.uniform1i(shaderProgram.texture1Uniform, 1);
// reflectionRatio
gl.uniform1f(gl.getUniformLocation(shaderProgram, "reflectionRatio"), reflectionRatio); // <- uniform1f, "f" for float
// bind indice buffer
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, waterVertexIndexBuffer);
setMatrixUniforms();
gl.drawElements(gl.TRIANGLES, waterVertexIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0);
// textfield.value = "updating ..."+lastTime;
}
function tick() {
requestAnimFrame(tick);
//drawScene();
move();
setMesh();
initWaterBuffers();
drawWater();
animate();
}
function webGLStart() {
var canvas = document.getElementById("canvasView");
initGL(canvas);
initShaders();
initWater();
initTexture();
gl.clearColor(0.2, 0.2, 0.2, 1.0);
gl.enable(gl.DEPTH_TEST);
tick();
// exxxxxxxxxxxtra
var win = document.getElementById("window");
refractionSlider = new mc.HSlider(win, 30, 20, onRefractionChanged).bindLabel(new mc.Label(win, 145, 20), 0);
reflectionSlider = new mc.HSlider(win, 30, 40, onReflectionChanged).bindLabel(new mc.Label(win, 145, 40), 0);
refractionSlider.setSliderParams(1, 3, 1.4);
reflectionSlider.setSliderParams(0, 1, 0.4);
win.onmousemove = onMouseMove;
}
function onRefractionChanged(e)
{
refractiveIndex = refractionSlider.getValue();
}
function onReflectionChanged(e)
{
reflectionRatio = reflectionSlider.getValue();
}
function onMouseMove(e)
{
var point = whereAt(e);
mouseX = point[0];
mouseY = point[1];
ripple(-(500/2 - mouseY)/ 500 * MESH_SIZE, (mouseX - 500/2)/ 500 * MESH_SIZE, 3);
}
// from - http://stackoverflow.com/questions/3011418/onmousemove-get-mouse-position
var whereAt = (function(){
if(window.pageXOffset!= undefined){
return function(ev){
return [ev.clientX+window.pageXOffset,
ev.clientY+window.pageYOffset];
}
}
else return function(){
var ev= window.event,
d= document.documentElement, b= document.body;
return [ev.clientX+d.scrollLeft+ b.scrollLeft,
ev.clientY+d.scrollTop+ b.scrollTop];
}
})();
// constants
var NUM_DETAILS = 64; // 48;
var INV_NUM_DETAILS = 1 / NUM_DETAILS;
var MESH_SIZE = 100; // 100;
var SURFACE_DETAILS = NUM_DETAILS-4;
var VCOUNT = SURFACE_DETAILS * SURFACE_DETAILS;
var WIDTH = 465;
var HEIGHT = 465;
//
var err_img_url = 'http://dl.dropbox.com/u/56320860/water.png';
// variables
var mouseX = 0;
var mouseY = 0;
var viewedAngleH = 0, rotH = 0;
var viewedAngleV = -20 * 0.017453292519943295, rotV = 0;
var cameraDistance = MESH_SIZE;
var focalLength = MESH_SIZE * 4;
var boxHeight = MESH_SIZE*0.75;
var refractiveIndex = 1.4;
var reflectionRatio = 0.4;
var cameraPosition = {x: 0, y: 0, z:0};
var cameraPos = [0, 0, 0];
// GL
var gl;
var shaderProgram;
// data
var heights = [];
var velocity = [];
var indices = [];
var uvt = [], uvt2 = [];
// buffers
var waterVertexPositionBuffer;
var waterVertexTexture0CoordBuffer;
var waterVertexTexture1CoordBuffer;
var waterVertexIndexBuffer;
var cubeVertexPositionBuffer;
var cubeVertexTextureCoordBuffer;
var cubeVertexIndexBuffer;
var refractionTexture;
var reflectionTexture;
// matrix
var mvMatrix = mat4.create();
var mvMatrixStack = [];
var pMatrix = mat4.create();
var _invmat = mat4.create();
var mat = mat4.create();
// minimalcomps
var refractionSlider;
var reflectionSlider;
var lastTime = 0;
function initWater()
{
var i, j, idx;
vertices = []; // new Vector.<Number>(VCOUNT * 7, true);
transformedVertices = []; // new Vector.<Number>(VCOUNT * 2, true);
uvt = []; // new Vector.<Number>(VCOUNT * 2, true);
uvt2 = []; // new Vector.<Number>(VCOUNT * 2, true);
for (i = 0; i < VCOUNT; i++) {
/*
vertices[i] = ((Math.floor(i/SURFACE_DETAILS)+2) * INV_NUM_DETAILS - 0.5) * MESH_SIZE;
vertices[i+1] = ((Math.floor(i%SURFACE_DETAILS)+2) * INV_NUM_DETAILS - 0.5) * MESH_SIZE;
vertices[i+2] = 0;
//*/
vertices.push(((Math.floor(i/SURFACE_DETAILS)+2) * INV_NUM_DETAILS - 0.5) * MESH_SIZE, ((Math.floor(i%SURFACE_DETAILS)+2) * INV_NUM_DETAILS - 0.5) * MESH_SIZE, 0);
uvt.push(0,0);
uvt2.push(0,0);
}
indices = []; // new Vector.<int>();
for (i=1; i<SURFACE_DETAILS; i++) for (j=1; j<SURFACE_DETAILS; j++) {
idx = j * SURFACE_DETAILS + i;
indices.push(idx-SURFACE_DETAILS-1, idx-SURFACE_DETAILS, idx,
idx-SURFACE_DETAILS-1, idx, idx-1);
}
// create field
heights = []; // new Vector.<Number>(NUM_DETAILS * NUM_DETAILS, true);
velocity = []; // new Vector.<Number>(NUM_DETAILS * NUM_DETAILS, true);
for (i = 0; i < NUM_DETAILS * NUM_DETAILS; i++) //velocity[i] = heights[i] = 0;
{
velocity.push(0);
heights.push(0);
}
}
function ripple(mx, my, intensity) {
var i, j, idx, dx, dy, acc, imin, jmin, imax, jmax;
mx = (mx / MESH_SIZE + 0.5) * NUM_DETAILS;
my = (my / MESH_SIZE + 0.5) * NUM_DETAILS;
imin = (mx > 5) ? Math.floor(mx - 3) : 2;
jmin = (my > 5) ? Math.floor(my - 3) : 2;
imax = (mx < NUM_DETAILS-5) ? Math.floor(mx + 4) : (NUM_DETAILS - 1);
jmax = (my < NUM_DETAILS-5) ? Math.floor(my + 4) : (NUM_DETAILS - 1);
for (i=imin; i<imax; i++) for (j=jmin; j<jmax; j++) {
dx = mx - i;
dy = my - j;
acc = 3 - Math.sqrt(dx * dx + dy * dy);
if (acc > 0) velocity[j*NUM_DETAILS+i] += acc*intensity;
}
}
function setMesh()
{
var i, j, index, index2, len, u, v,
t, s, r, hitz, sign,
vx, vy, vz,
nx, ny, nz,
dx, dy, dz,
rimo = refractiveIndex - 1,
xymax = MESH_SIZE * 0.45, //MESH_SIZE * 0.5,
ixymax = 1 / xymax;
for (i = 0; i < SURFACE_DETAILS; i++) {
for (j = 0; j < SURFACE_DETAILS; j++) {
index = (j * SURFACE_DETAILS + i) * 3;// * 7;
index2 = (j * SURFACE_DETAILS + i) * 2;
len = heights[(j+2)*NUM_DETAILS+i+2];
/*
vx = vertices[index]; index++;
vy = vertices[index]; index++;
vz = vertices[index] = len * 0.25; index++;
//*/
vx = vertices[index ];
vy = vertices[index+1];
vz = vertices[index+2] = len * 0.15;//0.25;
//*/
// ---Sphere map---
nx = (len - heights[(j+2)*NUM_DETAILS+i+1]) * 0.25;
ny = (len - heights[(j+1)*NUM_DETAILS+i+2]) * 0.25;
nz = 1 / Math.sqrt(nx * nx + ny * ny + 1);
nx *= nz;
ny *= nz;
// [modification] Refraction map
// incident vector (you can calculate them in the setup if you want faster)
dx = vx - cameraPosition.x;
dy = vy - cameraPosition.y;
dz = vz - cameraPosition.z;
len = 1 / Math.sqrt(dx * dx + dy * dy + dz * dz);
dx *= len;
dy *= len;
dz *= len;
// output vector
t = (dx * nx + dy * ny + dz) * rimo;
dx += nx * t;
dy += ny * t;
dz += nz * t;
// in this calculation, we can omit normalization of output vector !
//len = 1 / Math.sqrt(dx * dx + dy * dy + dz * dz);
//dx *= len;
//dy *= len;
//dz *= len;
// uv coordinate
if (dx == 0) {
if (dy == 0) {
u = v = 0.5;
sign = 0;
} else sign = (dy < 0) ? -1 : 1;
} else {
sign = (dx < 0) ? -1 : 1;
t = (sign * xymax - vx) / dx;
s = t * dy + vy;
if (-xymax < s && s < xymax) {
hitz = t * dz + vz;
if (hitz > boxHeight) {
r = (boxHeight-vz) / dz;
u = (dx * r + vx) * ixymax * 0.25 + 0.5;
v = (dy * r + vy) * ixymax * 0.25 + 0.5;
} else {
r = boxHeight / (hitz + boxHeight);
u = sign * r * 0.5 + 0.5;
v = s * ixymax * r * 0.5 + 0.5;
}
sign = 0;
} else sign = (dy < 0) ? -1 : 1;
}
if (sign != 0) {
t = (sign * xymax - vy) / dy;
s = t * dx + vx;
hitz = t * dz + vz;
if (hitz > boxHeight) {
r = (boxHeight-vz) / dz;
u = (dx * r + vx) * ixymax * 0.25 + 0.5;
v = (dy * r + vy) * ixymax * 0.25 + 0.5;
} else {
r = boxHeight / (hitz + boxHeight);
u = s * ixymax * r * 0.5 + 0.5;
v = sign * r * 0.5 + 0.5;
}
}
/*
vertices[index] = nx * 0.5 + 0.5 + ((i - NUM_DETAILS * 0.5) * INV_NUM_DETAILS * 0.25); index++;
vertices[index] = ny * 0.5 + 0.5 + ((NUM_DETAILS * 0.5 - j) * INV_NUM_DETAILS * 0.25); index++;
vertices[index] = u; index++;
vertices[index] = v;
//*/
uvt[index2 ] = nx * 0.5 + 0.5 + ((i - NUM_DETAILS * 0.5) * INV_NUM_DETAILS * 0.25); // index++;
uvt[index2+1] = ny * 0.5 + 0.5 + ((NUM_DETAILS * 0.5 - j) * INV_NUM_DETAILS * 0.25); // index++;
uvt2[index2 ] = u; // index++;
uvt2[index2+1] = v;
//*/
}
}
}
function move()
{
// ---Water simulation---
var i, j, idx, v, imax, jmax;
imax = jmax = NUM_DETAILS - 1;
for (i=1; i<imax; i++) for (j=1; j<jmax; j++) {
idx = j * NUM_DETAILS + i;
heights[idx] += velocity[idx];
if (heights[idx] > 100) heights[idx] = 100;
else if (heights[idx] < -100) heights[idx] = -100;
}
for (i=1; i<imax; i++) for (j=1; j<jmax; j++) {
idx = j * NUM_DETAILS + i;
v = -heights[idx] * 4; idx-=NUM_DETAILS;
v += heights[idx]; idx+=NUM_DETAILS-1;
v += heights[idx]; idx+=2;
v += heights[idx]; idx+=NUM_DETAILS-1;
v += heights[idx]; idx-=NUM_DETAILS;
velocity[idx] = (velocity[idx] + v * 0.5) * 0.9;
}
/*
// why dont I use Matrix3D !?
var targetAngleH:Number = (mouseX -232.5) / 465 * 40 * 0.017453292519943295,
targetAngleV:Number = -((mouseY -232.5) / 465 * 40 + 40) * 0.017453292519943295;
rotH += (targetAngleH - viewedAngleH) * 0.01;
rotV += (targetAngleV - viewedAngleV) * 0.01;
//viewedAngleH += (rotH *= 0.9);
//viewedAngleV += (rotV *= 0.9);
modelviewMatrix.identity();
modelviewMatrix.prependTranslation(0, 0, cameraDistance);
modelviewMatrix.prependRotation(-viewedAngleV*57.29577951308232, Vector3D.X_AXIS);
modelviewMatrix.prependRotation(-viewedAngleH*57.29577951308232, Vector3D.Y_AXIS);
_invmat.copyFrom(modelviewMatrix);
_invmat.invert();
_invmat.copyRowTo(3, cameraPosition);
//cameraPosition.negate();
//*/
var targetAngleH = ((mouseX -WIDTH/2) / WIDTH * 80) * 0.017453292519943295,
targetAngleV = ((mouseY -HEIGHT/2) / HEIGHT * 80) * 0.017453292519943295;
rotH += (targetAngleH - viewedAngleH) * 0.01;
rotV += (targetAngleV - viewedAngleV) * 0.01;
viewedAngleH += (rotH *= 0.9);
viewedAngleV += (rotV *= 0.9);
mat4.identity(mat);
//mat4.translate(mat, [0.0, 0.0, -100.0]);
mat4.rotate(mat, degToRad(-viewedAngleV*57.29577951308232), [1, 0, 0]);
mat4.rotate(mat, degToRad(-viewedAngleH*57.29577951308232), [0, 1, 0]);
mat4.inverse(mat);
mat4.multiplyVec3(mat, [0.0, 0.0, -100.0], cameraPos);
cameraPosition.x = cameraPos[0];
cameraPosition.y = cameraPos[1];
cameraPosition.z = cameraPos[2];
}
window.onload = webGLStart;
//
koh5_pano
Drag mouse to navigate.
Navigation
- Left/Right Mouse drag: Changes camera heading.
- Up/Sown Mouse drag: Changes camera pitch.
- Scroll wheel: Changes camera field of view.
- I-Key: Displays Info panel with canvas size, image size and FPS.
17.Aug.2010, Martin Wengenmayer