ブラウザだけで3Dプログラミング、PolygonExplorlerEditor

#PolygonExplorlerEditor

##これは何

<img width="500" alt="poxe_s1.png" src="https://qiita-image-store.s3.amazonaws.com/0/74838/3ae54af6-1025-af3d-7ce5-bdcae7503c05.png">

ブラウザ上でWebGLによるプログラミングをして、その場でプレビュー実行できるWebアプリです。略称はPoExE。

シェーダーを書いて公開できるサイトは色々ありますが、これはJavascript側のコーディングもWeb上のエディタでできるのがポイントです。Javascriptのコードとバーテクスシェーダ、フラグメントシェーダのそれぞれのGLSLコードをまとめて保存することができます。
保存したデータを再生専用のビュアーで見せることもできます。

次のような用途に気軽にちょこっとコードを書いて動かせる環境を目指しています。

 - WebGLやOpenGL系の原理的な勉強
 - モデリングやシェーダーのアルゴリズムの研究やプロトタイピング
 - ちょっとしたデモやアプリケーション

プレビュー画面での基本的なカメラの動作が組み込み済みなので、モデルのデータを用意するだけで3Dシーンを作成して、カメラを動かして眺めることができます。

Javascriptのコードは、WebGLの薄いラッパーライブラリを使って書くようになっています。WegGL特有のめんどくさいバッファ操作などのAPIを呼び出すことなく、基本的にはデータ構造を用意するだけでシーンを作成できるようにしています。
現状WebGL2には対応していません。

## 動かし方

動作ブラウザは、Chrome,Firefox,Safari,Edgeです。WebGLのサポート状況によっては制約があるかもしれません。モバイル環境でもエディットは辛いかもしれませんが動作はするはずです。

純粋なjavascript+htmlのアプリなので、次の3通りの方法で使えます。

 1. オンラインで使う
 2. ダウンロードしてオフラインで使う
 3. Webサーバを用意して使う

オンラインの場合は、 https://polygon.work/pox/ を開いてそのままサンプルのところからいじってください。編集内容はブラウザのみに保存され、サーバに送信されることはありません。編集したソースは、jsonファイルとしてローカルファイルに保存、読み込みできます。

ダウンロードは、githubからどうぞ。 https://github.com/wakufactory/pox 
poxe.html をファイルとしてブラウザで開くと動きます。ただしこの場合は、ajaxでデータをロードしてくるような機能は使えません。またテクスチャ画像もCORS制限に引っかかるのでWebGLでは使えないようです。

3番目は、ローカルPCにWebサーバを用意するなり、どこかのWebサイトに上げて利用する方法です。この場合はフルスペックで動作します。

### プレイヤーについて

