HOME > natural science Laboratory > コンピュータ・シミュレーション講座 > 仮想物理実験室

HTML5による物理シミュレーション環境の構築 ~WebGLライブラリThree.js 入門(1/3)~

文責:遠藤 理平 (2012年2月20日) カテゴリ:OpenGL入門(22)TIPS 集(83)WebGL(37)仮想物理実験室(167)

本稿では新しいWEBページ用の言語であるHTML5+WebGLを利用して、 ウェブブラウザ上で、物理シミュレーションの計算と描画をリアルタイムに実行する「物理シミュレータ」の作成までの手順を記述します。 本稿では、物理シミュレータの例として、任意の初期条件に対する2重振り子の時間発展を4次のルンゲクッタ法を計算する「2重振子シミュレータ」を構築します。 また、本稿では WebGL を簡単に利用することができるWebGLライブラリとして著名な Three.js を用いています。 物理や工学とは無関係に、初めて Three.js を利用する人がチュートリアルとしても利用できることも意図しております。

出版決定(発売日:2013年2月22日)

本コンテンツの内容をベースに、Three.js、jqPlot、物理学の基礎から、物理シミュレーションの意義、仮想物理実験室の構築までをまとめた本を執筆しました。 本書の詳しい内容(目次、サンプルプログラムリスト)はこちらをご覧ください

978-4-87783-303-9.png

HTML5による物理シミュレーション
JavaScriptでThree.js/jqPlot/jQuery UIを使う

著者 遠藤 理平
出版社 株式会社カットシステム
発売日 2013年2月22日
判型 B5変型判、392頁
税込価格 3,990円(本体 3,800円)
ISBN 978-4-87783-303-9

さらに、
フジテレビ月9ドラマ【ガリレオ】HTML5による物理シミュレーション&動画の提供決定!

続編・出版決定(発売日:2013年5月25日)

978-4-87783-303-9.png

HTML5による物理シミュレーション【拡散・波動編】
JavaScriptライブラリとCanvas 2D Context/Web Workersを使う

著者 遠藤 理平
出版社 株式会社カットシステム
発売日 2013年5月25日
判型 B5変型判、456頁
税込価格 4,410円(本体4,200円)
ISBN 978-4-87783-312-1
本書の詳細(はじめに、目次)につきましてはこちらをご覧ください

目次

パート1

