メインコンテンツへ移動

attribute 変数と頂点シェーダー_WebGL ノート 2

無料2015-12-22#JS#WebGL#webgl attribute变量#js向webgl传值#顶点着色器的attribute变量#顶点着色器#片元着色器#片元

attribute 変数を通じて頂点シェーダーに値を渡すことで、点の位置を動的に修正できます

前言

[前回のノート](/articles/ 用 webgl 绘制一个矩形-webgl 笔记 1/) では頂点シェーダーソースプログラム内で gl_Position をハードコーディングしました。点の位置を修正したい場合はシェーダーソースプログラムを修正する必要があり、これはもちろん意味がありません。私たちはより柔軟な方式が必要です。例えば attribute 変数です

一.util のカプセル化

[前回のノート](/articles/ 用 webgl 绘制一个矩形-webgl 笔记 1/) では疑似矩形(少し大きな点)を描画するために 70 行以上のコードを書きましたが、エラーチェックのみで、互換性処理はまだ行っていません。すべてのステップで WebGL ネイティブ API を呼び出しており、フローは非常に明確ですが、実際には面倒すぎます。1 つの点で 70 行、2 つの点で 140 行ではダメなので、まず util をカプセル化します

始める前に、いくつかの問題に注意する必要があります:

###1.getContext の互換性の問題

getContext('webgl') は標準方式ですが、低バージョンのブラウザでは異なる文字列を渡す必要があります。以下の通り:

/**
 * Creates a webgl context.
 * @param {!Canvas} canvas The canvas tag to get context
 *     from. If one is not passed in one will be created.
 * @return {!WebGLContext} The created context.
 */
var create3DContext = function(canvas, opt_attribs) {
  var names = ["webgl", "experimental-webgl", "webkit-3d", "moz-webgl"];
  var context = null;
  for (var ii = 0; ii < names.length; ++ii) {
    try {
      context = canvas.getContext(names[ii], opt_attribs);
    } catch(e) {}
    if (context) {
      break;
    }
  }
  return context;
}

上記のコードは google チームの webgl-utils.js から抜粋したもので、create3DContext の他に requestAnimationFrame の互換バージョンも提供しており、後でアニメーションでも使用するため、私たちの util は直接 webgl-utils.js を include します

###2.program オブジェクトの参照を保持

gl.createProgram() が返す program オブジェクトはシェーダーの初期化プロセス中で使用されるだけでなく、これから行う attribute 変数の値渡しでも使用します。program オブジェクトは js とシェーダー内部通信の架け橋ですが、問題はWebGL が gl.getProgram() のような getter を提供していないため、util をカプセル化する際に必ず program オブジェクトの参照を保持し、外部で使用できるようにする必要があります。例えば:

function initShaders(vsSrc, fsSrc) {
    var program = _createProgram(vsSrc, fsSrc);
    
    if (!program) {
        log('Failed to create program');
        return false;
    }
    gl.useProgram(program);

    // Save program object
    //!!! because there is no getter like gl.getProgram()
    glUtil.program = program;

    return true;
}

上記のコードは筆者の gl-util.js から抜粋したもので、必要であれば直接使用できます。インターフェース説明は以下の通り:

// 全局变量:glUtil
return {
    program: null,  // runtime assignment

    getContext: getContext,
    initShaders: initShaders,

    debug: function(onOrOff) {
        if (typeof onOrOff === 'boolean') {
            debug = onOrOff;
        }
    }
};

二.用語および API 説明

###1.シェーダー

シェーダーは頂点シェーダーとフラグメントシェーダーに分かれます:

  • 頂点シェーダー

頂点の特性(位置、色など)を記述するプログラム。頂点とは二次元または三次元空間中の 1 つの点を指し、例えば二次元または三次元図形の端点または交点

  • フラグメントシェーダー

逐フラグメント処理プロセス(照明など)を行うプログラム。フラグメントは WebGL 用語で、簡単に言えばピクセル(画像のユニット)と理解できます

P.S. 実際にはフラグメントは画面に表示される 1 つのピクセルを表し、そのピクセル点の位置、色および他の情報も含みます

###2.頂点シェーダーソースプログラム

GLSL ES で書かれたプログラムで、文字列形式で WebGL システム内部に渡されます

// 顶点着色器源程序
var vsSrc = 'void main() {' +
    'gl_Position = vec4(0.0, 0.0, 0.0, 1.0);' + // 设置坐标
    'gl_PointSize = 200.0;' +                   // 设置尺寸
'}';

その中で、gl_Position に値を割り当てるのは必須で、否则シェーダーは正常に動作しません。gl_Positionvec4 タイプで、つまり四次元ベクトル(同次座標、最後の成分は通常 1.0 を取る)

gl_PointSize にはデフォルト値 1.0 があり���値を割り当てる必要はありません。gl_PointSizefloat タイプで、受け付けません3f(C 言語中で 3f は浮動小数点数 3 を表す)のような形式で、しかも孤立点を描画する時のみ有効です(孤立点の描画とは何か、後で説明します)

###3.フラグメントシェーダーソースプログラム

頂点シェーダーソースプログラムと同じですが、制御するのはフラグメントシェーダーです

