プログラミング C# - 翔ソフトウェア (Sho's)

C#/.NET/ソフトウェア開発など

3D で遊ぼう ~C#er も TypeScript で楽々 WebGL~ ― 準備編

typescriptlogo.png

※ 『Hokuriku.NET Vol.13 in 富山「3D で遊ぼう ~C#er も TypeScript で楽々 WebGL~」』の資料の内容の解説。

今回は、TypeScript を使って WebGL に挑戦してみよう。

素で使うと中々に取っつき難い WebGL だが、TypeScript を使うことで比較的楽に使うことができる。 C#er の方も是非 Web での 3D プログラミングに興味を持っていただきたい。

WebGL とは

WebGL は、HTML5Canvas に三次元 (または二次元) のグラフィックを表示する標準仕様だ。

Web ブラウザーで、プラグインなしで動作する。

WebGL 動作環境

現状 WebGL は、PC 上のブラウザーでは、最新の IEGoogle ChromeFireFoxでは、ほぼ動作する。但し全てが動くとは限らない。

モバイルで動作するものは多くない。

モバイル環境の WebGL 対応状況
Platform iPhone, iPad Phones & Tablet Android 4.0+ Kindle Fire Phones BB10 Tablet MeeGo - N9 Symbian Windows Phone Windows 8 Android & Symbian Java,iOS Android Android, MeeGo* Firefox OS
WebGL
3D Canvas for the web
 
Specific
device

30+
   
 

2.0+
     
11+

12+ (android)
 
 

 

Mobile HTML5 compatibility on iPhone, Android, Windows Phone, BlackBerry, Firefox OS and other mobile devices より引用。

Windows RT タブレットでは動作する。

WebGL プログラミング

WebGL のプログラミングには、シェーディング言語である GLSL (OpenGL 2.0) と JavaScript を用いる。

次の図のような感じだ。左側が GLSL、右側が JavaScript による部分だ。

GLSL と JavaScript による WebGL プログラミング
GLSL と JavaScript による WebGL プログラミング

慣れていない人には、結構大変だ。

three.js

そこで、ここでは three.js という JavaScript のライブラリを用いることによる。

three.js は、WebGL のラッパーで、WebGL を抽象化して易しく使えるようにしてくれるのだ。

次の場所で入手することができる。

ダウンロードし、three.js または three.min.js というファイルを HTML ファイルから参照すれば OK だ。

three.d.ts

three.js を TypeScript から使う為には、型定義ファイルの three.d.ts も必要だ。

こちらは、次の場所で入手することができる。

または、NuGet で入手することができる。

Visual Studio で NuGet を検索してインストールすることも可能だ。

Visual Studio で NuGet の three.d.ts を検索
Visual Studio で NuGet の three.d.ts を検索

この three.d.ts を自分の TypeScript ファイルから参照する。

three.js による 3D プログラミング

では three.js で 3D プログラミングを始めてみよう。

ここでは、次のような順序で進めていく。

  • 0. 準備
  • 1. WebGL レンダラーの作成
  • 2. カメラの作成
  • 3. シーン (空間) の作成
    • 3.1 ライト (光源) の作成 → シーンに追加
    • 3.2 メッシュの作成 ― ジオメトリー (形) とマテリアル (材料) からメッシュを作成 → シーンに追加
  • 4. レンダリング ― レンダラーとカメラでシーンをレンダリング
0. 準備
  • 0.1 『[TypeScript] TypeScript の特長』を参考に、Visual Studio で「TypeScript を使用した HTML アプリケーション」を作成する。
  • 0.2 three.min.js と three.d.ts をプロジェクトのフォルダーに置く (ここではプロジェクトのフォルダーにその儘フラットに置いている)。
  • 0.3 threejswebglsample.ts というファイル名の TypeScript ファイルをプロジェクトに追加する。
  • 0.4 threejswebglsample.ts の一行目に、
    /// <reference path="three.d.ts"/>
    という1行を書き、three.d.ts を参照する
    (three.d.ts が threejswebglsample.ts と異なるフォルダーにある場合は、相対パスで書く)。
  • 0.5 threejswebglsample.html というファイル名の HTML ファイルをプロジェクトに追加する。