パート2 (すべて表示

パート3

HTML5とWebGL

初めにHTML5の位置づけについて簡単に触れます。HTML5とはこれまでのWebページ作成言語で主流だったHTML4やXHTMLの後継言語のことで、 2008年に草案がまとめられ、2014年までにウェブブラウザ各社への正式勧告を目指して策定が進められています。 HTML5は 2012年1月でもまだ「草案」段階で、仕様も流動的な準備段階であるが、開発側からもユーザ側からも非常に注目が集められています。 その大きな理由の一つに挙げられているのが、iPhoneやAndroidなどのスマートフォンの台頭による情報端末の多様化への対応のためです。つまり、様々なWEBコンテンツのクロスプラットフォーム化への重要な貢献が期待されているからです。 というのもスマートフォンの登場までは、Adobe社が提供する FLASH が動画やオーディオなどが組み合わされたマルチメディアコンテンツの実行環境としデファクトスタンダードの立ち位置にあり、FLASH のプラグインを導入することで種々のOSやウェブブラウザ上で閲覧することができるため、事実上クロスプラットフォーム実行環境が実現できていたわけですが、iPhone(やiPad)上でFLASHの実行を認めないApple社によりこのクロスプラットフォームの環境が崩れてしまいました。 その中で、新たなクロスプラットフォーム実行環境として白羽の矢が立てられたのが、WEB標準化団体であるW3Cが取りまとめている次世代WEBページ作成言語であるHTML5です。 HTML5はこれまで単に文章を構造化するための言語から、動画やオーディオ、さらにはビットマップデータを直接扱えるように進化しました。 つまり、プラグインなどを導入することなしに、スマートフォンを含む多くの環境でクロスプラットフォーム環境を実現することができるわけです。このWEBコンテンツのクロスプラットフォーム環境の構築は、WEB業界で重要かつ迅速に求められた結果、まだ「草案」段階でしかないHTML5の仕様を各ウェブブラウザが競って導入している理由です。2012年2月段階で、HTML5はスマートフォンで利用されているウェブブラウザでほぼ100パーセント利用可能とのことです(パソコン用でも各ウェブブラウザの最新のバーションをで利用可能です)。 ちなみに、HTML5は単体ではなく情報の視覚的体裁を指定するCSS3とウェブブラウザで実装されるスクリプト言語であるJavaScriptと組み合わせることで真価を発揮することになります。

HTML5の新要素「canvas要素」

HTML5では、新しく登場した canvas 要素を用いて予め確保した領域に、色をピクセル単位で指定することができるようになりました。 これは2次元や3次元グラフィックをプラグインなしにHTML上に直接描画することができることを意味します。 任意のピクセルに対する色の指定には、ウェブブラウザ上で演算することができる JavaScript を用い、 これで原理的にはどのような3次元グラフィックも描画することが可能となります。 「原理的」と修飾したのは、実際に3次元グラフィックをディスプレイに描画するには、3次元空間内のオブジェクトを2次元平面上に投影する必要があるわけですが、これを実行するには高度な計算を必要とするため、一般的に難しいのが現実です。

OpenGL のウェブブラウザ版 WebGLの登場

2次元・3次元のコンピュータグラフィックス技術は、各種技術の標準化を目指す Khronos Groupによって策定されている「OpenGL」と呼ばれる API が、Windows、Mac、Linux上の種々の環境で動作するクロスプラットフォーム技術として長らく活躍しています。 OpenGL は、仮想的に作られた3次元空間上の構造物を、任意の視点から2次元に射影するための演算を自動で行うことができ、 さらにコンピュータのグラフィックデバイスに直接司令を与えることができるため、非常に高速かつ高精度な3次元画像を描画することができることが最大の特徴です。そんな OpenGL の技術をウェブブラウザで実現するための規格が、2009年、OpenGL 同様 Khronos Groupによって策定されたのが「WebGL」です。WebGL は OpenGL と JavaScript との間の子で、HTML5 の canvas 要素に、OpenGLと同様の API を利用することで、精密な3次元グラフィックを描画することができるようになりました。 しかしここで問題が立ちはだかります。ウェブブラウザで最大のシェアを占めるInternetExploreが WebGL とは真っ向反対の立場をとっています。 なぜならば、もともと OpenGL は InternetExplore を開発するMicrosoft社が独自に開発した3次元グラフィックAPIである 「DirectX」とライバル関係にあるため、WebGL を認めるわけにはいかないのです。 しかしながら、DirectX は Microsoft社関連の製品でしか動作しないため、クロスプラットフォーム環境の主役には成り得ず、 さらにはウェブブラウザ上で WebGL を超える描画性能を示すことができていない以上、勝敗は明らかであると考えられます。 WebGLの実行環境で注意が必要なのは、ウェブブラウザがWebGLに対応していることと、パソコンに搭載されているグラフィックカードの OpenGL のバージョンが2.0以上である必要がある点です。 現時点(2012年2月現在)で GoogleChromeやFireFox、Safariなど主要ブラウザの最新バージョンで利用することができますが、古いパソコン(5年前程度)では、グラフィックカードが対応していないのが現状です。また、スマートフォンなどでのWebGLの対応は現在(2012年2月)準備中の段階であるようです。スマートフォンなどでWebGLが利用できるようになると、一気にウェブブラウザ用3次元グラフィックAPIとしての地位を築くことと思われます。

WebGLの例

WebGLの例として非常に有名なのが、Human Engines氏とGregg Tavares氏によって2010年末に発表された「Aquarium」です。下図はその画面キャプチャですが、WebGLに対応しているブラウザでご覧になって下さい。

こんな精密な3次元グラフィックがウェブブラウザで、もたつかずに動作することに驚きです。 最近では、学習などの実用面を重視した、化学構造の3次元描画ライブラリ「ChemDoodle」(下図左)3次元人体解剖図「BIODIGITAL HUMAN」(下図右)なども続々と現れています。昨今の教育業界で議論されている教材のデジタル化の流れは、このようなデジタルコンテンツの出現により一層進んでいくことが考えられます。

WebGLの利用方法

WebGLは、Javascriptで記述された WebGL 用の行列演算ライブラリ「glMatrix.js」を読み込み、所定プログラムを記述することで利用することができます。 2009年末、Giles氏によってWebGLを利用するためのチュートリアル「Learning WebGL」が発表され、WebGLの利用手順がわかりやすくなりました。WebGLをネイティブに扱うことで高性能ゲーム専用機並の描画が可能ということですが、とは言ってもOpenGLに精通している人でなければ非常に厳しい状況が続いていました。 そんな中、2010年末にWebGLを簡単に利用することができることを目指したライブラリ「Three.js(Mr.doob 氏)」が発表されました。Three.js は非常に使いやすく、気軽にWebGLを試すことができるため非常に人気があります。 発表当初は週1回程度、2012年2月でも1.5ヶ月に1回程度のバージョンアップを重ねていて、今後のさらなる発展も期待されます。 Three.js もまた JavaScriptで記述された「Three.js」を読み込むことで利用することができます。 本稿の目的である3次元物理シミュレーションの描画も Three.js を利用することにします。 WebGLのライブラリはThree.jsの他にも「J3D」や「SceneJS」や、日本製では「gl.enchant.js β版(株式会社ユビキタスエンターテインメント)」などがあります。


3次元物理シミュレーション:2重振子シミュレータの作成

本稿では、WebGLライブラリ Three.js による3次元物理シミュレーションの実例として、「2重振り子シミュレータ」を作成します。 WebGLに対応したブラウザでこちらをご覧ください。下図は「2重振り子シミュレータ」のキャプチャです。

本シミュレータの特徴は、以下のとおりです。

(1)直交座標系で記述した2重振子の時間発展を4次のルンゲクッタを用いてJavaScriptで計算
(2)計算結果を WebGL を用いて canvas 要素にリアルタイムに描画 (canvas要素はjavascriptで動的に生成されています)
(3)グラフ描画ライブラリ flotr2 を利用して、張力の時間発展をグラフ化(※1)
(4)シミュレーションの時刻[s]に対する、系のエネルギー[J]、球の位置[m]と速度[m/s]、棒の長さ[m]と張力[N]を表示(※2, ※3)
(5)マウスドラックで視点を変更可能
(6)任意の初期値に対する再スタート可能(※4)
【計算アルゴリズム】2重振子のC++プログラムソース(直交座標系+ルンゲクッタ)

(※1)Javascriptを用いたグラフの描画は、「flotr2」と呼ばれるライブラリを利用しています。
(※2)系のエネルギーや棒の長さは本来一定ですが、数値誤差により少しずつずれていきます。
(※3)本計算アルゴリズムは張力を計算するため、Lagrange 未定乗数法を用いています。
(※4)拘束に矛盾する方向への初速度を与えると正しいシミュレーションが行われません。棒の長さが変化してしまいます。

HMTLによるユーザインターフェースとWebGLによる3次元グラフィックスを用いることで、簡単に物理シミュレータを作成することができます。 これはOS上でネイティブに作動するアプリケーションを作成するよりも圧倒的に簡単です。 本稿では、この「2重振子シミュレータ」の作成までのチュートリアルを記述します。

環境の準備

1.テキストエディタ(何でもいいです)
2.WebGLが動作するウェブブラウザ(推奨:Google Chrome ver.17)
3.デバッカー(推奨:Google Chrome)
4.WebGL ライブラリ Three.js

本稿の内容は次の環境で動作確認を行なっております。
OS:Windows7 Professional
ブラウザ:Google Chrome(ver.17)
GPU:NVIDIA 550i(OpenGL 4.2対応) Three.jsのバージョン:r47

Three.jsのダウンロード

Three.jsのダウンロードは、「mrdoob/three.js - GitHub」にアクセスし、下図の「ZIP」をクリックして全ファイルをダウンロードします。Three.js を使用する際には「build」フォルダの中にある「Three.js」だけで十分ですが、サンプルも同包されているので、暇な時に見てみるとインスピレーションが湧いてくることと思います。

「build」フォルダの中にある「Three.js」を作業フォルダにコピーしてください。これで準備完了です。

Three.jsの動作確認

Three.js の動作確認のついでに、直方体を描画します。 テキストエディタを起動し、次のソースをコピー&ペーストして、作業フォルダに保存してください。

サンプル1:tutorial1.html(視点と光源の設定、立方体の描画)

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Three.js チュートリアル1</title>
<script src="Three.js"></script>
<style type="text/css">
div#canvas-frame{
  border: none;
  cursor: pointer;
  width: 600px;
  height: 600px;
  background-color: #EEEEEE;
}
</style>
<script>
  var renderer;
  function initThree() {
    width = document.getElementById('canvas-frame').clientWidth;
    height = document.getElementById('canvas-frame').clientHeight;  
    renderer = new THREE.WebGLRenderer({antialias: true});
    renderer.setSize(width, height );
    document.getElementById('canvas-frame').appendChild(renderer.domElement);
    renderer.setClearColorHex(0xFFFFFF, 1.0);
  }
  
  var camera;
  function initCamera() {  
    camera = new THREE.PerspectiveCamera( 45 , width / height , 1 , 10000 );
    camera.position.x = 100;
    camera.position.y = 20;
    camera.position.z = 50;
    camera.up.x = 0;
    camera.up.y = 0;
    camera.up.z = 1;
    camera.lookAt( {x:0, y:0, z:0 } );
  }
  var scene;
  function initScene() {    
    scene = new THREE.Scene();
  }
  var light;
  function initLight() {  
    light = new THREE.DirectionalLight(0xFF0000, 1.0, 0);
    light.position.set( 100, 100, 200 );
    scene.add(light);
  }
  var cube;
  function initObject(){   
    cube = new THREE.Mesh(
         new THREE.CubeGeometry(50,50,50),                //形状の設定
         new THREE.MeshLambertMaterial({color: 0xff0000}) //材質の設定
    );
    scene.add(cube);
    cube.position.set(0,0,0);
  }
  function threeStart() {
    initThree();
    initCamera();
    initScene();    
    initLight();
    initObject();
    renderer.clear();  
    renderer.render(scene, camera);
  }