// 片元着色器源程序
var fsSrc = 'void main() {' +
    'gl_FragColor = vec4(1.0, 0.0, 1.0, 0.75);' + // 设置颜色
'}';

gl_FragColor唯一の組み込み変数で、vec4 タイプですが、rgba 色値を表します。rgba の取值はすべて 0~1 ので、CSS 中の rgb が 0~255 のとは異なります

###4.描画操作

描画操作には 1 つのインターフェースのみですが、機能は非常に強力です:

// 绘制矩形(一个点,但点的尺寸略大)
gl.drawArrays(gl.POINTS, 0, 1);

API の具体情報は以下の通り:

gl.drawArrays(mode, first, count)

mode 指定绘制方式,接受常量:
---
gl.POINTS           孤立点(v0, v1...)
gl.LINES            孤立线段(v0v1, v2v3...)
gl.LINE_STRIP       连续线段(v0v1, v1v2...)
gl.LINE_LOOP        连续线段连成圈(v0v1, v1v2...vnv0)
gl.TRIANGLES        孤立三角形(v0v1v2, v3v4v5...)
gl.TRIANGLE_STRIP   连续三角带(v0v1v2, v2v1v3, v2v3v4...),用来构造复杂模型,如球、树
                    注意:第二个三角形是 v2v1v3,不是 v1v2v3,这是为了保证第二个三角形的绘制也按照逆时针顺序
gl.TRIANGLE_FAN     三角扇(v0v1v2, v0v2v3, v0v3v4),也可以用来构造复杂模型,但在实际应用中不常见,因为需要求出所有三角形的公共顶点 v0

first 指定从哪个顶点开始绘制
---
0 表示从第一个开始,配合缓冲区对象使用(缓冲区对象中可以存放多个顶点信息)

count 指定需要绘制点的个数,同样配合缓冲区对象使用
---
调用 gl.drawArray 时,顶点着色器会被执行 count 次,每次处理一个顶点,顶点着色器执行完毕后片元着色器开始执行,给片元涂色(中间还有光栅化的过程)

特に注意:頂点シェーダーは逐頂点実行され、フラグメントシェーダーは逐フラグメント実行され、1 回のみ実行されるわけではありません

三.attribute 変数

シェーダーソースプログラム中で変数を宣言し、その後 js がこの変数に値を渡し、さらに描画することで、点の位置を動的に修正できます。簡単に言えばこうです

###1.attribute 変数の宣言

// 顶点着色器源程序
var vsSrc = 'attribute vec4 a_Position;' +
    'void main() {' +
    'gl_Position = a_Position;' +   // 设置坐标
    'gl_PointSize = 200.0;' +       // 设置尺寸
'}';
// 片元着色器源程序
//...不变

C 言語の変数宣言方式に類似し、attribute は保存限定子と呼ばれ、vec4 は変数タイプです。変数名 a_Position 中の a_ プレフィックスは変数が attribute 変数であることを表し、もちろんこれは習慣ですが、採用することをお勧めします

P.S.なぜ頂点シェーダーソースプログラム中で attribute 変数を宣言するのか、フラグメント...ではないのか。私たちが点の座標を動的に修正したいからで、座標情報は頂点シェーダーソースプログラム中の gl_Position から来ており、フラグメントシェーダーとは関係がないからです

注意:頂点シェーダーのみが attribute 変数を使用でき、フラグメントシェーダーでは uniform 変数を使用する必要があります。さらに varying 変数もあり、具体の違いは GLSL ES ノートでさらに詳細に説明します

###2.シェーダーの初期化

直接 util を呼び出し、1 行のコードで残りの 7 つの事(createShader から useProgram までの 7 つのステップ)を完了できます

// 1.初始化着色器
glUtil.initShaders(vsSrc, fsSrc);

###3.attribute 変数に値を割り当てる

変数への値割り当ては 2 ステップに分かれ、まず保存位置を取得し、その後値を割り当てます

// 2.给 attribute 变量赋值
// 获取 attribute 变量的存储位置
var a_Position = gl.getAttribLocation(glUtil.program, 'a_Position');
if (a_Position < 0) {
    console.log('Failed to get the storage location of a_Position');
    return;
}
// 把顶点位置传递给 attribute 变量
gl.vertexAttrib3f(a_Position, 0.5, 0.0, 0.0);

###4.描画

// 绘制点
gl.drawArrays(gl.POINTS, 0, 1);

最初のパラメータに gl.POINTS を渡すと孤立点の描画を表し、線分、三角形なども描画できます。後で説明します

四.DEMO

上記のコードを含む完全な例は、以下を参照:http://www.ayqy.net/temp/webgl/attribute/index.html

五.まとめ

現在私たちは頂点座標を動的に修正できますが、もちろん、これは非常にシンプルなステップで、後にはさらに:

  • マウスクリック位置で点を描画するには?

答えは想象的に那么简单ではなく、canvas と WebGL 座標系が異なり、座標を変換する必要があります

  • 点の色を修正するには?

uniform 変数で十分です

  • 複数の点を描画するには

gl.drawArrays(gl.POINTS, 0, 1); をループ?できますか?

これらは後のノートで討論する問題で、まず焦らないでください

参考資料

  • 《WebGL プログラミングガイド》

コメント

コメントはまだありません

コメントを書く