エディタで編集して保存したjsonファイルは、view.htmlで再生することができます。
再生するJSONファイルをパラメータとして、 https://polygon.work/pox/view.html?data/poly20_div53.json のように使います。
stereoモードにしてスマフォで見ると、左右のステレオ画像が描画されるので、VRゴーグルで立体視することができます。
![2017-12-03 19.03.09.jpg](https://qiita-image-store.s3.amazonaws.com/0/74838/d6c5fbe3-d76e-461a-68d3-5c92f7bfe835.jpeg)


view.htmlのソースを参考にすると他のWebページへの組み込みも簡単にできると思います。

### モジュール構成

エディタ本体はpoxe.html一つだけ。Javascriptのファイルはjs以下にあります。
また、view.html は、保存したjsonデータを読み込んで表示するツールになります。


 - poxplayer.js 作成したソースを実行するモジュール
 - WWG.js WebGLラッパーライブラリ
 - CanvasMatrix.js 行列演算サポートライブラリ
 - WWModel.js ポリゴンモデル生成サポートライブラリ
 - WBind.js, pointer.js UI用サポートライブラリ
 
全てオリジナルのライブラリで、全部合わせても100Kb程度と軽量です。

## 画面の説明

<img width="500" alt="poxe_s0.png" src="https://qiita-image-store.s3.amazonaws.com/0/74838/0cc303be-1469-bc1c-61f2-df30c5dbd0ff.png">

だいたい見た通りですが、左側がエディタ部分、右側がプレビュー領域になります。

エディタはタブが3つあり、Javascriptのモデル部分と、バーテックスシェーダ、フラグメントシェーダのそれぞれのGLSLの編集領域になります。
コードをエディットして右上のAPPLYボタンで、コードが実行され、右のプレビュー領域に結果がレンダリングされます。またCTRL(COMMAND)+Sでも実行されます。

SAVEボタンで、JavascriptとGLSLのソースをまとめたJSON形式のファイルをダウンロードしてローカルで保存することができます。このファイルはそのままLOADボタンで読み込むことができます。また、最後に編集したものはブラウザ内にも保存されています。

プレビュー領域では、マウスドラッグかスワイプによってカメラが移動、マウスホイールかピンチでカメラのzoomになります。

プレビュー画面の上にあるチェックは、rotがカメラの自動回転、stereoは平行法のステレオ画像を生成します。このモードで、スマフォのVRゴーグルで立体視することができます。  [II]ボタンで実行を一時的に停止できます。


##カメラの動作

カメラはデフォルト状態では、右側のプレビュー画面で、マウスやタッチの操作によって自動的に動くようになっています。カメラ用の変換行列は自動計算され、シェーダにも設定されます。

カメラはデフォルトのモードでは、原点を中心に周りを球面に沿って動きます。カメラのzoomはカメラと原点の距離が変化することになります。

## WebGLのレンダリングパイプライン

用語の確認をかねて、WebGLのレンダリングパイプラインの概要です。

<img width="628" alt="wgl_pipe.png" src="https://qiita-image-store.s3.amazonaws.com/0/74838/58ba61c1-9725-8ecc-d794-994a8085d99f.png">


このツールでは、上の図において、V-SHADERとF-SHADERのソースをそれぞれ編集し、attributeのデータ、uniformのデータをJavascript側で用意することになります。


##モデルデータコードの書き方

Javascriptコードは、レンダリング開始時に1回実行されます。実行スコープはエディタライブラリのメソッド内、いくつかのサポートメソッドが使えますが、それ以外は基本的に普通のJavascriptコードとして書くことができます。

静的なシーンを作成する場合は、シーンのデータ構造となるデータを用意して、レンダリング開始のメソッドを呼ぶとシーンが作成されます。

アニメーションは、画面のフレーム更新の度に呼び出されるメソッド内に変更を記述することによって行います。

シンプルなサンプル https://polygon.work/pox/poxe.html?sample/sample_basic.json のコードをベースに以下解説します。

###基本のデータ構造
まず最初にシーンの全体の設定のためのデータ定義があります。

```js
POX.setting = {
	name:"sample_basic",
	scale:1.0
};
```
ここではnameとscaleを設定しています。nameはプログラムファイルを保存する時の名前として使われます。scaleはカメラ移動等の基準のためのもので、大体の描画したいオブジェクトの座標サイズと同程度の値を設置します。
カメラのパラメータ等もここに書くことができます。

次にシーンのデータを用意します。

シーンのデータは、最終的にシェーダに渡すattributeのバッファデータと、Uniformのデータを用意します。これらのデータを構造体の形で用意して、POX.setSceneメソッドを呼び出すことでシーンのレンダリングが実行されます。

ここでは var scene にシーンのデータ構造を作成しています。sceneの内容をみていきます。

```js
	//environment params
	env:{clear_color:[0.2,0.2,0.4,1.0],cull:false},
```
ここは、全体の環境設定で、クリアカラーとカリングをするかどうかのフラグを設定しています。色や座標のベクトルは、単純配列で指定します。

次に設定しているのが、バーテクスシェーダとフラグメントシェーダに与えるUniformのデータです。uniformのデータは、シェーダのGLSLのunoformで設定した変数名をパースして、javascriptの構造体データと自動的に結びつけるようになっています。

```js
	//vertex shader uniform data
	vs_uni:{
		col:[1.0,0.2,1.0,1.0]
	},
	//flagment shader uniform data
	fs_uni:{
		lvec:[1.0,1.0,1.0],
		mode:0
	},
```
ここでは、バーテクスシェーダのuniformのcolにvec4のデータを設定、フラグメントシェーダにはvec3のlvecと、intのmodeを設定しています。

次はいよいよモデルのデータ部分です。まずは三角形のポリゴンのデータを用意しています。

```js
	model:[
		// model triangle polygon
		{geo:{mode:"tri",
			vtx_at:["position","norm"], // v-shader attribute name
			vtx:[	//VBO buffer
				1,0,0,		//position1
				r3,r3,r3,	//normal1
				0,1,0,		//position2
				r3,r3,r3,	//normal2
				0,0,1,		//position3
				r3,r3,r3	//normal3
			],
			idx:[0,1,2]	//index buffer
			},
			vs_uni:{col:[1.0,0.0,0.0,1.0]}	//v-shader uniform for model
		},
```
modeは描画の方法で、"tri"は、gl.TRIANGLESに対応しています。

ポリゴンデータはVBOバッファに与える値を1次元の配列として用意します。vtx_atにバーテクスシェーダのどのattribute変数に対応するかを設定した上で、vtxに配列として、attributeの順番にデータをセットします。いわゆるインターリーブ方式で、一つのVBOに頂点ごとに複数のattributeを設定する方法を取っています。

vtxには3角ポリゴンなので、座標と法線それぞれvec3のデータが3つ、計18個のデータが設定されています。

idxはIBOに設定する頂点インデックスデータです。

モデルデータの中にも個別にuniformに設定するデータを書くことができます。最初のvs_uniで設定した値は、ここでのvs_uniでオーバライドされるので、オブジェクト毎に色を変えることになります。


もう一つのオブジェクトとして球を配置しています。球のデータはサポートライブラリである。WWModelを使って生成しています。

```js
var sp = new WWModel().primitive("sphere",{div:20,wx:0.5,wy:0.5,wz:0.5})
```

WWModelで生成したモデルデータは、objModelメソッドでVBOの形式に変換することができます。

```js
	{geo:sp.objModel(),  //convert model-object to VBO data
		vs_uni:{col:[0.0,0.0,1.0,1.0]}},
```

以上のデータをPOX.sceneに与えることで、三角ポリゴンと球の静的なシーンは完成です。

### オブジェクトの生成

頂点データをvtxデータに作って設定するのですが、プリミティブを簡単に生成するためのサポートクラスWWModelがあります。このクラスは現在以下のプリミティブやオブジェクトを生成することができます。

- 基本プリミティブ "box","sphere","plane","torus","sylinder","cone","disc"
- 正多面体 "polyhedron"
- パラメトリックサーフェス [x,y,z] = f(u,v)関数定義によるメッシュ
- .objファイルの読み込み (マテリアルファイルは非対応)

オブジェクトのサンプルは https://polygon.work/pox/poxe.html?sample/sample_objs.json をみてください。
<img width="250" alt="objs.png" src="https://qiita-image-store.s3.amazonaws.com/0/74838/261fec56-b28c-ca26-f9b3-18c4c3feb028.png">

.objファイルの読み込みは非同期に行われるため、WWmodel.loadObjメソッドはPromiseを返します。したがって、

```js
new WWModel().loadObj("res/bunny_s.obj",10.0).then((data)=> {
	//dataをsceneにセット
	POX.setScene(scene) ;
})
```
のように書くことになります。


### モデルデータの移動

モデルデータに、行列を与えて移動、回転、リサイズの変換を与えることができます。これにはbmというパラメータに変換行列を設定します。

```js
model:[
	{geo:model.objModel(),
		bm:new CanvasMatrix4().scale(1,1,2).rotate(30,1,0,0).translate(0,1,0) ;
	}
```

CanvasMatrix4は行列計算のサポートライブラリです。translate(x,y,z),scale(x,y,z),rotate(deg,x,y,z)等のメソッドで行列を生成できます。
上の例では、z方向を2倍に拡大し、x軸に30度回転し、y方向に1移動する行列ができます。

### テクスチャの使い方

テクスチャの使用例 https://polygon.work/pox/poxe.html?sample/sample_texture.json 

テクスチャは、テクスチャのソースをsceneデータの中にセットし、uniformの対応するテクスチャ変数に書くだけで使えます。

```js
scene = {
	texture:[
		{src:"sample/tex1024.png"}
	],
...
	fs_uni:{"tex1":0}
```

これでtex1024.pngがフラグメントシェーダの uniform sampler2D tex1　にバインドされます。

テクスチャの読み込みは内部的に非同期で行われますが、意識する必要はありません。テクスチャが読み込み終わった時点でレンダリングがスタートします。

上の例では、tex1にテクスチャのインデックスとして0を指定していますが、textureに名前を付けて指定することもできます。


### instanced arrayを使う

WebGL1に置いてinstanced arraysは拡張機能ですが、ほとんどの環境で使えそうなのでサポートしています。
インスタンス用のバッファ定義は、VBO同様にインスタンスごとのattributeを配列に入れて構造体に入れるだけでできます。

以下は、色違いのboxを3つインスタンスを使って描画する例です。
https://polygon.work/pox/poxe.html?data/sample_instance.json

```js
	model:[
		{geo:new WWModel().primitive("box",{wx:1,wy:1,wz:1}).objModel(),
		inst:{attr:["ipos","icolor"],
			data:[0,0,0, 1.0,0,0,1.0,
			      2,0,0, 0,1.0,0,1.0,
			      0,0,2, 0,0,1.0,1.0 ],
			count:3,
			}
		}]
```
attributeとして "ipos"と"icolor"を定義しているので、この値がインスタンスごとにバーテクスシェーダの対応するattributeに渡されるということになります。


###フレームごとの更新

動きの無い静的なシーンは以上のようにデータを用意するだけですが、フレームごとのアニメーションをする場合は、POX.updateメソッドを記述します。

updateメソッドの中では、

 - uniformデータの更新
 - オブジェクトの変換行列の更新
 - バッファデータの更新
 - カメラデータの更新(カメラを自動で動かす場合)

などを記述することになります。

```js
POX.update=function(render,cam,time){
	var ret={};
	// get first model data
	var m=render.getModelData(0);
	// set modifire matrix to rotate model 
	m.mm=new CanvasMatrix4().rotate(time/50,0,1,0);
	return ret;
}
```

ここでは、最初のモデルである3角ポリゴンに対して、変換行列を与えて回転させています。

モデルののデータはrender.getModelDataというメソッドで取得できます。そのモデルデータに対して、mmというパラメータに行列を与えるとそれが適用されます。最初のシーンで設定したbmの行列があれば、それを掛け合わせた行列が適用されることになります。

同様にモデルデータに対して、fs_uni,vs_uniのパラメータを更新するとuniform変数の値を変化させることができます。


## シェーダーの書き方

シェーダは、バーテクスシェーダとフラグメントシェーダそれぞれのタブに記述します。
attributeとuniformのデータは前述のようにjavascriptから構造体データとして与えますが、エンジン側で以下のuniformを用意しているのでjavascript側で設定せずに使えます。

```GLSL
uniform mat4 modelMatrix;  //モデルデータ変換行列
uniform mat4 mvpMatrix;    //モデル+カメラ変換行列
uniform mat4 invMatrix;    //モデル変換転置行列
uniform vec3 eyevec;       //カメラ位置ベクトル
uniform int stereo;        //通常 0、ステレオモード時 1:左、2:右
```


##その他の機能

以上基本的な機能以外に、ここに書いてないこともあります。おいおい加筆して行きたいと思います。

まずはサンプルのシーンを色々いじってみてください。

### まだできないこと

 - パラメータのUI
 - マルチパスレンダリング


これはできるの？のようなツッコミもコメントでお待ちしております・・・・・