</script>
</head>

<body onload="threeStart();">
<div id="canvas-frame"></div>
</body>
</html>

実行結果は次のとおりです。 WebGLに対応しているウェブブラウザで閲覧すると赤色の直方体が描画されることが分かります。

Three.js の説明に入る前に、上記ソースの構造について触れます。 ウェブブラウザは「<html>」タグで囲われた領域(「<html>」と「</html>」の間)に記述された文書をHTML文書とみなします。 HTML文書を構成する上で重要な要素は「head要素」と「body要素」の2つです。 この2つがなぜ重要かというと、XHTML登場以降のHTML文書は、文書の論理構造とウェブページとしての見た目の記述が明確に分離されることが求められるているからです。 つまり、body要素の中には文書のメインの内容を論理構造を表す要素(見出しを表す「h1」要素や段落を表す「p」要素など)を的確にマークアップ(タグで囲む)しながら記述し、head 要素の中には、ウェブページの見た目を指定する CSS を記述するための「style」要素や WebGL を利用するための JavaScript を記述する「script」要素などを配置します。 上記サンプルの模式図は次のとおりです。

本稿で扱うHTML文書は、WebGLの描画がメインなので body要素の中には描画用の領域を指定するために用意した「div」要素ひとつだけを記述してあります。WebGL は canvas 要素で確保された領域に描画されることは本稿の序文で触れましたが、body 要素の中に canvas 要素は見当たりません。 これは Three.js を含む多くのライブラリでは canvas 要素を動的に生成してくれるためです。本稿では自動生成される canvas 要素を、キャンバスの額縁の役割を担わす「canvas-frame」と名付けた文書構成の任意のまとまり表す「div 要素」の中に配置することにします。

また、「body」タグに「onload="threeStart();"」という記述があるのが確認できます。 これは、ウェブページ読み込み完了時に実行するJavascript の関数をしています。 HTMLには特定のイベント時(「onload」の場合はウェブページ読み込み完了というイベント時)に呼び出す関数が用意されています。 本稿でも後に「マウスがクリックされた時」や「フォームボタンが押された時」などのイベントに対する関数を定義して、シミュレータに利用します。

サンプル1の解説

Three.js を用いて描画する際の手順は次のとおりです。

  • 0.キャンバスの額縁の準備
  • 1.Three.js の初期化(initThree())
  • 2.カメラの準備(initCamera())
  • 3.シーンの準備(initScene())
  • 4.光源の準備(initLight())
  • 5.物体の準備(initObject())
  • 6.レンダリングの実行(threeStart())

0.キャンバスの額縁の準備

WebGLを実際に描画する領域となる自動生成される canvas要素を配置するためのキャンバスの額縁に相当する領域を準備します。 具体的には、

(1) body 要素に「canvas-frame」というidを付加した div要素を配置
(2) style 要素に 「canvas-frame」で指定した要素の視覚的情報を CSS で指定

します。

body 要素に div タグを追加

<div id="canvas-frame"></div>

style 要素に CSSで記述

文書中の id が付加された要素に対して、css では「要素+#+id名」で視覚的な情報を付加することができます。 css で「canvas-frame」で指定した要素のプロパティを次の手順で指定します。
(1) 枠線を消す
(2) マウスカーソルを「ポインター」に設定(CSS3)
(3) 確保する領域の横幅を600pxに設定
(4) 確保する領域の縦幅を600pxに設定
(5) 背景色を16進数で「#EEEEEE」と指定

<style type="text/css">
div#canvas-frame{
  border: none;      //(1)
  cursor: pointer;   //(2)
  width: 600px;      //(3)
  height: 600px;     //(4)
  background-color: #EEEEEE; //(5)
}
</style>

ここで指定した横幅(width)と縦幅(height)が、実際にWebGLで描画されるサイズとなります。 また css の基本について少し言及します。プロパティの記述する順番は任意ですが、同じプロパティが指定された場合、後に指定した値の方が優先されます。

動作確認

(3),(4),(5)を変更し、意図通りに動作するか確認してみてください。

1.レンダラーの設定

ここからscript要素の中で、JavaScript記述します。 仮想的な3次元空間における物体を2次元平面に適切に配置することを3次元レンダリングと呼びます。 一般的にレンダリングを行うソフトウェアのことはレンダラーと呼ばれます。 具体的には次の作業を行います。

