目次
シェーダのテスト
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組のシェーダしか記述できません。複数種類のシェーダを使いたい場合には、その数分フィールドを追加しないといけないので、この方法はあまり現実的ではなさそうです。