Welcome to Mashykom WebSite



WebGLを用いた3D コンピュータグラフィックス入門


 WebGLとは、Webブラウザ上(ホームページ上)で、三次元のCGをリアルタイムに表示して動かせるオープンソースのグラフィックAPIです。OpenGLから派生したOpenGL ESの3D APIをWebブラウザに移植したものということになります。

 OpenGLは、1990年代から歴史あるAPIで、長らく3DゲームやCADといった三次元処理を行えるAPIとして、広範に使用されてきました。2003年頃、高機能携帯電話などの組み込み機器の隆盛を受けて、そのOpenGLからサブセット版規格が生まれました。OpenGL ES(OpenGL for Embedded Systems)です。OpenGLの仕様が膨大になってきて、組み込み機器に搭載するのが大変だったOpenGLから、不要な機能をできるだけ削ぎ落としたバージョンです。WebGLはこのOpenGL ESをベースに、Webブラウザ向けに微調整されて出来た規格なのです。いわば、OpenGL ES for Webとでも言えます。

 WebGLにもバージョンがあり、現在、バージョン1と、バージョン2の二つがあります。WebGL1はOpenGL ES 2.0をベースにしています。WebGL2はOpenGL ES 3.0をベースにしています。WebGL2はWebGL1とどう違うのかというと、基本的なコンセプトはほぼ変わっておらず、WebGL1の機能増強版といったところです。現在、ほとんどすべてのブラウザにはWebGL1 がインストールされています。

 こうして、WebGL は、ウェブブラウザ上で OpenGL ES 相当の描画処理を行うことができる低レベル API なので、GPU を利用した高速で強力な描画性能をウェブにもたらします。セキュリティの観点から、ブラウザにはプラグイン等をインストールすることが敬遠されて久しいですが、WebGL は JavaScript の API として実装されているため、あらためてプラグイン等を一切インストールすること無く実行できます。ブラウザが WebGL を実行できるのであれば、通常のウェブページと同様に、PC だけでなくモバイル端末などでも同様のコンテンツを配信することができ、マルチプラットフォームな開発が行えるのが特徴です。 HTML canvas 内で 3D グラフィックスをレンダリングするための、OpenGL ES 2.0 に基づく API を Web コンテンツで使用できます。WebGL のプログラムは JavaScript で記述する制御コードと、コンピュータの Graphics Processing Unit (GPU) で実行する特殊効果コード (シェーダーコード) で構成されます。

 このページでは、WebGL の基礎を紹介します。ここでは、OpenGLでのシェーディング言語、および、OpenGL そのものの説明は行いません。OpenGLについては、OpenGLのページを読んでください。なお、JavaScript の理解が必須なので、JavaScript の予備知識がない場合には、JavaScript の Tutorials ページを参照してください。

Last updated: 2019.2.16



****************************************************
JavaScriptを用いたWebGLの使い方:
****************************************************



 HTMLでグラフィック関連のタグとして追加された canvas は、その名の通り図形や画像を描画するためのキャンバスとして動作するタグで、javascript を使って操作することが可能です。もちろん、WebGL もこの canvas を利用することになるので、WebGL を記述する前に canvas タグについて簡単に解説します。  canvas は 従来よりある img タグなどと同様、自由に大きさなどを指定できる矩形領域です。javascript によって矩形領域内に操作を加えることが可能で、図形、文字などを自由に描画することができます。それらに影をつけたり、色を塗ったり、あるいは回転させて描画させたりなど、かなり柔軟に操作できます。また、ウェブ上で一般的に使われているフォーマット(gif、jpg、png など)であれば、画像を使った描画処理も可能となっています。ただし、アニメーション処理を一括で管理するようなオブジェクトやメソッドは存在しません。つまり、動きのあるコンテンツを作成するためには、javascript による恒常的なループ処理を自前で実装してやる必要があります。  3D 描画のため WebGL を使うにあたって始めに必要なものは、canvas です。以下の HTML では canvas と、WebGL コンテキストの初期化が行われています。以下のコードは、WebGL入門のサイトから引用しました。GL コンテキストの初期化に成功すると、gl がコンテキストへの参照になります。この例ではクリアカラーを黒に設定しますので、コンテキストはこの色でクリアします。ファイル名を"index.html"として保存します。

<!doctype html>
<html lang="en">
  <head>
    <title>WebGL Demo</title>
    <meta charset="utf-8">
    <link rel="stylesheet" href="webgl.css" type="text/css">
  </head>

  <body>
    <canvas id="glcanvas" width="640" height="480"></canvas>
  </body>

  <script>
    main();
    //
    // start here
    //
    function main() {
	  const canvas = document.getElementById("glcanvas");
      // Initialize the GL context
      const gl = canvas.getContext("webgl");
      // Only continue if WebGL is available and working
      if (!gl) {
        alert("Unable to initialize WebGL. Your browser or machine may not support it.");
        return;
      }
      // Set clear color , fully opaque
      gl.clearColor(1.0, 0.0, 1.0, 1.0);
      // Clear the color buffer with specified clear color
      gl.clear(gl.COLOR_BUFFER_BIT);
    }
  </script>
