LoginSignup
2
3

More than 5 years have passed since last update.

シンプルさの重要性をWebGLヘルパーで学ぶ

Posted at

概要

シンプルさの重要性( http://eed3si9n.com/ja/simplicity-matters )をWebGLヘルパーの作成によって学びました。

WebGLは順序依存が強いライブラリ

WebGLでは、次の例のような順序に関する依存性があります。

テクスチャをフラグメントシェーダのユニフォームに適用するために、以下の関数を近接して発行する必要がある。

const texIndex = 0;
const actTex = (texIndex === 0) ? gl.TEXTURE0 : (texIndex === 0) ? gl.TEXTURE1 : gl.TEXTURE2;
const uniformLocation = gl.getUniformLocation(pg, key); // ロケーションオブジェクトを取得
gl.activeTexture(actTex); // プログラムにおいて複数のテクスチャを分類する
gl.bindTexture(gl.TEXTURE_2D, ftValue.texture); // テクスチャオブジェクトをバインド
gl.uniform1i(uniformLocation, texIndex); // ロケーションオブジェクトとテクスチャオブジェクトを関連付け

プログラムを実行するために、頂点データを設定し、今のプログラムを設定し、出力先、頂点シェーダ頂点情報、フラグメントシェーダのパラメータを設定し、実行する必要がある。

const vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); // 現在のアレイバッファを設定
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]), gl.STATIC_DRAW); // アレイバッファにデータを設定
gl.useProgram(pg); // 現在のプログラムオブジェクトを設定
gl.bindFramebuffer(gl.FRAMEBUFFER, out.framebuffer); // プログラムの出力先を指定
gl.viewport(0, 0, this.width, this.height); // 出力先の描画範囲を指定
const attribLocation = gl.getAttribLocation(pg, calcKeys.attrib); // プログラムの頂点シェーダのロケーションオブジェクトを取得
gl.enableVertexAttribArray(attribLocation); // ロケーションを有効化
gl.vertexAttribPointer(attribLocation, 2, gl.FLOAT, false, 0, 0); // ロケーションに、前回設定したアレイバッファを関連付け
const uniformLocation = gl.getUniformLocation(pg, key); // フラグメントシェーダの各ロケーションオブジェクトを取得
gl.uniform1f(uniformLocation, value); // ロケーションに値を設定
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); // 描画
gl.flush(); // 必要に応じてフラッシュ

マップによって複雑さをシンプルな表現に置き換えることができる

拙作uni2vecの中にあるCalcGLクラスを利用すれば、マップ表現で計算指示を出すことができます。
https://sites.google.com/site/uni2vec/cab/uni2vec.html

  • マップはハッシュテーブルまたは連想データ構造(シンプルさの重要性の用語)とも呼ばれます。Javascriptではオブジェクトのリテラルを利用することも可能です。
  • WebGL APIの順序に関する依存性の大部分は、CalcGLの各プロトタイプ関数の内部で処理されます。
  • CalcGLを利用するプログラムは、必要になるまでは複雑さに目を向けなくても良いという意味で、WebGLの複雑さはCalcGLによって隠蔽されています。(必要になるまではデスクトップパソコンのカバーを開けなくても良いというメタファ)
  • CalcGLは、uni2vecで使用していないuniformデータ型の設定を、calcプロトタイプ関数で実装していません。必要になったらデスクトップパソコンのカバーを開ければ良いし、機能の追加が簡単なようにWebGL APIもなっているし、機能の追加が簡単なようにCalcGLも実装しているつもりです。(マザーボードは特定のアーキテクチャに従っていますよねというメタファ)

以下の例のように。※

const env = new ShaderEnv(); // 複数のフラグメントシェーダのenv入力に渡す16個の環境変数を管理するクラス
// ...
function processNormalize(matName) { // 複数のベクトルを並列に正規化する機能の例
    const mat = ftMap[matName]; // 事前に生成していたFrameTexオブジェクトを、連想データ構造から取得(FrameTexはフレームバッファとテクスチャを関連付けた連想データ構造)
    const workFtScr = ftMap['work0']; // ワーク領域を表すFrameTexオブジェクトを、連想データ構造から取得

    calcgl.calc({
        pg: pgMap['inverseNorm'], // プログラムの連動データ構造から、各ベクトルのノルムの逆数を並列計算するプログラムを取得
        env: env.env, // フラグメントシェーダのvec4入力の一つを設定
        matTex: mat, // フラグメントシェーダのテクスチャ入力の一つをFrameTexとして設定
        out: workFtSrc, //出力先をFrameTexとして設定
    });

    calcgl.calc({
        pg: pgMap['scalarProd'], // プログラムの連動データ構造から、各ベクトルのスカラー倍を並列計算するプログラムを取得
        env: env.env,
        matTex: mat, // フラグメントシェーダのテクスチャ入力の一つを、FrameTex連想データ構造として設定
        workTexScr: workFtScr, // 前段で計算したノルムの逆数をFrameTexとして設定
        out: ftMap['tmp'], //出力先をFrameTexとして設定
    });

    toggleMat(matName); // matNameで取得できるFrameTexオブジェクトを、前段で出力した一時FrameTexオブジェクトに置き換え
}

※例示のため、Javascriptの中括弧表現に置き換えています。uni2vec内ではパフォーマンスチューニングのため、マップオブジェクトを再利用する表現になっています。

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3