WordPress で WebGL を試す -その2-

目次

シェーダのテスト

WordPress で WebGL を試す -その1- ではシェーダを使用していない WebGL のテストを行いましたが、今回はシェーダを使用したテストになります。ただし、黄色い三角形を描画するといった単純なものです。

また、メインページ等、WebGL を用いた複数の記事が表示される場合、どのような動作になるのか確認してみました。


Your browser doesn’t appear to support the HTML5 <canvas> element.

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