(0) グローバル変数(オブジェクト)の宣言
(1) 額縁を担う「canvas-frame」で指定したの要素の横幅と縦幅の取得
(2) レンダラーオブジェクトの生成(プロパティ:アンチエイリアスを有効)
(3) レンダラーの横幅と縦幅を設定(額縁と同じサイズに設定)
(4) 「canvas-frame」で指定した要素の中に「canvas」要素を追加する
(5) レンダラーのクリアカラー(背景色とアルファ値)を設定

  var width, height;                                                           //(0)
  var renderer;                                                                //(0)
  function initThree() {
    width = document.getElementById('canvas-frame').clientWidth;               //(1)
    height = document.getElementById('canvas-frame').clientHeight;             //(1)
    renderer = new THREE.WebGLRenderer({antialias: true});                     //(2)
    renderer.setSize(width, height);                                           //(3)
    document.getElementById('canvas-frame').appendChild(renderer.domElement);  //(4)
    renderer.setClearColorHex(0xFFFFFF, 1.0);                                  //(5)
  }

ドキュメントオブジェクトモデル(DOM)を利用したプロパティの取得

Javascript などのアプリケーションから HTML文書を操作するために、ドキュメントオブジェクトモデル(DOM)と呼ばれる API が標準で用意されています。idで指定した要素のプロパティなどの取得や設定することができます。 DOMの基本形は次のとおりです。

document.getElementById('id名').プロパティ名

(1)では、DOMを利用してキャンバスの額縁の担わせる「canvas-frame」とidを付加した div要素の横幅と縦幅を取得し、変数「width」と「height」にそれぞれ代入しています。

レンダラーの宣言(プロパティ:アンチエイリアスを有効)

(2)は「WebGLRenderer」クラスのオブジェクト「renderer」をコンストラクタ(WebGLRenderer())で宣言しています。 Three.js は、JavaScriptのオブジェクト指向型言語として側面を活用して設計されていて、様々なクラスが定義されています。 宣言したオブジェクトの種々のプロパティにユーザが値を設定することで意図通りの描画を行うことができます。

var オブジェクト名 = new コンストラクタ (プロパティの初期化)

本稿では Three.js のクラス構造を理解することが目的ではないので深入りせず、クラスの利用方法に主眼を置きます。 コンストラクタとはオブジェクト宣言と同時にプロパティを初期化する関数のことで、「antialias」プロパティに「true」の値を設定しています。

アンチエイリアスとは

アンチエイリアスとは、物体の輪郭を描画するときに輪郭と背景の色との混ぜ合わせることでエッジをなめらかにすることです。 アンチエイリアスを有効にすることで、物体の輪郭がギザギザになることを抑えます。 Three.js はデフォルトでは無効になっているので、利用したければ明示的に有効にする必要があります。

動作確認

(4),(5)を変更して、アンチエイリアスやクリアカラーが意図通りに動作するか確認してみてください。

2.カメラの設定

OpenGL(WebGL)では、3次元空間上の物体を2次元平面に投影する方式に透視投影正投影の2種類のカメラが用意されています。 透視投影とは、視点から手前の物体を大きく、遠くの物体を小さく描画する方式で、通常生活における物の見え方と同じです。 一方、正投影とは物体の見た目の大きさが視点からの距離に依らず描画する方式で、建築やデザインなどの分野で物体を様々な視点から描画することが多いでよく用いられます。Three.js でも透視投影と正投影の2つの方式でカメラを設定することができます。 本稿では透視投影の方式で、次の手順で設定します。

(0) グローバル変数(オブジェクト)の宣言
(1) 透視投影によるカメラの設定
(2) カメラの位置座標の設定
(3) カメラの上を「z」軸方向に設定
(4) 視野の中心座標の設定

  var camera;                //(0)
  function initCamera() {  
    var camera = new THREE.PerspectiveCamera( 45 , width / height , 1 , 10000 ); //(1)
    camera.position.x = 100;   //(2)
    camera.position.y = 20;    //(2)
    camera.position.z = 50;    //(2)
    camera.up.x = 0;           //(3)
    camera.up.y = 0;           //(3)
    camera.up.z = 1;           //(3)
    camera.lookAt( {x:0, y:0, z:0 } ); //(4)
  }

透視投影によるカメラの設定

透視投影は、視体積と呼ばれる領域を設定することで投影図を作成します(下図)。 視体積は次の4つのパラメータで指定されます。
視野角:fov
アスペクト(縦横比):aspect
カメラから視体積の手前までの距離:near
カメラから視体積の奥までの距離:far

上記の4つパラメータを用いて透視投影によるカメラの設定を行います。 カメラの向きは上がy軸、右がx軸、奥が-z軸がデフォルトとなります。

 var camera = new THREE.PerspectiveCamera( fov , aspect , near , far );

カメラの設定も Three.js で定義された「PerspectiveCamera」クラスのオブジェクト「camera」の宣言し、 オブジェクトのプロパティに値を設定することでカメラの詳細を設定することができます。

カメラの位置座標の設定

カメラの位置座標や視野の中心座標は、(2)のようにオブジェクトのプロパティとして設定します。 上の(2)の書式は次のようなメソッドを利用することもできます。

  camera.position.set(50,50,100); //(2)

カメラの上を設定

カメラの上を(3)のようにオブジェクトのプロパティとして設定します。 デフォルトでは、

カメラの視野の中心を設定

カメラの視野の中心は「lookAt()」メソッドを利用します。 「lookAt()」の引数はプロパティに中心座標「x」「y」「z」をもつオブジェクトです。 「lookAt()」メソッドは視点の中心座標を設定するだけではなく、 ここまでで設定したプロパティを実際に設定する役割も果たします。 つまり、各プロパティを設定した後にこのメソッドを実行する必要があります。

動作確認

(1),(2),(3),(4),(5)を変更して、カメラの設定が意図通り動作するかを確認してください。

その他の投影方式

Three.js では、透視投影の他、正投影、(透視投影と正投影の)複合投影を行うカメラを実装するためのクラスがそれぞれ用意されています。

var camera = THREE.OrthographicCamera = function ( left, right, top, bottom, near, far ) //正投影
var camera = THREE.CombinedCamera = function ( width, height, fov, near, far, orthonear, orthofar ) //複合投影

3.シーンの宣言

シーンとは仮想3次元空間のことです。 「Scene」クラスのオブジェクト「scene」を宣言します。

  var scene;
  function initScene() {    
    scene = new THREE.Scene();
  }

4.光源の設定とシーンへの追加