</html>

ここで、webgl.cssの中身は

canvas {
	border: 2px solid black;
	background-color: black;
}
video {
	display: none;
}

です。これをファイル名"webgl.css"として、上のコードが保存されているディレクトリと同じディレクトリに保存します。
 canvas の WebGL コンテキストを取得するため、canvas に "webgl" という名前のコンテキストを要求します。それが失敗した場合は、alertを出します。以上で、準備の処理は完了です。この時点で、gl は null 値 (WebGL コンテキストが利用できない) またはレンダリングを行う WebGL コンテキストへの参照のどちらかになります。

 以上で、Web GL コンテキストを正常に初期化するために充分なコードが揃いました。また、以下のように紫色に塗りつぶされた大きな四角形が表示され、コンテンツの受け取りを待つ状態になっています。このコードをコピペして、ブラウザで開いてみてください。
webgl_1.png

 次に、図形の描写をします。レンダリングを始める前に理解しておくべき重要なことは、 2 次元のオブジェクトをレンダリングするときでも、それは 3 次元の空間に描画されるということです。そのためオブジェクトを描画するのと同じように、シーンの外観を作成するシェーダーのプログラムを作成する必要があります。スクリーン上で例えば、正方形がどのように見えるかを設定することです。
 シェーダーは OpenGL ES Shading Language を使用して作成します。コンテンツの管理や更新を簡単にするため、JavaScript ですべてを作成する代わりに、HTML 文書の中にあるシェーダーを読み込むようにコードを記述します。この役割を担うのが いかに定義するinitShaders() 関数です。

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)) {
    alert("シェーダープログラムを初期化できません。");
  }
  
  gl.useProgram(shaderProgram);
  
  vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");
  gl.enableVertexAttribArray(vertexPositionAttribute);
}


2 つのシェーダープログラムを読み込みます。一つ目はフラグメントシェーダーで、ID 値が "shader-fs" である 'script' 要素から読み込みます。二つ目はバーテックスシェーダーで、ID 値が "shader-vs" である 'script'要素から読み込みます。

 フラグメントシェーダーは、各ピクセルの色を決定します。ポリゴン内の各ピクセルは、GL の用語でフラグメントと呼びます。gl_FragColor はフラグメントの色として使用する、GL の組み込み変数です。以下のように値を設定することで、ピクセルの色を定義します。


<script id="shader-fs" type="x-shader/x-fragment">
  void main(void) {
    gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
  }
</script>

 バーテックスシェーダーは、各頂点の位置や形状を定義します。

<script id="shader-vs" type="x-shader/x-vertex">
  attribute vec3 aVertexPosition;

  uniform mat4 uMVMatrix;
  uniform mat4 uPMatrix;
  
  void main(void) {
    gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
  }
</script>

シェーダーのコードを読み込んだら、シェーダーオブジェクトがバーテックスシェーダー, あるいは,フラグメントシェーダーであることを判断するために MIME タイプの確認を行い、そして取り出したソースコードから適切なタイプのシェーダーを生成します。 最後に、ソースコードはシェーダーに渡され、そしてコンパイルされます。シェーダーのコンパイル時にエラーが発生した場合は、警告を表示して null 値を返します。エラーが発生しなかった場合は、コンパイル済みのシェーダーを呼び出し元に返します。以下のプログラムがこれを担います。

gl.shaderSource(shader, theSource);
    
  // シェーダープログラムをコンパイル
  gl.compileShader(shader);  
    
  // コンパイルが成功したかを確認
  if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {  
      alert("シェーダーのコンパイルでエラーが発生しました: " + gl.getShaderInfoLog(shader));  
      return null;  
  }
    
  return shader;
}

 例えば、正方形を描画するときに、その頂点情報を収めるバッファを作成しなければなりません。これは、initBuffers() 関数を呼び出して行います。

var horizAspect = 480.0/640.0;

function initBuffers() {
  squareVerticesBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, squareVerticesBuffer);
  
  var vertices = [
    1.0,  1.0,  0.0,
    -1.0, 1.0,  0.0,
    1.0,  -1.0, 0.0,
    -1.0, -1.0, 0.0
  ];
  
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
}

 シェーダーの確立とオブジェクトの構築を行ったら、実際にシーンを描画する段階になります。

function drawScene() {
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
  
  perspectiveMatrix = makePerspective(45, 640.0/480.0, 0.1, 100.0);
  
  loadIdentity();
  mvTranslate([-0.0, 0.0, -6.0]);
  
  gl.bindBuffer(gl.ARRAY_BUFFER, squareVerticesBuffer);
  gl.vertexAttribPointer(vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0);
  setMatrixUniforms();
  gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
}

