arusu0629のブログ

Swift初心者エンジニア

WebGLについて⑬

【前回】 arusu0629.hatenablog.com

複数モデルのレンダリング

複数のモデルをレンダリングする際に新しくVBOを用意する必要はなく、元々使用していたVBOを再利用する

複数のモデルを違う位置にレンダリングするので、操作するのはモデル変換行列。ビュー変換行列とプロジェクション変換行列はどちらのモデルに対しても同じものを適用する

前回までのjavascriptからの変更点を抜粋

  • tmpMatrixにビュー, プロジェクション座標変換行列を掛け合わせたものを保持しておくm.multiply(tmpMatrix, mMatrix, mvpMatrix);
    →どちらのモデルに対しても同じものを使うので再利用する
  • 1つ目のモデル, 2つ目のモデルそれぞれのモデル座標変換行列を用意する
    →1つ目のモデルは(1.5, 0, 0)の位置で2つ目のモデルは反対側の(-1.5, 0, 0)の位置
    ※ この時の注意点として2つ目のモデル座標変換行列を用意するときにm.identity(mMatrix)としている点である。これは初期化処理に当たり、1つ目のモデルのモデル座標変換行列をリセットしている

WebGLについて⑫

【前回】 arusu0629.hatenablog.com

ポリゴンに色を塗る(頂点色の指定)

前回までにただのポリゴンの描画までしてきましたが、今回はその描画したポリゴンに色を付けていきます。

ポリゴンに色を付けるには、頂点情報に「色情報」を追加してあげればOKです!

まずはhtml側のソース

追加した箇所

attribute vec4 color;  // 頂点バッファから色情報を受け取るための変数
varying   vec4 vColor; // フラグメントシェーダに色情報を渡すための変数

// 頂点シェーダ
void main(void) {
    vColor = color; // 頂点バッファ(VBO)から受け取った色情報をフラグメントシェーダに渡すための変数に代入する
}

// フラグメントシェーダ
precision mediump float;

varying vec4 vColor;

void main(void) {
    gl_FragColor = vColor; // 頂点シェーダから渡された色情報を使う
}

精度を指定するprecision precisionは数値の精度を指定するためのキーワードで、続けて精度修飾子を記述することで利用できる
【精度修飾子】
lowp : 精度低
mediump : 精度中
highp : 精度高


続いてjavascript側のソース

追加した箇所

// 位置情報に加えて色情報を取得するために配列として取得する
var attLocation = new Array(2);
attLocation[0] = gl.getAttribLocation(prg, 'position');
attLocation[1] = gl.getAttribLocation(prg, 'color');

// attributeの要素数を配列に格納
var attStride = new Array(2);
attStride[0] = 3;
attStride[1] = 4; // 色情報はvec4

// 頂点の色情報
var vertex_color = [
   1.0, 0.0, 0.0, 1.0,
   0.0, 1.0, 0.0, 1.0,
   0.0, 0.0, 1.0, 1.0
];

// 色情報のvboを生成
gl.bindBuffer(gl.ARRAY_BUFFER, color_vbo);
gl.enableVertexAttribArray(attLocation[1])
gl.vertexAttribPointer(attLocation[1], attStride[1], gl.FLOAT, false, 0, 0);


今回は位置情報に加えて色情報も頂点シェーダに渡す必要があるので、attLocationを配列として用意してあげる
同じようにattStrideも配列として用意して位置情報と色情報の要素数をそれぞれ指定する

頂点の色情報をvertex_colorと配列で定義して、要素数頂点数 × 4で構成されている

続いてVBO(頂点バッファ)の生成部分だが、位置情報と同じようなやり方でcreate_vboメソッドを用いて色情報専用のVBO(color_vbo)を生成する
あとはバインドして登録すればhtml側で定義されたattribute変数に渡すことができ、各頂点に色情報が付与される

【実行結果】 f:id:arusu0629:20180408202122p:plain

【続き】 arusu0629.hatenablog.com

WebGLについて⑪

【前回】 arusu0629.hatenablog.com

ポリゴンのレンダリング

■初期化処理

    // canvasを初期化する際の深度を設定する
    gl.clearDepth(1.0);

    // canvasを初期化
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
  • clearDepth
    →3次元空間を扱う場合に奥行きに関する情報をクリアするために使われるメソッド。clearメソッドに震度に関してクリアするために組み込み定数のgl.DEPTH_BUFFER_BITを使う



■シェーダとプログラムオブジェクトの生成

    // 頂点シェーダとフラグメントシェーダを生成
    var v_shader = create_shader('vs');
    var f_shader = create_shader('fs');

    // プログラムオブジェクトの生成とリンク
    var prg = create_program(v_shader, f_shader);

    // attributeLocationの取得
    var attLocation = gl.getAttribLocation(prg, 'position');

    // attributeの要素数(この場合はxyzの3要素)
    var attStride = 3;
  • attLocationattStride
    →頂点シェーダにデータを渡す際に必要となる情報を保持するのに使用する

