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 だけでなくモバイル端末などでも同様のコンテンツを配信することができます。

 具体的には、OpenGL ES 2.0 に基づく API であるWebGLを用いて、 HTML ページのcanvas タグ内で 3D グラフィックスをレンダリングします。WebGL のプログラムは JavaScript で記述する制御コードと、コンピュータの Graphics Processing Unit (GPU) で実行する特殊効果コード (シェーダーコード) で構成されます。

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

Last updated: 2019.10.20



JavaScriptを用いたWebGLのプログラム


 HTMLでグラフィック関連のタグとして追加された canvas は、その名の通り図形や画像を描画するためのキャンバスとして動作するタグで、javascript を使って操作することが可能です。もちろん、WebGL もこの canvas を利用することになるので、WebGL を記述する前に canvas タグについて簡単に解説します。

 canvas は 従来よりある img タグなどと同様、自由に大きさなどを指定できる矩形領域です。javascript によって矩形領域内に操作を加えることが可能で、図形、文字などを自由に描画することができます。それらに影をつけたり、色を塗ったり、あるいは回転させて描画させたりなど、かなり柔軟に操作できます。また、ウェブ上で一般的に使われているフォーマット(gif、jpg、png など)であれば、画像を使った描画処理も可能となっています。ただし、アニメーション処理を一括で管理するようなオブジェクトやメソッドは存在しません。つまり、動きのあるコンテンツを作成するためには、javascript による恒常的なループ処理を自前で実装してやる必要があります。

 3D 描画のため WebGL を使うにあたって始めに必要なものは、canvas タグの設定とスタイル設定のためのcss ファイルです。以下のコードは、WebGL入門のサイトから引用しました。この HTML では、

<canvas id="glcanvas" width="640" height="480"></canvas> 
でcanvas タグが設定され、webgl.css ファイルでスタイル設定がなされています。
const gl = canvas.getContext("webgl");
で、canvasで描画されるWebGL コンテキストの初期化が行われています。描画対象の初期化に成功すると、識別子 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();
    //
    // ここから始める
    //
    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 == null) {
        alert("Unable to initialize WebGL. Your browser or machine may not support it.");
        return;
      }
      // カラーバッファの色を指定
      gl.clearColor(1.0, 0.0, 1.0, 1.0);
      // カラー・バッファをgl.clearColor で指定された色にする
      gl.clear(gl.COLOR_BUFFER_BIT);
    }
  </script>
</html>

ここで、webgl.cssの中身は


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

です。これをファイル名"webgl.css"として、上のコードが保存されているディレクトリと同じディレクトリに保存します。

if コード

 
if (gl == null) 

は、canvas に "webgl" という名前のコンテキストを要求し、それが成功したか否かを聞いています。この時点で、gl は null 値 (WebGL コンテキストが利用できない) 、または、レンダリングを行う WebGL コンテキストへの参照の、どちらかになります。失敗した場合は、alertを出します。

 以上で、Web GL コンテキストを正常に初期化するために充分なコードが揃いました。CodePen を用いて、作成したウインドウを表示しましょう。「Result」をクリックすると、以下のように紫色に塗りつぶされた大きな四角形が表示され、コンテンツの受け取りを待つ状態になっています。

See the Pen WebGL_1 by mashyko (@mashyko) on CodePen.


 次に、図形の描写をします。レンダリングを始める前に理解しておくべき重要なことは、 2 次元のオブジェクトをレンダリングするときでも、それは 3 次元の空間に描画されるということです。そのためオブジェクトを描画するのと同じように、シーンの外観を作成するシェーダーのプログラムを作成する必要があります。スクリーン上で例えば、正方形がどのように見えるかを設定することです。

シェーダー・コードを用いたWebGLのプログラム


 シェーダーは OpenGL ES Shading Language を使用して作成します。シェーダーは図形形状の頂点に関する情報と各ピクセルの色をスクリーン上にレンダーするためのデータを作成するプログラムです。だから、WebGLの内容をレンダリングするためには二種類のシェーダー(vertex and fragment shaders)が必要です。これらのプログラムをGLSL言語で書いて、WebGLのプログラムに組み込んで、GPUで実行させるためにコンパイルする必要があります。シェーダーに関する詳しい説明は、このサイトを参照ください。また、WebGLで採用されている clipspace 座標系では、座標の軸の範囲は[-1,+1]に制限されています。clipspace 座標系(x, y, z)は以下のようになっています。