コンテキストを背景色でクリアします。そしてカメラの遠近感を定義します。視野角を 45 度、縦横の比を 640/480 (canvas の寸法) に設定しています。このチュートリアルは、MDNのwebgl-examplesにあります。

 以上のプログラムをつなぎ合わせて、WebGLによるグラフィックス描画は完成します。以下が完成したJavaScriptです。

 4角形を書くだけで、これだけの長さのコードが必要です。アニメーションを描画するときのことを考えると、気が遠くなります。

 立方体が回転するグラフィクスを描くWebGLのJavaScriptプログラムは非常に長いので、ここで説明することは控えます。実際に描かれるグラフィックスは、このGithubのexample5を開いてブラウザで開けば,以下のようなグラフィックスが表示できます。
WebGL Demo

****************************************************
Three.js を用いたWebGLの利用法
****************************************************



Three.jsは、HTMLの3D技術WebGLを容易にしたフレームワークです。ウェブブラウザ上でリアルタイムレンダリングによる3次元コンピュータグラフィックスを描画する軽量なJavaScriptライブラリです。WebGLだけで3D表現をするためには、立方体一つ表示するだけでも多くのJavaScriptやGLSLコードを書く必要があり専門知識も必要です。Three.jsを使えばJavaScriptの知識だけで簡単に3Dコンテンツが作成できるため、手軽に扱えるようになります。言い換えると、three.jsは、WebGLのAPIを簡略化するためのラッパと言えます。

 Three.js のチュートリアルに沿って、使用方法を説明します。このチュートリアルのオリジナルはこのサイトにあります。始めに、日本語版とも言えるICS MEDIAさんのチュートリアル・サイトの説明を引用します。以下のコードをコピペして、example.html として保存してください。

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8"/>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/100/three.min.js"></script>
</head>
<body>
  <canvas id="myCanvas"></canvas>
</body>


  <script>
	// ページの読み込みを待つ
	window.addEventListener('load', init);

	function init() {

	  // サイズを指定
	  const width = 480;
	  const height = 320;

	  // レンダラーを作成
	  const renderer = new THREE.WebGLRenderer({
		canvas: document.querySelector('#myCanvas')
	  });
	  renderer.setPixelRatio(window.devicePixelRatio);
	  renderer.setSize(width, height);

	  // シーンを作成
	  const scene = new THREE.Scene();

	  // カメラを作成
	  const camera = new THREE.PerspectiveCamera(45, width / height);
	  camera.position.set(0, 0, +1000);

	  // 箱を作成
	  const geometry = new THREE.BoxGeometry(400, 400, 400);
	  const material = new THREE.MeshNormalMaterial();
	  const box = new THREE.Mesh(geometry, material);
	  scene.add(box);

	  tick();

	  // 毎フレーム時に実行されるループイベントです
	  function tick() {
		box.rotation.y += 0.01;
		renderer.render(scene, camera); // レンダリング

		requestAnimationFrame(tick);
	  }
	}
  </script>
</html>

Three.jsはHTML5のcanvas要素を利用して、canvas要素はコンテンツを表示する描画エリアを確保します。canvas要素には属性としてid(ID値)を設定する必要があります。以下のように、記述します。

<body>
  <canvas id="myCanvas"></canvas>
</body>

canvas要素の内容はJavaScriptを使って設定します。 Three.min.jsはJavaScriptのライブラリですが、このファイルを読み込むことによってはじめてThree.jsが利用できるようになります。CDN(コンテンツ・デリバリー・ネットワーク)で提供されているURLを使います。以下のように、'head’タグの中に記述します。

<head>
  <meta charset="utf-8"/>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/100/three.min.js"></script>
  </head>

 このファイルをブラウザで開いてください。以下のように、立方体が回転する3Dグラフィックスが描かれます。コードはかなり単純化されています。

 'JavaScript'の記述の内容について大まかな説明をしましょう。WebGLの処理は'three.min.js'のページの読み込みが終わってから実行させます。addEventListener()関数を使って、ページが読み込み終わったときに実行させたい関数を指定します。この関数init()の中にThree.jsのコードを書いていきます。まずは、レンダラーとシーンを作成します。描画する立体図形の映像を撮るカメラの位置と方向を定めるために、カメラを作成します。「箱を作成」の部分は、直方体を描画しています。'BoxGeometry'が箱を意味します。直方体ではなくて、例えば、球体を描画するときは、'SphereGeometry'を使います。'tick();'の部分は、描画した直方体を回転させる為の記述です。コマンドなど記述の詳細はこのように書くものだ理解します。この中身を少しづつ修正していくと、複雑なグラフィックスも描けるようになります。


**************
続く 
**************

このページの先頭に行く
Three.jsのページに行く
トップ・ページに行く