attLocation: そのデータが何番目のデータなのかを保持するために使う

attStride: データがいくつの要素からなるのかを保持するために使う


■頂点バッファの生成と通知

    // モデル(頂点)データ
    var vertex_position = [
        0.0,  1.0, 0.0,
        1.0,  0.0, 0.0,
       -1.0,  0.0, 0.0
    ];

    // VBOの生成
    var vbo = create_vbo(vertex_position);

    // VBOをバインド
    gl.bindBuffer(gl.ARRAY_BUFFER, vbo);

    // attribute属性を有効にする
    gl.enableVertexAttribArray(attLocation);

    // attribute属性を登録
    gl.vertexAttribPointer(attLocation, attStride, gl.FLOAT, false, 0, 0);
  • gl.enableVertexAttribArray
    →目的の属性を有効にすることが出来る
  • gl.vertexAttribPointer
    →シェーダにデータを登録する
    第1引数 :何番目のであるかのインデックス
    第2引数 :データの要素数
    第3引数 :データの型
    第4~6引数:基本的にはこの形で、メモリ上の参照を駆使して特殊なデータの受け渡し方をしたい場合に使用することがある

    ※このメソッドを実行する際に対象のVBOを必ずバインドする必要がある


    ■座標変換行列の生成と通知
    // minMatrix.jsを用いた行列関連処理
    // matIVオブジェクトを生成
    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());

    // ビュー座標変換行列
    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);

    // uniformLocationの取得
    var uniLocation = gl.getUniformLocation(prg, 'mvpMatrix');

    // uniformLocationへの座標変換行列を登録
    gl.uniformMatrix4fv(uniLocation, false, mvpMatrix);
  • gl.getUniformLocation
    →attribute変数同様にuniform変数のインデックスを取得するために使用される

  • gl.uniformMatrix4fv
    →uniform変数のインデックスにデータを登録して頂点シェーダにデータが正しく受け渡せるようにする。第2引数のブール値は行列を転置するかどうかの値(※trueにするとクラッシュする場合がある)



■モデルの描画とコンテキストの再描画

    // モデルの描画
    gl.drawArrays(gl.TRIANGLES, 0, 3);

    // コンテキストの再描画
    gl.flush();
  • gl.drawArrays
    →モデルをバッファ上に描画する
    ※このメソッド実行時にはまだ画面上にはポリゴンは描画されておらず下記で説明するflushが実行されることでレンダリング結果が画面上に描画される

    第1引数:頂点をどのように利用して描画するかの組み込み定数
    第2引数:何番目の頂点から利用するかのオフセット値
    第3引数:いくつの頂点を描画するかの値

  • flush
    →コンテキストの再描画



■全ソースコード
■実行結果f:id:arusu0629:20180401192012p:plain

WebGLについて⑩

【前回】 arusu0629.hatenablog.com

座標変換行列

モデル変換において、移動・回転・拡大縮小を行う順序には十分に注意しないといけない
具体的には、拡大縮小 -> 回転 -> 移動 という順序で変換を行えば、回転・拡大縮小した状態のモデルを任意の位置に移動させることが出来る

ただし、OpenGLには行列が列オーダーになっているため、掛け合わせる順序が真逆になる

つまり、拡大縮小 -> 回転 -> 移動 という順序で掛け合わせると結果は意図したものと全く違うものになるため、正しくは 移動 -> 回転 -> 拡大縮小 という順序で掛け合せれば良いということ

■モデル・ビュー・プロジェクション行列の掛け合わせる順序
mvpMatrixと各変換行列を掛け合わせたものを表記することが多いが、掛け合わせる順序はmvpではなく、 p > v > m の順序で掛け合わせる

【続き】 arusu0629.hatenablog.com

WebGLについて⑨

【前回】 arusu0629.hatenablog.com

モデルデータと頂点属性
  • 頂点属性
    →頂点が持つ様々な要素のこと
    例:位置情報、頂点色、法線、テクスチャ座標