threejswebglsample.html では、three.min.js と threejswebglsample.ts から生成される threejswebglsample.js を参照する。

次のようにする。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="utf-8" />
    <script src="three.min.js"></script>
    <script src="threejswebglsample.js"></script>
    <title>THREE.js で WebGL</title>
</head>
<body>
    <div id="viewport"></div>
</body>
</html>

中に viewport という id の div があるが、後で three.js によって、この中に Canvas を作る。

1. WebGL レンダラーの作成

レンダラーを作成する TypeScript のコードは次のようになる。

    // 1. WebGL レンダラーを作成
    try { var renderer = new THREE.WebGLRenderer({ antialias: true }); } catch (e) {}
    if (renderer == null) {
        alert("お使いの環境では WebGL はご利用いただけません。");
        return;
    }
    // サイズの設定
    renderer.setSize(window.innerWidth, window.innerHeight);
    // 背景色の設定(色, 透明度)
    renderer.setClearColorHex(0x000000, 1);
    // レンダラーによって、viewport の ID を持つ HTML 要素に子要素として canvas を追加
    document.querySelector("#viewport").appendChild(renderer.domElement);

これで描画する準備ができた。

1. WebGL レンダラーの作成
1. WebGL レンダラーの作成
2. カメラの作成

次にカメラを作成する。

     // 2. カメラを作成
    // (透視投影の) カメラを作成
    var fov                   = 100;                                     // 画角
    var aspect                = window.innerWidth / window.innerHeight;  // 縦横比
    var camera                = new THREE.PerspectiveCamera(fov, aspect);
    camera.position           = new THREE.Vector3(0, 0, 1000);           // z 方向に 1000 ずらす

カメラは、z 方向に 1000 ずらした位置に置くことにする。

2. カメラの作成
2. カメラの作成
3. シーン (空間) の作成

シーン (空間) を作成する。 シーンには、後でライト (光源) を置いたり、メッシュと呼ばれるオブジェクトを配置したりする。

    // 3. シーン (空間) を作成
    var scene                 = new THREE.Scene();

シーン (空間) が作成された。

3. シーン (空間) の作成
3. シーン (空間) の作成
3.1 ライト (光源) の作成 → シーンに追加

ライト (光源) を作成し、シーンに追加する。

ライトには幾つかの種類があるが、ここでは太陽光のような平行光源 (無限遠光源) を作成する。

    // 3.1 ライト (光源) を作成 → シーンに追加
    // 平行光源 (無限遠光源) を作成 
    var directionalLight      = new THREE.DirectionalLight(0xffffff, 1); // 引数: 色, 強さ
    directionalLight.position = new THREE.Vector3(0, 0, 1);              // z 方向から照らす
    // 光源をシーンに追加
    scene.add(directionalLight);

シーン (空間) にライト (光源) が置かれた。

3.1 ライト (光源) の作成 → シーンに追加
3.1 ライト (光源) の作成 → シーンに追加
3.2 メッシュの作成 ― ジオメトリー (形) とマテリアル (材料) からメッシュを作成 → シーンに追加

ジオメトリー (形) とマテリアル (材料) からメッシュを作成し、シーンに追加する。

three.js では、予めプリミティブ (基本的な) ジオメトリー (形) が幾つか用意されている。ここでは、その中から直方体を作成する。

また、マテリアル (材料) も幾つもの種類が用意されているが、ここでは単純な色だけを持ったものを用いる。

    // 3.2 メッシュを用意 → シーンに追加
    // ジオメトリー (形) とマテリアル (材料) からメッシュを作成

    // プリミティブなジオメトリーを作成
    var geometry = new THREE.CubeGeometry(500, 500, 500);
    // マテリアルを作成 (赤)
    var material = new THREE.MeshLambertMaterial({ color: 0xff0000 });

    // ジオメトリーとマテリアルを合わせてメッシュを作成
    var cubeMesh = new THREE.Mesh(geometry, material);
    // メッシュをシーンに追加
    scene.add(cubeMesh);

これで、シーン (空間) にメッシュが置かれた。