OpenGL(WebGL)にて3次元空間を照らす光源は、点光源スポット光源の2種類があります。 さらに、点光源の特殊な例として平行光源(無限遠光源)を設定することができます。また、光源のパラメータとして「環境光」などの設定を行うことができます。 それに対して、Three.js は「点光源」「スポット光源」「平行光源」とそして「環境光」を独立に設定します。 OpenGLと同様、光源を複数個設定することもできます。 基本的には、環境光とその他の光源を組み合わせて利用します。 環境光を設定しないと光が当たっていない面が暗くなりすぎます。 本稿でははじめ平行光源だけ以下の手順で設定し、その後環境光を追加することにします。

(0) グローバル変数(オブジェクト)の宣言
(1) 平行光源の設定
(2) 光源ベクトルの設定
(3) シーンに光源を追加

  var light;                                                //(0)
  function initLight() {  
    light = new THREE.DirectionalLight(0xFF0000, 1.0, 0);   //(1)
    light.position.set( 100, 100, 200 );                    //(2)
    scene.add(light);                                       //(3)
  }

平行光源は「DirectionalLight」クラスのオブジェクト「light」を宣言し、コンストラクタを用いてプロパティの初期化を行うことで、設定することができます。

  var light = new THREE.DirectionalLight( hex, intensity)

「hex」は光源の色を16進数で指定します。デフォルトでは白色光「0xFFFFFF」です。 「intensity」は光源の強度です。デフォルトが「1」です。 また、平行光の方向はオブジェクトのプロパティで指定します。

  light.position.set( x, y, z );

光源方向ベクトルは「set()」メソッド指定した(x,y,z)から(0,0,0)への方向で、ベクトルの大きさは関係ありません。 カメラの位置座標の設定と同様「position.x, position.y, position.z」プロパティに直接代入することで指定することができます。 最後にシーンへの光源の追加は「add()」メソッドを利用します。

動作確認

(1),(2)を変更して平行光源の方向と、光源の色が意図通りに動作するかを確認してください。

※注意:3次元空間内の物体の色は光源の色だけでは決まりません。物体そのものの色とも関係があるためです。 あとでも説明しますが、この立方体の色は赤(0xFF0000)であるため、光源に緑や青の成分が存在していたとしても、 立方体の見かけの色は変わりません。つまり、この立方体の場合、光源の色を「0xFFFF00」や「0xFF0000」にしても見かけの色に変化はありません。

その他の光源

  var light = new THREE.AmbientLight( hex );                               //(1)環境光源
  var light = new THREE.PointLight( hex, intensity, distance );            //(2)点光源
  var light = new THREE.SpotLight( hex, intensity, distance, castShadow ); //(3)スポットライト光源

5.立方体の宣言とシーンへの追加

いよいよ3次元空間に物体を追加します。サンプル1ではシーンに立方体を追加します。

  var cube;                                            //(0)
  function initObject(){   
    cube = new THREE.Mesh(                             //(1)
      new THREE.CubeGeometry(50,50,50),                //(2)
      new THREE.MeshLambertMaterial({color: 0xff0000}) //(3)
    );
    scene.add(cube);                                   //(4)
    cube.position.set(0,0,0);                          //(5)
  }

(1)「Mesh」クラスのオブジェクト「cube」をコンストラクタを用いて宣言します。 コンストラクタの第1引数に「形状オブジェクト(geometory)」、第2引数に「材質オブジェクト(material)」を設定します。 形状は、Three.js であらかじ「立方体」「円錐型」「八面体」「平面型」「球型」「トーラス型」などが用意されています。 第1引数に代入する立方体を表す形状オブジェクトは「CubeGeometry」クラスのコンストラクタで宣言することができます。

var geometry = THREE.CubeGeometry ( width, height, depth, segmentsWidth, segmentsHeight, segmentsDepth, materials, sides );

コンストラクタの引数である「width」「height」「depth」は立方体の縦横高さを表し、その他のプロパティは省略可能です。 第2引数には材質オブジェクトを代入します。本稿では光源からの光を反射する材質を定義する「MeshLambertMaterial」クラスのコンストラクタで宣言したオブジェクトを代入します。

var geometry = THREE.MeshLambertMaterial( parameters );

コンストラクタの引数は連想配列です。 サンプルでは、物質の色を「color: 0xff0000」で指定しています。 また、連想配列型の引数には環境光に対する色をしてすることもできます。これについてはまた後で触れます。 (4),(5)ではシーンに立方体をシーンに追加し、位置座標を指定します。

動作確認

(2),(3),(5)を変更して立方体の大きさや色、位置が意図通りに動作するかを確認してください。

その他の形状クラス

THREE.CubeGeometry ( width, height, depth, segmentsWidth, segmentsHeight, segmentsDepth, materials, sides );//立方体
THREE.CylinderGeometry ( radiusTop, radiusBottom, height, segmentsRadius, segmentsHeight, openEnded );//円錐型
THREE.OctahedronGeometry ( radius, detail ) //八面体
THREE.PlaneGeometry ( width, height, segmentsWidth, segmentsHeight ); //平面型
THREE.SphereGeometry ( radius, segmentsWidth, segmentsHeight, phiStart, phiLength, thetaStart, thetaLength );//球型
THREE.TorusGeometry ( radius, tube, segmentsR, segmentsT, arc )//トーラス型

6.レンダリングの実行(threeStart() )

最後に、シーンに追加した物体を、設定したカメラからの見え方で実際に描画するのがレンダリングです。
「threeStart() 」はbodyタグに追加したイベント発生時の実行関数です。 つまり、ウェブページの読み込みが完了したら、この関数が呼び出されます。
(0) これまでに定義した関数を順番に実行
(1) 描画エリアをクリアカラーで塗りつぶす
(2) レンダリングする

  function threeStart() {
    initThree();                       //(0)
    initCamera();                      //(0)
    initScene();                       //(0)
    initLight();                       //(0)
    initObject();                      //(0)
    renderer.clear();                  //(1) 
    renderer.render(scene, camera);    //(2)
  }

ここまで長々とサンプルについて説明しましたが、本稿で作成する2重振子シミュレータを作成する上で、シミュレーション結果を Three.js を用いて描画する作業の重要な項目はあらかた終わります。 もう一度手順をおさらいします。

  • 0.キャンバスの額縁の準備
  • 1.Three.js の初期化(initThree())
  • 2.カメラの準備(initCamera())
  • 3.シーンの準備(initScene())
  • 4.光源の準備(initLight())
  • 5.物体の準備(initObject())
  • 6.レンダリングの実行(threeStart())

以上の手順のうち、「カメラの設定」や「光源の設定」は「レンダリングの実行」の前までに終わらせればいいので、順番を変更することは可能です。