■VBOの生成

  • まずVBOの元になるバッファオブジェクトをcreateBufferメソッドを用いて生成する
  • バッファを操作するにはWebGLにバインドする必要があるためbindBufferメソッドを使ってバインドする。第1引数に組み込み定数のgl.ARRAY_BUFFERを指定することでVBOが生成できる
  • 続いて生成したVBOに頂点情報として渡されたdataをセットするためにbinderDataを実行する。その際にFloat32Arrayで配列を作り直しているのは浮動小数点を扱うためである
  • gl.STATIC_DRAWという組み込み定数はそのバッファがどのような頻度で内容を変更されてるのかを定義する。VBOの場合は、基本的にモデルデータはそのままで何度も利用することになるためこの定数を指定する
  • WebGLバインド出来るバッファは1度に1つだけなので、他のバッファに操作を加える場合は適宜バッファをバインドし直すため、最後にバッファのバインドを無効にして予期せぬエラーを発生させないようにする



【まとめ】
WebGLでは頂点について自由に属性を付加することが出来る。付加した属性の数だけVBOが必要になる
頂点属性のデータは1次元配列として用意する。その配列データをもとにVBOを生成する
まずはバッファを作り、WebGLにバインドすることで操作を可能にした後で、配列データをバッファにセットしてVBOを生成する。バインドは1度に1つしか出来ないため、操作が終わった後にはバインドを解除して予期せぬエラーを防ぐ

【続き】 arusu0629.hatenablog.com

WebGLについて⑧

【前回】 arusu0629.hatenablog.com

シェーダのコンパイルとリンク

■ポリゴンを描画するまで〜

  1. HTMlからcanvas要素を取得
  2. canvasからWebGLコンテキストの取得
  3. シェーダのコンパイル
  4. モデルデータを用意
  5. 頂点バッファ(VBO)の生成と通知
  6. 座標変換行列の生成と通知
  7. 描画命令の発行
  8. canvasを更新してレンダリング

1,2については以下の記事でやってきたので割愛します。 arusu0629.hatenablog.com

今回は3.についての記事です!

※上記プログラムはWebGLコンテキストを既に取得しているとみなして書いています

  • シェーダを生成するにはWebGLのメソッドであるcreateShaderを使う
  • 頂点シェーダの場合はgl.VERTEX_SHADERを引数に指定
  • フラグメントシェーダではgl.FRAGMENT_SHADERを引数に指定
  • 生成したシェーダをソースに割り当てるためにshaderSourceメソッドを実行する
  • シェーダをコンパイルするにはcompileShaderメソッドに引数として生成したシェーダを渡してあげればOK


プログラムオブジェクト

WebGLプログラムと各シェーダとのデータのやり取りを管理してくれるオブジェクト

  • まずはプログラムオブジェクトを生成するためにcreateProgramメソッドを行う
  • 生成したプログラムオブジェクトに各シェーダを割り当てる
  • その後はlinkProgramメソッドを使ってシェーダをリンクする
  • リンクが上手く出来たかを組み込み定数のLINK_STATUSを見て判断する
  • リンクが問題なければuseProgramメソッドを実行。これをしないとWebGLに正しく認識されない

【まとめ】
ここまでの内容として

  1. HTMLソースを用意
  2. javascriptファイルの参照とシェーダのソースを記述
  3. javascriptにはシェーダの生成・コンパイルを行うメソッドとリンクを管理するプログラムオブジェクトのメソッドを用意

【続き】 arusu0629.hatenablog.com

WebGLについて⑦

【前回】 arusu0629.hatenablog.com

頂点バッファの基礎
  • ローカル座標
    →頂点がどのように配置されているのかを表した座標
    例:(1.0, 0.0, 0.0) ⇔ 原点からX方向に1.0の距離に存在する
    ※原点を(0.0, 0.0, 0.0)とした場合

  • 頂点バッファ(VBO:vertex buffer object)
    WebGLのプログラム内で、頂点のローカル座標を格納するための入れ物
    他にもフレームバッファやインデックスバッファなどがある

    頂点バッファの役割には位置情報を保持する以外にも、頂点が持つ法線の情報や、に関する情報、他にもテクスチャ座標や頂点の重さなど、ありとあらゆる頂点に関する情報を格納することが出来る

■頂点バッファの処理の流れ

  1. 頂点の各情報を配列に格納
  2. WebGLのメソッドを使ってVBOを作成
  3. WebGLのメソッドを使ってVBOに配列のデータを転送
  4. 頂点シェーダ内のattribute変数とVBOを紐付ける

1.について
頂点の位置情報には(x, y, z)の3つの情報が必要なため頂点数×3の要素数の配列が必要
ここで配列は多次元配列にせず1次元配列にすること。VBOの生成には1次元配列を使う

【まとめ】
WebGLでは実装したい頂点情報の数だけ、頂点バッファ(VBO)が必要になる
頂点情報というのは位置情報だけでなく、色や法線の情報などありとあらゆる頂点に関する情報をVBOに格納することが出来る
頂点シェーダ内で定義されたattribute修飾子付きの変数にVBOを紐付けることが必要

【続き】 arusu0629.hatenablog.com