3.2 メッシュの作成 ― ジオメトリー (形) とマテリアル (材料) からメッシュを作成 → シーンに追加
3.2 メッシュの作成 ― ジオメトリー (形) とマテリアル (材料) からメッシュを作成 → シーンに追加
4. レンダリング ― レンダラーとカメラでシーンをレンダリング

最後にレンダリングを行うと描画される。

    // 4. レンダリング - レンダラーとカメラでシーンをレンダリング
    renderer.render(scene, camera);

ここまでをクラスのメソッドに入れ、実行できるようにしてみよう。threejswebglsample.ts は、こうなる。

/// <reference path="three.d.ts"/>

module ThreeJSWebGLSample {
    export class Application {
        static run() {
            // 1. WebGL レンダラーを作成
            try { var renderer = new THREE.WebGLRenderer({ antialias: true }); } catch (e) {}
            if (renderer == null) {
                alert("お使いの環境では WebGL はご利用いただけません。");
                return;
            }
            // サイズの設定
            renderer.setSize(window.innerWidth, window.innerHeight);
            // 背景色の設定(色, 透明度)
            renderer.setClearColorHex(0x000000, 1);
            // レンダラーによって、viewport の ID を持つ HTML 要素に子要素として canvas を追加
            document.querySelector("#viewport").appendChild(renderer.domElement);

            // 2. カメラを作成
            // (透視投影の) カメラを作成
            var fov                   = 100;                                     // 画角
            var aspect                = window.innerWidth / window.innerHeight;  // 縦横比
            var camera                = new THREE.PerspectiveCamera(fov, aspect);
            camera.position           = new THREE.Vector3(0, 0, 1000);           // z 方向に 1000 ずらす

            // 3. シーン (空間) を作成
            var scene                 = new THREE.Scene();

            // 3.1 ライト (光源) を作成 → シーンに追加
            // 平行光源 (無限遠光源) を作成 
            var directionalLight      = new THREE.DirectionalLight(0xffffff, 1); // 引数: 色, 強さ
            directionalLight.position = new THREE.Vector3(0, 0, 1);              // z 方向から照らす
            // 光源をシーンに追加
            scene.add(directionalLight);

            // 3.2 メッシュを用意 → シーンに追加
            // ジオメトリー (形) とマテリアル (材料) からメッシュを作成

            // プリミティブなジオメトリーを作成
            var geometry = new THREE.CubeGeometry(500, 500, 500);
            // マテリアルを作成 (赤)
            var material = new THREE.MeshLambertMaterial({ color: 0xff0000 });
            // ジオメトリーとマテリアルを合わせてメッシュを作成
            var cubeMesh = new THREE.Mesh(geometry, material);
            // メッシュをシーンに追加
            scene.add(cubeMesh);

            // 4. レンダリング - レンダラーとカメラでシーンをレンダリング
            renderer.render(scene, camera);
        }
    }
}

// ウィンドウがロードされた時
window.onload = () => {
    // アプリケーションの起動
    ThreeJSWebGLSample.Application.run();
};
実行 その 1

コンパイルして threejswebglsample.js を生成し、threejswebglsample.html を Internet Explorer 11 以降等の WebGL 対応の Web ブラウザーで表示してみよう。

threejswebglsample.html
threejswebglsample.html

赤い四角形が表示された。

実際には、立方体なのだが、真横から見ている為単なる正方形に見える。

実行 その 2

これでは面白くないので、アニメーションをさせてみよう。

アニメーションをさせる為に次のようなコードでレンダリングしてみる。 メッシュの角度を少しずつ変化させるコードを requestAnimationFrame で再帰的に呼び出す。

これで立方体が少しずつ回転する。

    static rendering(renderer: THREE.Renderer, camera: THREE.Camera, scene: THREE.Scene, cubeMesh: THREE.Mesh) {
        // メッシュを自転
        cubeMesh.rotation.x += 0.01;
        cubeMesh.rotation.y += 0.01;

        // レンダリング
        renderer.render(scene, camera);
        // 繰り返す
        // - setInterval(render, 1000 / 60); より軽い
        // -- 不要な場合にループを行わない
        // -- 最適化されている
        requestAnimationFrame*1;
    }

このコードを追加すると、threejswebglsample.ts 全体は次のようになる。