少しだけ応用(平面と球の追加)

上のサンプルに平面と球を追加します。

  var cube, sphere, plane;
  function initObject(){   
    cube = new THREE.Mesh(
         new THREE.CubeGeometry(50,50,50),                
         new THREE.MeshLambertMaterial({color: 0xff0000})
    );
    scene.add(cube);
    cube.position.set(0,-50,0);
  
    sphere = new THREE.Mesh(
      new THREE.SphereGeometry(20,20,20),              
      new THREE.MeshLambertMaterial({color: 0x00ff00}) 
    );
    scene.add(sphere);
    sphere.position.set(0,0,0);
  
    plane = new THREE.Mesh(
      new THREE.PlaneGeometry(50, 50),                 
      new THREE.MeshLambertMaterial({color: 0x0000ff}) 
    );
    scene.add(plane);
    plane.position.set(0,50,0);    
  }

実行結果


Three.js によるアニメーション

WebGL によるアニメーションは、テレビ等のアニメーションと同様に静止画(フレーム)を連続して描画することで実現します。 ウェブブラウザには指定した時間間隔で Javascript の関数を呼び出す仕組みがあります。 これにより、最大で 60fps のフレームレートで呼び出しが可能となります(fpsとは1秒あたりのフレーム数)。 もちろん呼び出された関数の実行に時間がかかるほど、フレームレートが下がります。 それでは具体的に、どのようにしてアニメーションを実行するのかを示します。

「window.requestAnimationFrame」関数を用いたアニメーション

「window.requestAnimationFrame」関数は、一定間隔で指定の関数を呼び出すことのできる関数の一つです。 この関数は、閲覧しているウェブブラウザがアクティブ(そのページを閲覧中)の時だけ実行することが特徴です。 つまり、ウェブブラウザを最小化や異なるタブで他ページを閲覧している最中(非アクティブ)の場合は静止しているため、省エネとなります。 利用の仕方は、引数に呼び出す関数名を入力するだけです。

  function loop() {
    (処理)
    window.requestAnimationFrame(loop);
  }  

上の例を見れば分かる通り、「loop()」関数の中で、「window.requestAnimationFrame」関数を用いて 「loop()」関数を呼び出しています。つまり、無限ループを形成していることになります。 ただし、無限ループを実行するには、「loop()」関数を外から一度呼び出す必要があります。

  function threeStart() {
    (処理)
    loop();
  }

これにより、連続描画を行うことでアニメーションを実現することができるのです。

サンプル2:tutorial2.html(アニメーションの実装)

次の Javascript ソースをコピーアンドペーストして、実行してください。 Javascript 以外はサンプル1と同じです。

  var width, height;
  var renderer;
  function initThree() {
    width = document.getElementById('canvas-frame').clientWidth;
    height = document.getElementById('canvas-frame').clientHeight;  
    renderer = new THREE.WebGLRenderer({antialias: true});
    renderer.setSize(width, height );
    document.getElementById('canvas-frame').appendChild(renderer.domElement);
    renderer.setClearColorHex(0xFFFFFF, 1.0);
  }
  
  var camera;
  function initCamera() {  
    camera = new THREE.PerspectiveCamera( 45 , width / height , 1 , 10000 );
    camera.position.x = 400;
    camera.position.y = 20;
    camera.position.z = 50;
    camera.up.x = 0;
    camera.up.y = 0;
    camera.up.z = 1;
    camera.lookAt( {x:0, y:0, z:0 } );
  }
  var scene;
  function initScene() {    
    scene = new THREE.Scene();
  }
  var light;
  function initLight() {  
    light = new THREE.DirectionalLight(0xFF0000, 1.0, 0);
    light.position.set( 100, 100, 200 );
    scene.add(light);
  }
  var cube = Array();
  function initObject(){
    for(var i=0; i<3; i++){
      cube[i] = new THREE.Mesh(
           new THREE.CubeGeometry(50,50,50),                //形状の設定
           new THREE.MeshLambertMaterial({color: 0xff0000}) //材質の設定
      );
      scene.add(cube[i]);
      cube[i].position.set(0,-100+100*i,0);
    }
  }
  var t=0;
  function loop() {
    t++;
    cube[0].rotation.set( t/100, 0, 0 );
    cube[1].rotation.set( 0, t/100, 0 );
    cube[2].rotation.set( 0, 0, t/100 );
    renderer.clear();
    renderer.render(scene, camera);
    window.requestAnimationFrame(loop);
  }  
  function threeStart() {
    initThree();
    initCamera();
    initScene();    
    initLight();
    initObject();
    loop();
  }

サンプル2の実行結果は次のとおりです。 立方体を3つ配置し、「x」「y」「z」軸で回転させています。

サンプル2の解説

サンプル1と比較して新しいのは3つです。

(1) オブジェクトの配列と参照
(2) 「loop()」関数の定義と実行
(3) 物体の回転角を指定するメソッドの追加

オブジェクトの配列と参照

サンプル2の立方体は配列を用いています。 Javascript で配列は

  var cube = Array();

で宣言することができます。 C言語のように配列のサイズを予め決める必要はありません。 サンプル2では、配列に「Mesh」クラスのオブジェクトをfor文を用いて代入しています。 また、配列に代入したオブジェクトのプロパティやメソッドを参照する場合は、

  配列名[識別子].position.set(x, y, z);

のように、配列の識別子のあとに「.(ドット)」演算子をつなげます。

「loop()」関数の定義と実行

「loop()」関数は、この関数の中に「window.requestAnimationFrame(loop);」を記述することで無限ループを実現することができます。 このことは本節で始めに触れました。 アニメーションを実装する際、当然ですが物体に動きがなければ静止画とおなじになってしまうので、 サンプル2では物体を回転させることにします。

物体の回転角を指定するメソッド

一般に3次元空間の物体は、位置と回転角を指定することで任意の姿勢を表現することができます。 サンプル1では、物体の位置座標を指定するメソッド「position.set(x,y,z)」について触れました。 サンプル2では、位置座標に加えて回転角を指定するメソッドについて触れます。 物体の回転を行うには、「Mesh」クラスのオブジェクトのメッソド利用します。

  オブジェクト名.rotation.set( theta_x, theta_y, theta_z );

