index.html
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>wgld.org WebGL sample 012</title>
<script src="script.js" type="text/javascript"></script>
<script src="minMatrix.js" type="text/javascript"></script>
<script id="vs" type="x-shader/x-vertex">
attribute vec3 position;
attribute vec3 normal;
attribute vec4 color;
uniform mat4 mvpMatrix;
varying vec3 vNormal;
varying vec4 vColor;
void main(void){
vNormal = normal;
vColor = color;
gl_Position = mvpMatrix * vec4(position, 1.0);
}
</script>
<script id="fs" type="x-shader/x-fragment">
precision mediump float;
uniform mat4 invMatrix;
uniform vec3 lightDirection;
uniform vec3 eyeDirection;
uniform vec4 ambientColor;
varying vec3 vNormal;
varying vec4 vColor;
void main(void){
vec3 invLight = normalize(invMatrix * vec4(lightDirection, 0.0)).xyz;
vec3 invEye = normalize(invMatrix * vec4(eyeDirection, 0.0)).xyz;
vec3 halfLE = normalize(invLight + invEye);
float diffuse = clamp(dot(vNormal, invLight), 0.0, 1.0);
float specular = pow(clamp(dot(vNormal, halfLE), 0.0, 1.0), 50.0);
vec4 destColor = vColor * vec4(vec3(diffuse), 1.0) + vec4(vec3(specular), 1.0) + ambientColor;
gl_FragColor = destColor;
}
</script>
</head>
<body>
<canvas id="canvas"></canvas>
</body>
</html>
script.js
// sample_012
//
// WebGLでフォンシェーディング
onload = function(){
// canvasエレメントを取得
var c = document.getElementById('canvas');
c.width = 500;
c.height = 300;
// webglコンテキストを取得
var gl = c.getContext('webgl') || c.getContext('experimental-webgl');
// 頂点シェーダとフラグメントシェーダの生成
var v_shader = create_shader('vs');
var f_shader = create_shader('fs');
// プログラムオブジェクトの生成とリンク
var prg = create_program(v_shader, f_shader);
// attributeLocationを配列に取得
var attLocation = new Array();
attLocation[0] = gl.getAttribLocation(prg, 'position');
attLocation[1] = gl.getAttribLocation(prg, 'normal');
attLocation[2] = gl.getAttribLocation(prg, 'color');
// attributeの要素数を配列に格納
var attStride = new Array();
attStride[0] = 3;
attStride[1] = 3;
attStride[2] = 4;
// トーラスの頂点データを生成
var torusData = torus(4, 16, 1.5, 3.0);
var position = torusData.p;
var normal = torusData.n;
var color = torusData.c;
var index = torusData.i;
// VBOの生成
var pos_vbo = create_vbo(position);
var nor_vbo = create_vbo(normal);
var col_vbo = create_vbo(color);
// VBO を登録する
set_attribute([pos_vbo, nor_vbo, col_vbo], attLocation, attStride);
// IBOの生成
var ibo = create_ibo(index);
// IBOをバインドして登録する
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo);
// uniformLocationを配列に取得
var uniLocation = new Array();
uniLocation[0] = gl.getUniformLocation(prg, 'mvpMatrix');
uniLocation[1] = gl.getUniformLocation(prg, 'invMatrix');
uniLocation[2] = gl.getUniformLocation(prg, 'lightDirection');
uniLocation[3] = gl.getUniformLocation(prg, 'eyeDirection');
uniLocation[4] = gl.getUniformLocation(prg, 'ambientColor');
// minMatrix.js を用いた行列関連処理
// matIVオブジェクトを生成
var m = new matIV();
// 各種行列の生成と初期化
var mMatrix = m.identity(m.create());
var vMatrix = m.identity(m.create());
var pMatrix = m.identity(m.create());
var tmpMatrix = m.identity(m.create());
var mvpMatrix = m.identity(m.create());
var invMatrix = m.identity(m.create());
// 平行光源の向き
var lightDirection = [-0.5, 1.0, 0.5];
// 環境光の色
var ambientColor = [0.1, 0.1, 0.1, 1.0];
// 視点ベクトル
var eyeDirection = [0.0, 0.0, 20.0];
// ビュー×プロジェクション座標変換行列
m.lookAt(eyeDirection, [0, 0, 0], [0, 1, 0], vMatrix);
m.perspective(45, c.width / c.height, 0.1, 100, pMatrix);
m.multiply(pMatrix, vMatrix, tmpMatrix);
// カウンタの宣言
var count = 0;
// カリングと深度テストを有効にする
gl.enable(gl.DEPTH_TEST);
gl.depthFunc(gl.LEQUAL);
gl.enable(gl.CULL_FACE);
// 恒常ループ
(function(){
// canvasを初期化
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clearDepth(1.0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
// カウンタをインクリメントする
count++;
// カウンタを元にラジアンを算出
var rad = (count % 360) * Math.PI / 180;
// モデル座標変換行列の生成
m.identity(mMatrix);
m.rotate(mMatrix, rad, [0, 1, 1], mMatrix);
m.multiply(tmpMatrix, mMatrix, mvpMatrix);
// モデル座標変換行列から逆行列を生成
m.inverse(mMatrix, invMatrix);
// uniform変数の登録
gl.uniformMatrix4fv(uniLocation[0], false, mvpMatrix);
gl.uniformMatrix4fv(uniLocation[1], false, invMatrix);
gl.uniform3fv(uniLocation[2], lightDirection);
gl.uniform3fv(uniLocation[3], eyeDirection);
gl.uniform4fv(uniLocation[4], ambientColor);
// モデルの描画
gl.drawElements(gl.TRIANGLES, index.length, gl.UNSIGNED_SHORT, 0);
// コンテキストの再描画
gl.flush();
// ループのために再帰呼び出し
setTimeout(arguments.callee, 1000 / 30);
})();
// シェーダを生成する関数
function create_shader(id){
// シェーダを格納する変数
var shader;
// HTMLからscriptタグへの参照を取得
var scriptElement = document.getElementById(id);
// scriptタグが存在しない場合は抜ける
if(!scriptElement){return;}
// scriptタグのtype属性をチェック
switch(scriptElement.type){
// 頂点シェーダの場合
case 'x-shader/x-vertex':
shader = gl.createShader(gl.VERTEX_SHADER);
break;
// フラグメントシェーダの場合
case 'x-shader/x-fragment':
shader = gl.createShader(gl.FRAGMENT_SHADER);
break;
default :
return;
}
// 生成されたシェーダにソースを割り当てる
gl.shaderSource(shader, scriptElement.text);
// シェーダをコンパイルする
gl.compileShader(shader);
// シェーダが正しくコンパイルされたかチェック
if(gl.getShaderParameter(shader, gl.COMPILE_STATUS)){
// 成功していたらシェーダを返して終了
return shader;
}else{
// 失敗していたらエラーログをアラートする
alert(gl.getShaderInfoLog(shader));
}
}
// プログラムオブジェクトを生成しシェーダをリンクする関数
function create_program(vs, fs){
// プログラムオブジェクトの生成
var program = gl.createProgram();
// プログラムオブジェクトにシェーダを割り当てる
gl.attachShader(program, vs);
gl.attachShader(program, fs);
// シェーダをリンク
gl.linkProgram(program);
// シェーダのリンクが正しく行なわれたかチェック
if(gl.getProgramParameter(program, gl.LINK_STATUS)){
// 成功していたらプログラムオブジェクトを有効にする
gl.useProgram(program);
// プログラムオブジェクトを返して終了
return program;
}else{
// 失敗していたらエラーログをアラートする
alert(gl.getProgramInfoLog(program));
}
}
// VBOを生成する関数
function create_vbo(data){
// バッファオブジェクトの生成
var vbo = gl.createBuffer();
// バッファをバインドする
gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
// バッファにデータをセット
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.STATIC_DRAW);
// バッファのバインドを無効化
gl.bindBuffer(gl.ARRAY_BUFFER, null);
// 生成した VBO を返して終了
return vbo;
}
// VBOをバインドし登録する関数
function set_attribute(vbo, attL, attS){
// 引数として受け取った配列を処理する
for(var i in vbo){
// バッファをバインドする
gl.bindBuffer(gl.ARRAY_BUFFER, vbo[i]);
// attributeLocationを有効にする
gl.enableVertexAttribArray(attL[i]);
// attributeLocationを通知し登録する
gl.vertexAttribPointer(attL[i], attS[i], gl.FLOAT, false, 0, 0);
}
}
// IBOを生成する関数
function create_ibo(data){
// バッファオブジェクトの生成
var ibo = gl.createBuffer();
// バッファをバインドする
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo);
// バッファにデータをセット
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Int16Array(data), gl.STATIC_DRAW);
// バッファのバインドを無効化
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
// 生成したIBOを返して終了
return ibo;
}
// トーラスを生成する関数
function torus(row, column, irad, orad, color){
var pos = new Array(), nor = new Array(),
col = new Array(), idx = new Array();
for(var i = 0; i <= row; i++){
var r = Math.PI * 2 / row * i;
var rr = Math.cos(r);
var ry = Math.sin(r);
for(var ii = 0; ii <= column; ii++){
var tr = Math.PI * 2 / column * ii;
var tx = (rr * irad + orad) * Math.cos(tr);
var ty = ry * irad;
var tz = (rr * irad + orad) * Math.sin(tr);
var rx = rr * Math.cos(tr);
var rz = rr * Math.sin(tr);
if(color){
var tc = color;
}else{
tc = hsva(360 / column * ii, 1, 1, 1);
}
pos.push(tx, ty, tz);
nor.push(rx, ry, rz);
col.push(tc[0], tc[1], tc[2], tc[3]);
}
}
for(i = 0; i < row; i++){
for(ii = 0; ii < column; ii++){
r = (column + 1) * i + ii;
idx.push(r, r + column + 1, r + 1);
idx.push(r + column + 1, r + column + 2, r + 1);
}
}
return {p : pos, n : nor, c : col, i : idx};
}
// HSVカラー取得用関数
function hsva(h, s, v, a){
if(s > 1 || v > 1 || a > 1){return;}
var th = h % 360;
var i = Math.floor(th / 60);
var f = th / 60 - i;
var m = v * (1 - s);
var n = v * (1 - s * f);
var k = v * (1 - s * (1 - f));
var color = new Array();
if(!s > 0 && !s < 0){
color.push(v, v, v, a);
} else {
var r = new Array(v, n, m, m, k, v);
var g = new Array(k, v, v, n, m, m);
var b = new Array(m, m, k, v, v, n);
color.push(r[i], g[i], b[i], a);
}
return color;
}
};
minMatrix.js
// ------------------------------------------------------------------------------------------------
// minMatrix.js
// version 0.0.1
// Copyright (c) doxas
// ------------------------------------------------------------------------------------------------
function matIV(){
this.create = function(){
return new Float32Array(16);
};
this.identity = function(dest){
dest[0] = 1; dest[1] = 0; dest[2] = 0; dest[3] = 0;
dest[4] = 0; dest[5] = 1; dest[6] = 0; dest[7] = 0;
dest[8] = 0; dest[9] = 0; dest[10] = 1; dest[11] = 0;
dest[12] = 0; dest[13] = 0; dest[14] = 0; dest[15] = 1;
return dest;
};
this.multiply = function(mat1, mat2, dest){
var a = mat1[0], b = mat1[1], c = mat1[2], d = mat1[3],
e = mat1[4], f = mat1[5], g = mat1[6], h = mat1[7],
i = mat1[8], j = mat1[9], k = mat1[10], l = mat1[11],
m = mat1[12], n = mat1[13], o = mat1[14], p = mat1[15],
A = mat2[0], B = mat2[1], C = mat2[2], D = mat2[3],
E = mat2[4], F = mat2[5], G = mat2[6], H = mat2[7],
I = mat2[8], J = mat2[9], K = mat2[10], L = mat2[11],
M = mat2[12], N = mat2[13], O = mat2[14], P = mat2[15];
dest[0] = A * a + B * e + C * i + D * m;
dest[1] = A * b + B * f + C * j + D * n;
dest[2] = A * c + B * g + C * k + D * o;
dest[3] = A * d + B * h + C * l + D * p;
dest[4] = E * a + F * e + G * i + H * m;
dest[5] = E * b + F * f + G * j + H * n;
dest[6] = E * c + F * g + G * k + H * o;
dest[7] = E * d + F * h + G * l + H * p;
dest[8] = I * a + J * e + K * i + L * m;
dest[9] = I * b + J * f + K * j + L * n;
dest[10] = I * c + J * g + K * k + L * o;
dest[11] = I * d + J * h + K * l + L * p;
dest[12] = M * a + N * e + O * i + P * m;
dest[13] = M * b + N * f + O * j + P * n;
dest[14] = M * c + N * g + O * k + P * o;
dest[15] = M * d + N * h + O * l + P * p;
return dest;
};
this.scale = function(mat, vec, dest){
dest[0] = mat[0] * vec[0];
dest[1] = mat[1] * vec[0];
dest[2] = mat[2] * vec[0];
dest[3] = mat[3] * vec[0];
dest[4] = mat[4] * vec[1];
dest[5] = mat[5] * vec[1];
dest[6] = mat[6] * vec[1];
dest[7] = mat[7] * vec[1];
dest[8] = mat[8] * vec[2];
dest[9] = mat[9] * vec[2];
dest[10] = mat[10] * vec[2];
dest[11] = mat[11] * vec[2];
dest[12] = mat[12];
dest[13] = mat[13];
dest[14] = mat[14];
dest[15] = mat[15];
return dest;
};
this.translate = function(mat, vec, dest){
dest[0] = mat[0]; dest[1] = mat[1]; dest[2] = mat[2]; dest[3] = mat[3];
dest[4] = mat[4]; dest[5] = mat[5]; dest[6] = mat[6]; dest[7] = mat[7];
dest[8] = mat[8]; dest[9] = mat[9]; dest[10] = mat[10]; dest[11] = mat[11];
dest[12] = mat[0] * vec[0] + mat[4] * vec[1] + mat[8] * vec[2] + mat[12];
dest[13] = mat[1] * vec[0] + mat[5] * vec[1] + mat[9] * vec[2] + mat[13];
dest[14] = mat[2] * vec[0] + mat[6] * vec[1] + mat[10] * vec[2] + mat[14];
dest[15] = mat[3] * vec[0] + mat[7] * vec[1] + mat[11] * vec[2] + mat[15];
return dest;
};
this.rotate = function(mat, angle, axis, dest){
var sq = Math.sqrt(axis[0] * axis[0] + axis[1] * axis[1] + axis[2] * axis[2]);
if(!sq){return null;}
var a = axis[0], b = axis[1], c = axis[2];
if(sq != 1){sq = 1 / sq; a *= sq; b *= sq; c *= sq;}
var d = Math.sin(angle), e = Math.cos(angle), f = 1 - e,
g = mat[0], h = mat[1], i = mat[2], j = mat[3],
k = mat[4], l = mat[5], m = mat[6], n = mat[7],
o = mat[8], p = mat[9], q = mat[10], r = mat[11],
s = a * a * f + e,
t = b * a * f + c * d,
u = c * a * f - b * d,
v = a * b * f - c * d,
w = b * b * f + e,
x = c * b * f + a * d,
y = a * c * f + b * d,
z = b * c * f - a * d,
A = c * c * f + e;
if(angle){
if(mat != dest){
dest[12] = mat[12]; dest[13] = mat[13];
dest[14] = mat[14]; dest[15] = mat[15];
}
} else {
dest = mat;
}
dest[0] = g * s + k * t + o * u;
dest[1] = h * s + l * t + p * u;
dest[2] = i * s + m * t + q * u;
dest[3] = j * s + n * t + r * u;
dest[4] = g * v + k * w + o * x;
dest[5] = h * v + l * w + p * x;
dest[6] = i * v + m * w + q * x;
dest[7] = j * v + n * w + r * x;
dest[8] = g * y + k * z + o * A;
dest[9] = h * y + l * z + p * A;
dest[10] = i * y + m * z + q * A;
dest[11] = j * y + n * z + r * A;
return dest;
};
this.lookAt = function(eye, center, up, dest){
var eyeX = eye[0], eyeY = eye[1], eyeZ = eye[2],
upX = up[0], upY = up[1], upZ = up[2],
centerX = center[0], centerY = center[1], centerZ = center[2];
if(eyeX == centerX && eyeY == centerY && eyeZ == centerZ){return this.identity(dest);}
var x0, x1, x2, y0, y1, y2, z0, z1, z2, l;
z0 = eyeX - center[0]; z1 = eyeY - center[1]; z2 = eyeZ - center[2];
l = 1 / Math.sqrt(z0 * z0 + z1 * z1 + z2 * z2);
z0 *= l; z1 *= l; z2 *= l;
x0 = upY * z2 - upZ * z1;
x1 = upZ * z0 - upX * z2;
x2 = upX * z1 - upY * z0;
l = Math.sqrt(x0 * x0 + x1 * x1 + x2 * x2);
if(!l){
x0 = 0; x1 = 0; x2 = 0;
} else {
l = 1 / l;
x0 *= l; x1 *= l; x2 *= l;
}
y0 = z1 * x2 - z2 * x1; y1 = z2 * x0 - z0 * x2; y2 = z0 * x1 - z1 * x0;
l = Math.sqrt(y0 * y0 + y1 * y1 + y2 * y2);
if(!l){
y0 = 0; y1 = 0; y2 = 0;
} else {
l = 1 / l;
y0 *= l; y1 *= l; y2 *= l;
}
dest[0] = x0; dest[1] = y0; dest[2] = z0; dest[3] = 0;
dest[4] = x1; dest[5] = y1; dest[6] = z1; dest[7] = 0;
dest[8] = x2; dest[9] = y2; dest[10] = z2; dest[11] = 0;
dest[12] = -(x0 * eyeX + x1 * eyeY + x2 * eyeZ);
dest[13] = -(y0 * eyeX + y1 * eyeY + y2 * eyeZ);
dest[14] = -(z0 * eyeX + z1 * eyeY + z2 * eyeZ);
dest[15] = 1;
return dest;
};
this.perspective = function(fovy, aspect, near, far, dest){
var t = near * Math.tan(fovy * Math.PI / 360);
var r = t * aspect;
var a = r * 2, b = t * 2, c = far - near;
dest[0] = near * 2 / a;
dest[1] = 0;
dest[2] = 0;
dest[3] = 0;
dest[4] = 0;
dest[5] = near * 2 / b;
dest[6] = 0;
dest[7] = 0;
dest[8] = 0;
dest[9] = 0;
dest[10] = -(far + near) / c;
dest[11] = -1;
dest[12] = 0;
dest[13] = 0;
dest[14] = -(far * near * 2) / c;
dest[15] = 0;
return dest;
};
this.transpose = function(mat, dest){
dest[0] = mat[0]; dest[1] = mat[4];
dest[2] = mat[8]; dest[3] = mat[12];
dest[4] = mat[1]; dest[5] = mat[5];
dest[6] = mat[9]; dest[7] = mat[13];
dest[8] = mat[2]; dest[9] = mat[6];
dest[10] = mat[10]; dest[11] = mat[14];
dest[12] = mat[3]; dest[13] = mat[7];
dest[14] = mat[11]; dest[15] = mat[15];
return dest;
};
this.inverse = function(mat, dest){
var a = mat[0], b = mat[1], c = mat[2], d = mat[3],
e = mat[4], f = mat[5], g = mat[6], h = mat[7],
i = mat[8], j = mat[9], k = mat[10], l = mat[11],
m = mat[12], n = mat[13], o = mat[14], p = mat[15],
q = a * f - b * e, r = a * g - c * e,
s = a * h - d * e, t = b * g - c * f,
u = b * h - d * f, v = c * h - d * g,
w = i * n - j * m, x = i * o - k * m,
y = i * p - l * m, z = j * o - k * n,
A = j * p - l * n, B = k * p - l * o,
ivd = 1 / (q * B - r * A + s * z + t * y - u * x + v * w);
dest[0] = ( f * B - g * A + h * z) * ivd;
dest[1] = (-b * B + c * A - d * z) * ivd;
dest[2] = ( n * v - o * u + p * t) * ivd;
dest[3] = (-j * v + k * u - l * t) * ivd;
dest[4] = (-e * B + g * y - h * x) * ivd;
dest[5] = ( a * B - c * y + d * x) * ivd;
dest[6] = (-m * v + o * s - p * r) * ivd;
dest[7] = ( i * v - k * s + l * r) * ivd;
dest[8] = ( e * A - f * y + h * w) * ivd;
dest[9] = (-a * A + b * y - d * w) * ivd;
dest[10] = ( m * u - n * s + p * q) * ivd;
dest[11] = (-i * u + j * s - l * q) * ivd;
dest[12] = (-e * z + f * x - g * w) * ivd;
dest[13] = ( a * z - b * x + c * w) * ivd;
dest[14] = (-m * t + n * r - o * q) * ivd;
dest[15] = ( i * t - j * r + k * q) * ivd;
return dest;
};
}