/// <reference path="three.d.ts"/>

module ThreeJSWebGLSample {
    export class Application {
        static run() {
            // 1. WebGL レンダラーを作成
            try { var renderer = new THREE.WebGLRenderer({ antialias: true }); } catch (e) {}
            if (renderer == null) {
                alert("お使いの環境では WebGL はご利用いただけません。");
                return;
            }
            // サイズの設定
            renderer.setSize(window.innerWidth, window.innerHeight);
            // 背景色の設定(色, 透明度)
            renderer.setClearColorHex(0x000000, 1);
            // レンダラーによって、viewport の ID を持つ HTML 要素に子要素として canvas を追加
            document.querySelector("#viewport").appendChild(renderer.domElement);

            // 2. カメラを作成
            // (透視投影の) カメラを作成
            var fov                   = 100;                                     // 画角
            var aspect                = window.innerWidth / window.innerHeight;  // 縦横比
            var camera                = new THREE.PerspectiveCamera(fov, aspect);
            camera.position           = new THREE.Vector3(0, 0, 1000);           // z 方向に 1000 ずらす

            // 3. シーン (空間) を作成
            var scene                 = new THREE.Scene();

            // 3.1 ライト (光源) を作成 → シーンに追加
            // 平行光源 (無限遠光源) を作成 
            var directionalLight      = new THREE.DirectionalLight(0xffffff, 1); // 引数: 色, 強さ
            directionalLight.position = new THREE.Vector3(0, 0, 1);              // z 方向から照らす
            // 光源をシーンに追加
            scene.add(directionalLight);

            // 3.2 メッシュを用意 → シーンに追加
            // ジオメトリー (形) とマテリアル (材料) からメッシュを作成

            // プリミティブなジオメトリーを作成
            var geometry = new THREE.CubeGeometry(500, 500, 500);
            // マテリアルを作成 (赤)
            var material = new THREE.MeshLambertMaterial({ color: 0xff0000 });
            // ジオメトリーとマテリアルを合わせてメッシュを作成
            var cubeMesh = new THREE.Mesh(geometry, material);
            // メッシュをシーンに追加
            scene.add(cubeMesh);

            // 4. レンダリング - レンダラーとカメラでシーンをレンダリング
            Application.rendering(renderer, camera, scene, cubeMesh);
        }

        static rendering(renderer: THREE.Renderer, camera: THREE.Camera, scene: THREE.Scene, cubeMesh: THREE.Mesh) {
            // メッシュを自転
            cubeMesh.rotation.x += 0.01;
            cubeMesh.rotation.y += 0.01;

            // レンダリング
            renderer.render(scene, camera);
            // 繰り返す
            // - setInterval(render, 1000 / 60); より軽い
            // -- 不要な場合にループを行わない
            // -- 最適化されている
            requestAnimationFrame*2;
        }
    }
}

// ウィンドウがロードされた時
window.onload = () => {
    // アプリケーションの起動
    ThreeJSWebGLSample.Application.run();
};

コンパイルして表示してみよう。

threejswebglsample.html
threejswebglsample.html

今度は、立方体が回っているのが分かるだろう。

実行 その 3

試しに、マテリアル (材料) を換えてみよう。 例えば、

    // マテリアルを作成 (赤)
    var material = new THREE.MeshLambertMaterial({ color: 0xff0000 });

の部分を、

    // マテリアルを作成 (赤)
    var material = new THREE.MeshPhongMaterial({ color    : 0xff0000,
                                                 specular : 0xcccccc,
                                                 shininess: 50      ,
                                                 ambient  : 0xffffff });

に変えてコンパイルし、表示してみよう。

threejswebglsample.html
threejswebglsample.html

先程のものより艶のある立方体が表示された筈だ。

実際のサンプルを次の場所に用意した。

マテリアル (材料) を変えたり、ジオメトリー (形) を立方体以外にしたり、ライト (証明) を変えたり、アニメーションを変えたり、色々と試してみてほしい。 3D のものが表示されるのは楽しいものだ。

次回は、別のサンプルをご紹介する。

*1:) => Application.rendering(renderer, camera, scene, cubeMesh

*2:) => Application.rendering(renderer, camera, scene, cubeMesh