これまで、Canvas2Dで描画部分を作っていたのですが
やはり、速度面で問題があり、WebGLを導入する事にしました。
とはいえ、全くやった事がないので、何から始めるか悩んだのですが
まずはCanvas2Dの機能をラッピングする事からはじめて見ました。
が、そんな簡単な話ではなかったので、何日かに分けて掲載できればと思います。
ますは、基盤を作っていこうかと思います。
目次
- Programを作る
- Shaderで必要なUniformを動的に取得する
Programを作る
ProgramにセットするShaderは後日記載できればと思います。
なので、Shaderがある程で書いていきます。
/**
* @param {WebGLRenderingContext} gl
* @constructor
* @public
*/
constructor (gl)
{
// 他の関数でコンテキスト使えるようにしておき
this.gl = gl;
}
/**
* @param {string} vertex_source
* @param {string} fragment_source
* @return {WebGLProgram}
* @public
*/
createProgram (vertex_source, fragment_source)
{
// 新規のprogramを作成
const program = this.gl.createProgram();
// 頂点シェーダーを作成
const vertexShader = this.gl.createShader(this.gl.VERTEX_SHADER);
this.gl.shaderSource(vertexShader, vertex_source);
this.gl.compileShader(vertexShader);
// 頂点シェーダーのエラーを感知する。
// ただ処理が重いので本番用のビルド時は対象外にする。
if (!this.gl.getShaderParameter(vertexShader, this.gl.COMPILE_STATUS)) {
console.log(this.gl.getShaderInfoLog(vertexShader));
}
// フラグメントシェーダーを作成
const fragmentShader = this.gl.createShader(this.gl.FRAGMENT_SHADER);
this.gl.shaderSource(fragmentShader, fragment_source);
this.gl.compileShader(fragmentShader);
// フラグメントシェーダーのエラーを感知する。
// ただ処理が重いので本番用のビルド時は対象外にする。
if (!this.gl.getShaderParameter(fragmentShader, this.gl.COMPILE_STATUS)) {
console.log(this.gl.getShaderInfoLog(fragmentShader));
}
// 頂点シェーダーをプログラムにアタッチ
this.gl.attachShader(program, vertexShader);
// フラグメントシェーダーをプログラムにアタッチ
this.gl.attachShader(program, fragmentShader);
// プログラムを起動
this.gl.linkProgram(program);
// プログラムの起動時のエラーを感知する。
// ただ処理が重いので本番用のビルド時は対象外にする。
if (!this.gl.getProgramParameter(program, this.gl.LINK_STATUS)) {
console.log(this.gl.getProgramInfoLog(program));
}
// プログラムを起動した後は不要になるので、デタッチしておく
this.gl.detachShader(program, vertexShader);
this.gl.detachShader(program, fragmentShader);
// デタッチ後は削除して綺麗にしておく
this.gl.deleteShader(vertexShader);
this.gl.deleteShader(fragmentShader);
return program;
}
これで、汎用的にプログラムを生成させる事ができます。
Shaderで必要なUniformを動的に取得する
Shaderで記載したUniformを毎回個別に書くのは大変なので
生成したプログラムから必要なUniformやpositionなどを動的に取得する。
/**
* @param {WebGLProgram}
* @public
*/
createUniform (program)
{
const uniformInfo = {};
// シェーダーで使ってるUniformを取得
const activeUniforms = this.gl.getProgramParameter(program, this.gl.ACTIVE_UNIFORMS);
for (let idx = 0; idx < activeUniforms; idx++) {
// Uniformの情報をセット
// 変数名や引き渡しに必要な関数を動的に取得する
const info = this.gl.getActiveUniform(program, idx);
const names = info.name.split(".");
// 配列の変数も扱えるようにする
const name = (names[0].endsWith("[0]"))
? names[0].slice(0, -3)
: names[0];
const block = {};
block.location = this.gl.getUniformLocation(program, name);
block.type = info.type;
// typeから必要な関数に対して、positionをセットする
switch (info.type) {
case this.gl.FLOAT:
block.method = this.gl.uniform1f;
break;
// ...他の関数も同様にセットしていく。
}
uniformInfo[name] = block;
}
return uniformInfo;
}
途中の処理を省いてますが、最終的にシェーダーで利用するUniformの命名はu_変名
で運用する。
これで、最低限必要な引き渡しの環境は整ったので、次はシェーダー側への引き渡し部分を記載できたらと思います。