WebGL
WebGLの簡易リファレンス
コンテキストオブジェクトの取得と初期化
canvas要素からコンテキストオブジェクトを取り出す。
code:js
var distCanvas = document.getElementById('canvas');
gl = distCanvas.getContext('webgl') || distCanvas.getContext('experimental-webgl'); //WebGL
gl = distCanvas.getContext('webgl2'); // WebGL2
これで取ってきたコンテキストオブジェクトのメソッドをあれこれ呼び出してwebglを使う
webglの初期化
code:js
gl.clearColor(0.0, 0.0, 0.0, 1.0); // 黒色で初期化
gl.clearDepth(1.0); // 初期化対象のおくゆき(?)
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); // 実際に画面をクリア
z-バッファは、画面上の各ピクセルに色を置く際に、一緒にカメラとの距離を書き込んでおく。
モデルが重なってる等して、同じピクセルに色を置こうとした際に、カメラとの距離を比較して、より近い側だったら上書きする・・・みたいな処理をするらしい。
HTMLの別のscriptタグに、あらかじめシェーダソースを書いておく。(なり、別のファイルに書くなりして、シェーダプログラムのテキストデータを用意する)
code:html
<script id="vs" type="x-shader/x-vertex">
attribute vec3 position;
void main(void){
gl_Position = vec4(position, 1.0);
}
</script>
<script id="fs" type="x-shader/x-fragment">
void main(void){
gl_FlagColor = vec4(1.0, 1.0, 1.0, 1.0);
}
</script>
これをgetElementById等で取り出す。
typeのx-shader/x-vertexはたぶん独自定義。
頂点シェーダはx-shader/x-vertex、フラグメントシェーダはx-shader/x-fragment
取り出した際のtype属性に応じたシェーダのインスタンスを作成する。
code:js
var scriptElement = document.getElementById("vs");
switch(scriptElement.type){
case 'x-shader/x-vertex':
shader = gl.createShader(gl.VERTEX_SHADER);
break;
case 'x-shader/x-fragment':
shader = gl.createShader(gl.FRAGMENT_SHADER);
break;
default:
return;
}
作ったインスタンスに、シェーダのソースコードを割り当ててコンパイル。
code:js
gl.shaderSource(shader, scriptElement.text);
gl.compileShader(shader);
//コンパイルが上手くいったかチェック
if(gl.getShaderParameter(shader, gl.COMPILE_STATUS)){
return shader;
}else{
alert(gl.getShaderInfoLog(shader));
}
シェーダプログラムを作成して、頂点シェーダとフラグメントシェーダを紐づけ
code:js
var program = gl.createProgram();
gl.attachShader(program, vs); // 頂点シェーダ
gl.attachShader(program, fs); // フラグメントシェーダ
gl.linkProgram(program);
// リンクが上手く行ったかチェック
if(gl.getProgramParameter(program, gl.LINK_STATUS)){
gl.useProgram(program);
return program;
}else{
alert(gl.getProgramInfoLog(program));
}
この中継を行っているインスタンスをシェーダプログラムと呼ぶみたい。
モデルデータ(VBO, IBO)の用意
モデルデータ自体はfloatの配列・・・今回は頂点の座標しか定義してない
code:js
// model data
var model_data = [
1.0, 1.0, 0.0,
-1.0, 1.0, 0.0,
1.0, -1.0, 0.0,
-1.0, -1.0, 0.0,
];
// model index
var model_idx = [
0, 1, 2,
1, 2, 3
];
model_idxは、定義した頂点の座標をもとに、どうポリゴンを張るかの定義。
この例だと3角ポリゴン2枚作る
他にも、頂点カラーとか、法線の向きとか、色々作れる
VBOは要するに、頂点毎の情報くらいのニュアンスのような気がする
用意したモデルデータをGPUに送る領域の確保(VBO, IBOの作成)
code:js
var vbo = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vbo); // ARRAY_BUFFER領域にvboをバインド
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(model_data), gl.STATIC_DRAW); // ARRAY_BUFFER領域にバインドされてるバッファへ、データの書き込み
gl.bindBuffer(gl.ARRAY_BUFFER, null); // ARRAY_BUFFER領域のバインドを解除
var ibo = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo); // IBOはELEMENT_ARRAY_BUFFER領域を使う
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Int16Array(model_idx), gl.STATIC_DRAW);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
情報をシェーダに送信(ではない)する
ニュアンス的には、GPUへ送ったバッファデータを、シェーダの変数に割り当てる作業。
工程がちょっと複雑
シェーダ側の変数に対応するポインタ(のようなもの)を取得
ポインタに対して、有効化を設定
シェーダに送りたいデータをバインド
ポインタに対して、バインドされてるデータを紐付け(?)
code:js
var shPosition = gl.getAttribLocation(prog, 'position');
gl.bindBuffer(gl.ARRAY_BUFFER, vbo);// 送信するVOBを選択
gl.enableVertexAttribArray(attLocation);// 送信先のパラメータを有効化
gl.vertexAttribPointer(attLocation, 3, gl.FLOAT, false, 0, 0); // 送信実行
// シェーダ側が"attribute vec3 position;"って書いてるからこういう取り方
画面に表示する
code:js
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo);
gl.drawElements(gl.TRIANGLES, model_idx.length, gl.UNSIGNED_SHORT, 0);
gl.flush();
IOBを使った描画はdrawElements
drawを呼んだ時にバインドされているARRAY_BUFFER, ELEMENT_ARRAY_BUFFERを描画するイメージ
flushで反映