ただし、「theta_x」「theta_y」「theta_z」はそれぞれ「x軸」「y軸」「z軸」による回転角を表します。 単位はラジアンです。 位置座標を指定する場合と同じ形式であることがわかります。 位置座標同様、プロパティに直接代入することもできます。

  オブジェクト名.rotation.x =  theta_x;
  オブジェクト名.rotation.y =  theta_y;
  オブジェクト名.rotation.z =  theta_z;

アニメーションを行う場合、値の変更をレンダリングの前に行う必要があります。 また、レンダリングの前に「renderer.clear();」を忘れずに実行する必要もあります。 もし、「renderer.clear();」を実行しなければ、前のフレームで描画した内容の上に新しく描画してしまいます。

サンプル2-2:tutorial2_2.html(カメラの移動)

前節では、アニメーションで物体の移動を実現しましたが、同様の手順でカメラ(視点)の移動も行えます。 サンプル2に加えて、カメラの移動を加えます。

  var t=0;
  function loop() {
    t++;
    renderer.clear();
    cube[0].rotation.set( t/100, 0, 0 );
    cube[1].rotation.set( 0, t/100, 0 );
    cube[2].rotation.set( 0, 0, t/100 );
    camera.position.set( 400*Math.cos(t/100), 400*Math.sin(t/200), 50*Math.cos(t/50));
    camera.lookAt( {x:0, y:0, z:0 } );    
    renderer.render(scene, camera);
    window.requestAnimationFrame(loop);
  }  

カメラが移動していることが確認できます。 ソースについてですが、Javascript では高校数学までで学習する関数が「Math」クラスのメソッドとして用意されています(一覧はこちら)。 一例を上げると

Math.PI               //\pi
Math.cos(theta)       //\cos(\theta)
Math.asin(x)          //\arcsin(x)
Math.pow(x,2)         //x^2
Math.sqrt(x)          //\sqrt{x}
Math.exp(x)           //\exp{x}

などです。 カメラの設定を変更した際には、必ず「camera.lookAt」関数をレンダリングの前までに実行する必要があります。 「camera.lookAt」関数は、視点の中心を設定しているだけではなく、カメラの設定を実際に反映するための操作も行なっているためです。

サンプル2-3:tutorial2_3.html(環境光の設定)

Three.js において、光源には直接的に物体を照らす光源(平行光源、スポットライト光源、点光源)と、間接的な光源である環境光とが存在します。 これまでは直接光だけを設定していましたが、本節では環境光の設定を行います。 まずは、環境光による物体の照射を加えたサンプルを確認してください。

サンプル2-2と比較して、直接光が当たっていない箇所も明るくなっていることが確認できます。 まず、そもそも環境光についてですが、現実の物体は光源からの直接的な照射だけではなく、その他の様々な物体からの反射された光によって照らされます。 コンピュータブラフィックの世界では、このような間接的に物体を照らす光を環境光と呼び、物体への間接的に照らす近似方法の1つとして知られています。 環境光は OpenGL では、直接的光源のパラメータとして与えますが、Three.js では光源の一つとして別途環境光を宣言します。 具体的には、平行光源に加え環境光を次の用に宣言します。

  var light, light2;
  function initLight() {  
    light = new THREE.DirectionalLight(0xFF0000, 1.0, 0);
    light.position.set( 50, 50, 200 );
    scene.add(light);
    
    light2 = new THREE.AmbientLight(0x555555);
    scene.add(light2);    
  }

環境光も直接光と同様、コンストラクタを利用してオブジェクトを宣言し、シーンに追加することで利用可能となります。 また、環境光の強さはコンストラクタの引数(上の例では:0x555555)で指定します。 例えば「0xFFFFFF」とした場合、環境光の強さはRGB全てに対して最大となります。 しかしながら、環境光で物体を照らすためには、光源の設定だけではなく物体の材質に「ambient」プロパティを設定する必要があります。

  var cube = Array();
  function initObject(){
    for(var i=0; i<3; i++){
      cube[i] = new THREE.Mesh(
           new THREE.CubeGeometry(50,50,50),
           new THREE.MeshLambertMaterial({color: 0xff0000, ambient:0xFF0000})
      );
      scene.add(cube[i]);
      cube[i].position.set(0,-100+100*i,0);
    }
  }

物体の材質を宣言する際のコンストラクタの引数に「ambient」プロパティを設定します。 このプロパティは、環境光に対する反射の大きさを RGBそれぞれに対して16進数で指定します。 つまり、実際に物体が描画される色は、RGBのそれぞれに対して環境光とこのプロパティの乗算で決まり、 もし環境光と ambient プロパティがともに「0xFFFFFF」ならば、物体は環境光の全反射で真っ白になってしまいます。

動作確認

環境光と ambient プロパティの値を変更し、物体の色が意図通りに動作するかを確認してください。

サンプル2-4:tutorial2_4.html(影の設定)

WebGL(OpenGL)では、光源に対して他の物体による影を描画するAPIは存在せず、別途計算する必要があります。 Three.js では、簡単な設定で実現することができます。

物体に影を設定は、次の4つのオブジェクトのプロパティを設定します。
(1) レンダラー
(2) 光源
(3) 影の生成元の物体
(4) 影が描画される物体
模式図は次のとおりです。

具体的には各関数でオブジェクトのプロパティを追加します。

  function initThree() {
   (省略)
    renderer.shadowMapEnabled = true;//影をつける(1)
  }
  function initLight() {  
   (省略)
    light.castShadow = true;//影をつける(2)
   (省略)
  }
  function initObject(){
   (省略)
    cube[i].castShadow = true;//影をつける(3)  
   (省略)
    plane.receiveShadow = true; //影をつける(4)
   (省略)
  }

影を利用する上で注意が必要なのは、計算コストが非常にかかるので多用すると処理速度が追いつかなくなります。

サンプル2-5:tutorial2_5.html(テクスチャ)

Three.js にはテクスチャを簡単に貼る方法が用意されています。

これまでは色を指定していた物体の材質に、テクスチャを選択することでテクスチャを自動的に張ることができます。 具体的なソースコードは次のとおりです。

  var sphere, sphere2 ;
  function initObject(){
    var texture1  = new THREE.ImageUtils.loadTexture('earthmap1k.jpg');
    sphere1 = new THREE.Mesh(
      new THREE.SphereGeometry(50,50,50),
      new THREE.MeshLambertMaterial({map: texture1})
    );
    scene.add(sphere1);
    sphere1.position.set(0,0,0);
    var texture2  = new THREE.ImageUtils.loadTexture('moonmap1k.jpg');
    sphere2 = new THREE.Mesh(
      new THREE.SphereGeometry(5,20,20),
      new THREE.MeshLambertMaterial({map: texture2})
    );
    scene.add(sphere2);
  }