clip-space-graph.svg

 バーテックスシェーダーは、各頂点の位置や形状を定義します。簡単なプログラムを紹介します。aVertexPosition で定義された属性(attribute)から頂点の情報を読み取ります。uProjectionMatrix と uModelViewMatrixで2つの4行4列のマトリックスを設定しています。vertex positionを設定するとき、通常、描画の形状が2次元でも、3次元でも、4次元ベクトル vec4 attribute で設定します。それは、GL内での数学的な処理が4次元ベクトルで演算されるからです。gl_Positionが各バーテックスのデータを読み込んで、GPUでのclipspaceに対応する設定に変換して、保存します。



  // Vertex shader program

  const vsSource = `
    attribute vec4 aVertexPosition;

    uniform mat4 uModelViewMatrix;
    uniform mat4 uProjectionMatrix;

    void main() {
      gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition;
    }
  `;

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



  // Fragment shader program

 const fsSource = `
    void main() {
      gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
    }
  `;

 次に、これらのシェーダーをWebGLに送り、コンパイルしてから、リンクを確立する必要があります。HTML 文書の中にあるシェーダーのscriptを読み込むようにコードを記述します。この役割を担うのが以下に定義するinitShaders() 関数です。

 2 つのシェーダープログラムを読み込みます。シェーダーのコードを読み込んだら、シェーダーオブジェクトがバーテックスシェーダー, あるいは,フラグメントシェーダーであることを判断するために MIME タイプの確認を行い、そして取り出したソースコードから適切なタイプのシェーダーを生成します。

//
// Initialize a shader program, so WebGL knows how to draw our data
//

function initShaderProgram(gl, vsSource, fsSource) {
  const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource);
  const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource);

  
  // シェーダープログラムを作成
  
  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);
}

 最後に、ソースコードはシェーダーに渡され、そしてコンパイルされます。シェーダーのコンパイル時にエラーが発生した場合は、警告を表示して null 値を返します。エラーが発生しなかった場合は、コンパイル済みのシェーダーを呼び出し元に返します。以下のプログラムがこれを担います。



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

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



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 の寸法) に設定しています。以上のコードをつなぎ合わせて、WebGLによるグラフィックス描画のJavascript プログラムは完成します。このチュートリアルは、MDNのWebGL-Tutorialsにあります。

 HTMLは以下のようなプログラムになります。詳細は、このサイトのsample2をみてください。



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

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

  <script src="../gl-matrix.js"></script>
  <script src="webgl-demo.js"></script>
</html>


このプログラムをCodepenを用いて実行してみます。Codepen 向けにコードを若干修正しています。この画面の「Result」および「Return」をクリックすると、ウインドウに白い四角形が描かれます。

See the Pen WebGL_2 by mashyko (@mashyko) on CodePen.


 JSをクリックすると、上で説明したJavascriptのコードが表示されます。4角形を書くだけで、これだけの長さのコードが必要です。アニメーションを描画するときのことを考えると、気が遠くなります。

 最後に、アニメーションを用いた例を挙げます。ただ、立方体が回転するグラフィクスを描くWebGLのJavaScriptプログラムは非常に長いので、コードの内容を説明することは控えます。結果のみを紹介します。CodePen を利用して表示すると以下のようになります。

See the Pen webgl_5 by mashyko (@mashyko) on CodePen.

実際に描かれるグラフィックスは、このGithubのexample5を開いてブラウザで開けば,上記のようなグラフィックスが表示できます。



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>

 このファイル example.html を開けば、ブラウザに回転する立方体が描けます。ここでは、CodePenを用いて、描画してみます。画面上の「Result」をクリックすると、以下のように、立方体が回転する3Dグラフィックスが描かれます。コードはかなり単純化されています。

See the Pen threes_1 by mashyko (@mashyko) on CodePen.


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


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