Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
0
Help us understand the problem. What are the problem?

Flash Advent Calendar 8日目 - WebGL導入への道のり01 -

これまで、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_変名で運用する。
これで、最低限必要な引き渡しの環境は整ったので、次はシェーダー側への引き渡し部分を記載できたらと思います。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
0
Help us understand the problem. What are the problem?