##なにをしてるか
形(頂点シェーダ)と色(フラグメントシェーダ)を元に物体を生成して、
3D空間をカメラでのぞいている。
本来ブラウザからは使えないOpenGLとの橋渡しをしてくれるのがWebGL。
##用語の整理 ###シェーダ shader => シェーディング(陰影処理)を行うプログラム。 本来相当な負荷がかかる描画処理をGPUを用いて軽快に行える。 実装にはGLSL(openGL Shading Language)を使うのが一般的。 頂点シェーダとフラグメントシェーダのふたつがある。
プログラムの中で単に「シェーダ」といった場合は、
GLSLで記述されたシェーダを指す。ことにする。
###頂点シェーダ 頂点に関するすべての情報。three.jsのGeometryか。
###フラグメントシェーダ 面の情報。色とか。three.jsのMaterialか。
###GLSL (webGL Shader Language) シェーダを書くための言語。 型の決まりとかはワラワラしてるけど、 書き方はC言語的でシンプル。ムズいけど。
##流れ
###WebGLコンテキストを取得
canvasは'2d'でWebGLは'webgl'
var gl = c.getContext('webgl');
###頂点シェーダとフラグメントシェーダの生成
JS側でシェーダ用のオブジェクトを作って、
HTMLに埋め込んでるGLSLと関連付けて、
GLSLをコンパイルする。
以下、長いので
頂点 = 頂点シェーダ
フラグメント = フラグメントシェーダ
var v_shader = gl.createShader(gl.VERTEX_SHADER);
var f_shader = gl.createShader(gl.FRAGMENT_SHADER);
// シェーダとソースの関連付け
gl.shaderSource(shader, scriptElement.text);
// シェーダのコンパイル
gl.compileShader(shader);
###プログラムオブジェクトの生成 頂点とフラグメントをプログラムオブジェクトというもので結びつける。
// プログラムオブジェクトの生成
var program = gl.createProgram();
// プログラムオブジェクトにシェーダを割り当て
gl.attachShader(program, vs);
gl.attachShader(program, fs);
// シェーダをリンク
// これはいったい何してる?
gl.linkProgram(program);
// プログラムオブジェクトを有効にする
// createした時点で有効にしてくれればいいのに
gl.useProgram(program);
###シェーダに渡す変数の宣言 JSからシェーダに渡す変数を宣言する
// プログラムオブジェクトにpositionという変数を関連付ける
var attLocation = gl.getAttribLocation(prg, 'position');
// 要素数は3つ(3Dのpositionだから)
var attStride = 3;
###頂点のバッファVBOを作成する
頂点をそのままギャっと描画できるわけじゃなく、
一度バッファにいれる必要があるらしい。
// バッファを作って
// バッファをWebGLとバインドして
// バッファに頂点のデータを浮動小数で流し込んで
// バッファを解除する。なぜ?
var vbo = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.STATIC_DRAW);
###attribute属性をを有効にする getAttribLocationで関連付けた変数を有効にする
gl.vertexAttribPointer(attLocation, attStride, gl.FLOAT, false, 0, 0);
###モデル・ビュー・プロジェクションの行列生成
// minMatrix.js を用いた行列関連処理
// matIVオブジェクトを生成
// identityは単位行列
var m = new matIV();
// 各種行列の生成と初期化
var mMatrix = m.identity(m.create()); // モデル変換行列
var vMatrix = m.identity(m.create()); // ビュー変換行列 = カメラの位置
var pMatrix = m.identity(m.create()); // プロジェクション変換行列 = 視野角とアスペクト比
var mvpMatrix = m.identity(m.create());
###モデル・ビュー・プロジェクションの行列計算
// ビュー座標変換行列
// カメラ位置 x, y, z
m.lookAt([0.0, 1.0, 3.0], [0, 0, 0], [0, 1, 0], vMatrix);
// プロジェクション座標変換行列
// 視野角, アスペクト比, ニア, ファー
m.perspective(90, c.width / c.height, 0.1, 100, pMatrix);
// 各行列を掛け合わせ座標変換行列を完成させる
m.multiply(pMatrix, vMatrix, mvpMatrix);
m.multiply(mvpMatrix, mMatrix, mvpMatrix);
###シェーダに頂点を渡す
シェーダで宣言してる位置の変数に、
JS側で計算した行列を渡す。
// uniformLocationの取得
// getAttribLocationと同じ要領
// mvpMatrixはシェーダで宣言してる変数名
var uniLocation = gl.getUniformLocation(prg, 'mvpMatrix');
// uniformLocationへ座標変換行列を登録
gl.uniformMatrix4fv(uniLocation, false, mvpMatrix);
###描画する
// モデルの描画
// バッファに展開するだけで描画はflushされるまでしない
gl.drawArrays(gl.TRIANGLES, 0, 3);
// コンテキストの再描画
gl.flush();
##アニメーションはどうするか requestAnimationframeで回して、 canvasをクリアして、 mvpMatrixを再描画すればOK。
##参考
ほぼすべて https://wgld.org/d/webgl/ を写経して覚える。
こんなにわかりやすいサイトは初めてすごい。