目次
シェーダのテスト
WordPress で WebGL を試す -その1- ではシェーダを使用していない WebGL のテストを行いましたが、今回はシェーダを使用したテストになります。ただし、黄色い三角形を描画するといった単純なものです。
また、メインページ等、WebGL を用いた複数の記事が表示される場合、どのような動作になるのか確認してみました。
バーテックスシェーダとフラグメントシェーダを記述できるように 「Custum JS」 を書き換え、「Custum VS」, 「Custum FS」 フィールドを追加しました。「Custum VS」 にはバーテックスシェーダを 「Custum FS」 にはフラグメントシェーダを記述します。
canvas の追加:
WebGL を表示したい位置に以下の canvas タグをテキストエディタで記述します。id は glCanvas002 にしています。
<canvas id="glCanvas002" style="background-color: black;" width="400" height="300"> Your browser doesn't appear to support the HTML5 <code><canvas></code> element. </canvas>
JavaScript の追加:
WebGL のコードです。「Custum JS」フィールドに記述します。
var canvas = null;
var gl = null;
var program = null;
var vertexBuffer = null;
var fps = 30;
var animationFrame = window.requestAnimationFrame
|| window.webkitRequestAnimationFrame
|| window.mozRequestAnimationFrame
|| window.oRequestAnimationFrame
|| window.msRequestAnimationFrame
|| function(f) { return window.setTimeout(f, 1000 / fps); };
var now = window.performance && (
performance.now ||
performance.mozNow ||
performance.msNow ||
performance.oNow ||
performance.webkitNow );
var getTime = function() {
return ( now && now.call( performance ) ) || ( new Date().getTime() );
}
function loadIdentity()
{
var matrix = [
1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0
];
return matrix;
}
function multiMatrix(mat1, mat2)
{
var matrix = new Array(16);
matrix[0] = mat1[0] * mat2[0] + mat1[1] * mat2[4] + mat1[2] * mat2[8] + mat1[3] * mat2[12];
matrix[1] = mat1[0] * mat2[1] + mat1[1] * mat2[5] + mat1[2] * mat2[9] + mat1[3] * mat2[13];
matrix[2] = mat1[0] * mat2[2] + mat1[1] * mat2[6] + mat1[2] * mat2[10] + mat1[3] * mat2[14];
matrix[3] = mat1[0] * mat2[3] + mat1[1] * mat2[7] + mat1[2] * mat2[11] + mat1[3] * mat2[15];
matrix[4] = mat1[4] * mat2[0] + mat1[5] * mat2[4] + mat1[6] * mat2[8] + mat1[7] * mat2[12];
matrix[5] = mat1[4] * mat2[1] + mat1[5] * mat2[5] + mat1[6] * mat2[9] + mat1[7] * mat2[13];
matrix[6] = mat1[4] * mat2[2] + mat1[5] * mat2[6] + mat1[6] * mat2[10] + mat1[7] * mat2[14];
matrix[7] = mat1[4] * mat2[3] + mat1[5] * mat2[7] + mat1[6] * mat2[11] + mat1[7] * mat2[15];
matrix[8] = mat1[8] * mat2[0] + mat1[9] * mat2[4] + mat1[10] * mat2[8] + mat1[11] * mat2[12];
matrix[9] = mat1[8] * mat2[1] + mat1[9] * mat2[5] + mat1[10] * mat2[9] + mat1[11] * mat2[13];
matrix[10] = mat1[8] * mat2[2] + mat1[9] * mat2[6] + mat1[10] * mat2[10] + mat1[11] * mat2[14];
matrix[11] = mat1[8] * mat2[3] + mat1[9] * mat2[7] + mat1[10] * mat2[11] + mat1[11] * mat2[15];
matrix[12] = mat1[12] * mat2[0] + mat1[13] * mat2[4] + mat1[14] * mat2[8] + mat1[15] * mat2[12];
matrix[13] = mat1[12] * mat2[1] + mat1[13] * mat2[5] + mat1[14] * mat2[9] + mat1[15] * mat2[13];
matrix[14] = mat1[12] * mat2[2] + mat1[13] * mat2[6] + mat1[14] * mat2[10] + mat1[15] * mat2[14];
matrix[15] = mat1[12] * mat2[3] + mat1[13] * mat2[7] + mat1[14] * mat2[11] + mat1[15] * mat2[15];
return matrix;
}
function zrotate(mat, angle)
{
var matrix = loadIdentity();
var theta = angle * Math.PI / 180.0;
var co = Math.cos(theta);
var si = Math.sin(theta);
matrix[0] = co;
matrix[1] = -si;
matrix[4] = si;
matrix[5] = co;
return multiMatrix(mat, matrix);
}
function ortho(mat, l, r, b, t, n, f)
{
var matrix = loadIdentity();
matrix[0] = 2 / (r -l);
matrix[3] = - (r + l) / (r - l);
matrix[5] = 2 / (t - b);
matrix[7] = - (t + b) / (t - b);
matrix[10] = -2 / (f - n);
matrix[11] = - (f + n) / (f - n);
return multiMatrix(mat, matrix);
}
function getShader(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-vertex") {
shader = gl.createShader(gl.VERTEX_SHADER);
} else if (shaderScript.type == "x-shader/x-fragment") {
shader = gl.createShader(gl.FRAGMENT_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 vertexShader = getShader("shader-vs");
var fragmentShader = getShader("shader-fs");
program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
alert("Could not initialise shaders");
}
gl.useProgram(program);
program.vertexPositionAttribute =
gl.getAttribLocation(program, "a_position");
gl.enableVertexAttribArray(program.vertexPositionAttribute);
program.projectionLocation =
gl.getUniformLocation(program, "u_projection");
program.modelViewLocation =
gl.getUniformLocation(program, "u_modelView");
}
function initBuffers()
{
var vertices = [
-1.0, -1.732 / 3.0, 0.0,
1.0, -1.732 / 3.0, 0.0,
0.0, 1.732 * 2.0 / 3.0, 0.0
];
vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(
gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
vertexBuffer.itemSize = 3;
vertexBuffer.numItems = 3;
}
var startTime = getTime();
function drawScene()
{
var aspect = gl.drawingBufferWidth / gl.drawingBufferHeight;
var projection = loadIdentity();
var modelView = loadIdentity();
var frame = Math.floor( ( getTime() - startTime ) / ( 1000 / fps / 5) % 360);
projection = ortho(projection, -aspect * 1.2, aspect * 1.2, -1.2, 1.2, -1.0, 1.0);
modelView = zrotate(modelView, frame);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.useProgram(program);
gl.uniformMatrix4fv(program.projectionLocation, false, projection);
gl.uniformMatrix4fv(program.modelViewLocation, false, modelView);
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.vertexAttribPointer(
program.vertexPositionAttribute,
vertexBuffer.itemSize, gl.FLOAT, false, 0, 0);
gl.drawArrays(gl.TRIANGLES, 0, vertexBuffer.numItems);
}
function animation()
{
drawScene();
animationFrame(animation);
};
function initialize()
{
canvas = document.getElementById("glCanvas002");
try {
gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
}
catch(e) {}
if (gl) {
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
initShaders();
initBuffers();
gl.clearColor(0.0, 0.0, 0.0, 1.0);
animation();
}
}
window.onload = initialize;
バーテックスシェーダの追加:
attribute vec3 a_position;
uniform mat4 u_projection;
uniform mat4 u_modelView;
void main()
{
gl_Position = u_projection * u_modelView * vec4(a_position, 1.0);
}
フラグメントシェーダの追加:
precision mediump float;
void main()
{
gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0);
}
テスト結果:
- 個別投稿のページの場合は期待通り WebGL が動くが、メインページ等、WebGL を使った複数の投稿を含む場合、後の方の WebGL (JavaScript) にヘッダが上書きされるようでうまく動きませんでした。予想はしていましたが…。やはり、固定ページで使うべきということでしょうか。仕方がないので more タグを使ってメインページには canvas を表示しないようにしました。
- 「Custum VS」「Custum FS」を追加してバーテックスシェーダ、フラグメントシェーダの記述ができるようにしましたが、1組のシェーダしか記述できません。複数種類のシェーダを使いたい場合には、その数分フィールドを追加しないといけないので、この方法はあまり現実的ではなさそうです。