テクスチャを利用すると、計算コストを抑えたまま一見複雑なことができます。 (天体テクスチャはPlanet Earth Texture Mapsを利用させていただいています) ただし、WebGLではクロスドメインテクスチャ利用の禁止により、ローカルのテクスチャを読み込むことができません。


Three.js によるマウスイベント

ウェブページは情報提供者が閲覧者に有用な情報を与えることが目的であるため、 コンテンツを表示するために開発されたウェブブラウザはユーザインターフェースとしての非常に優れています。 従来のhtml文書の表示(出力)、マウスやキーボードを用いた閲覧者による操作(入力)が容易に実装することができ、 特に HTML5 からは canvas 要素が加わったための表現力が今後飛躍的に増すことになります。 WebGL(Three.js)も、マウスやキーボードによる操作を行うことができます。 よく利用するマウスイベントの例は以下のとおりです。

マウスイベント例

イベント名意味
onmousedownマウスダウン時(マウスボタンを押した時)
onmouseupマウスアップ時(マウスボタンを離した時)
onmousemoveマウスムーブ時(マウスを動かした時)
onmouseoverマウスオーバー時(マウスがオブジェクト上に移動した時)
onmouseoutマウスアウト時(マウスがオブジェクト上から外に移動した時)

マウスイベントはウェブブラウザ上で所定の動きがあった場合に、Javascript を実行することができます。 また、イベント発生時のマウスの座標を取得することができます。 具体的に、マウスダウンイベントの場合は次のとおりです。

  window.onmousedown = function (ev){
    var x1 = ev.clientX; //マウスのx座標(ブラウザ上の座標)の取得
    var y1 = ev.clientY; //マウスのy座標(ブラウザ上の座標)の取得
    var x2 = ev.screenX; //マウスのx座標(ディスプレイ上の座標)の取得
    var y2 = ev.screenY; //マウスのy座標(ディスプレイ上の座標)の取得
  }

「ev」はマウスイベント発生時に実行される関数の引数に渡されるオブジェクトです。 クリック時のマウスの座標などは「ev」のプロパティとして与えられます。 本稿では、マウスドラックとホイール回転によりカメラの移動を実装します。

マウスドラックで視点を移動することができます
(※マウスホイールも実装していますが、ウィンドウスクロールしてしまうため確認できません。)

サンプル3:tutorial3.html(マウスイベント)

Javascriptのグローバル領域に次のソースコードを追加します。 マウスダウン時のマウス座標をグローバル変数(sy, sz)に代入し、 マウスアップ時のマウス座標との差からカメラの移動量を計算します。

  var down = false;
  var sy = 0, sz = 0;
  window.onmousedown = function (ev){    //マウスダウン
    if (ev.target == renderer.domElement) { 
      down = true;
      sy = ev.clientX; sz = ev.clientY;
    }
  };
  window.onmouseup = function(){        //マウスアップ
    down = false; 
  };
  window.onmousemove = function(ev) {   //マウスムーブ
    var speed = 2;
    if (down) {
        if (ev.target == renderer.domElement) { 
        var dy = -(ev.clientX - sy);
        var dz = -(ev.clientY - sz);
        camera.position.y += dy*speed;
        camera.position.z -= dz*speed;
        sy -= dy;
        sz -= dz;
      }
    }
  }
  window.onmousewheel = function(ev){   //マウスホイール
    var speed = 0.2;    
    camera.position.x += ev.wheelDelta * speed ; 
  }

ここまでで、Three.js の簡単な使い方の概要の説明が終わりました。 次節は、物理シミュレーションを作成する際の手順に入ります。

次へ:HTML5による物理シミュレーション環境の構築 ~WebGLライブラリThree.js 入門(2/3)~




▲このページのトップNPO法人 natural science トップ

コメント(3)

icyfire (2012年6月24日 00:37)

Something is wrong in tutorial2_4.html if using Three.js in Version:\"49\".

TW (2012年10月13日 02:04)

同じ理由かどうかは分かりませんが、私もtutorial2_4.htmlがうまくいきません。

『具体的には各関数でオブジェクトのプロパティを追加します。』にて、追加する場所がまずいのかな、とも思いました。
というのも、『(省略)』というふうに略されていますので、どこの行に追加してよいかの判断がつかないからです。

TW (2012年10月13日 02:52)

自己レスです。
解決しました。先ほどの想像とは別の問題だったようです。

デバッガを見ると、planeが定義されていないと出ていました。
『サンプル2-4:tutorial2_4.html(影の設定)』の項ですが、本ページの上から学習していくと『サンプル2-3:tutorial2_3.html(環境光の設定)』のソースをベースにします。
この2_3では陰の設定で必要な平面オブジェクト自体が定義されていないにもかかわらず、planeのプロパティーを設定しようとしたため、エラーで正しく動作できていなかったようです。

『少しだけ応用(平面と球の追加)』を流用し、適切な場所に平面オブジェクトを追加したうえで、2-4にあるプロパティーを追加すれば、正しく動作しました。

P.S.1
先の投降でも書きましたが、正しく動作するとは言え、2-4で追加するプロパティーの位置が適切なのかどうか判断つきませんでした。
追加した位置は以下です。
initThree():最終行。
initLight():lightをsceneへaddする直前。
initObject():cube[i]をsceneへaddした直後。およびplaneをsceneへaddした直後。

P.S.2
ただしく動作している(と思われる)のですが、デバッガには必ずエラーが1件だけ表示されます。
動作環境とエラーは以下です。
OS:windows7 home premium 64bit sp1
ブラウザ:firefox 16.1
Three.js:r51
エラー: TelemetryStopwatch: key \\\"FX_SESSION_RESTORE_COLLECT_DATA_MS\\\" was already initialized
ソースファイル: resource://gre/modules/TelemetryStopwatch.jsm
行: 53

P.S.3
icyfireさんも同じ理由であれば良いのですが。。。
ちなみに私は入門者です。
そんな私でも理解できるように丁寧に説明いただけて、大変助かっています。

▲このページのトップNPO法人 natural science トップ

関連記事

OpenGL入門

TIPS 集







WebGL

仮想物理実験室







▲このページのトップNPO法人 natural science トップ