WebGL+HTML5で3DCGを勉強してみた! Part3

    どうも。営業部の伊藤です。

    長期化しておりますが、、、続けます!!!

    Part2でSTEP3の「シェーダを生成&コンパイル」まで説明が終わりましたので、今回のブログではSTEP4の「モデルデータを用意」とSTEP5の「モデルデータからVBO(VertexBufferObject)を生成&バインド」を書きます。

    早速モデルデータを用意したいところですが、その前にプログラムオブジェクトを作成する必要があります。
    プログラムオブジェクトとはなんぞや??
    Part1のフラグメントシェーダ(ピクセルシェーダ)説明部分、Part2のvarying説明部分でも書きましたが、varying修飾子変数を利用してシェーダ間で頂点情報を橋渡しする必要があります。
    この橋渡しを実現するためのオブジェクトがプログラムオブジェクトであり、WebGL側との連係もこのオブジェクトを利用します。

    var vShader = setShader(“vertex”);
    var fShader = setShader(“fragment”);

    shaderProgram = gl.createProgram();
    gl.attachShader(shaderProgram, vShader);
    gl.attachShader(shaderProgram, fShader);
    gl.linkProgram(shaderProgram);

    setShader関数でバーテックスシェーダ(頂点シェーダ)とSTEP3のフラグメントシェーダ(ピクセルシェーダ)の生成&コンパイルまで実施し、その後にプログラムオブジェクトを生成(createProgram)しています。
    そして、作成したプログラムオブジェクト(shaderProgram)に各々のシェーダを割り当てた(attachShader)後にリンク(linkProgram)しています。
    これでパラメータ受け渡しの準備は完了しましたので、本題の「モデルデータの用意」と「VBO(VertexBufferObject)の生成&バインド」に進みます。


    【モデルデータ】
    モデルデータとは3DCG空間に表示するオブジェクトの頂点情報(座標、法線、テクスチャ座標、頂点色など)です。
    三次元空間上にオブジェクトを表示するため、位置情報が無ければ何も表示できません。よって位置情報は必須です。

    【VBO(VertexBufferObject)】
    VBOは別名頂点バッファ(VertexBuffer)とも呼ばれ、命名の通り頂点情報を保有するためのバッファとなります。
    このVBOにバッファリングした情報は、最終的にはバーテックスシェーダ(頂点シェーダ)に通知します。

    「モデルデータの用意」と「VBO(VertexBufferObject)の生成&バインド」部分は以下になります。

    vboPosition = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, vboPosition);
    position = [
    -1.0, -1.0, 1.0,
    1.0, -1.0, 1.0,
    1.0, 1.0, 1.0,
    -1.0, 1.0, 1.0,

    -1.0, -1.0, -1.0,
    -1.0, 1.0, -1.0,
    1.0, 1.0, -1.0,
    1.0, -1.0, -1.0,

    -1.0, 1.0, -1.0,
    -1.0, 1.0, 1.0,
    1.0, 1.0, 1.0,
    1.0, 1.0, -1.0,

    -1.0, -1.0, -1.0,
    1.0, -1.0, -1.0,
    1.0, -1.0, 1.0,
    -1.0, -1.0, 1.0,

    1.0, -1.0, -1.0,
    1.0, 1.0, -1.0,
    1.0, 1.0, 1.0,
    1.0, -1.0, 1.0,

    -1.0, -1.0, -1.0,
    -1.0, -1.0, 1.0,
    -1.0, 1.0, 1.0,
    -1.0, 1.0, -1.0,
    ];
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(position), gl.STATIC_DRAW);

    vboSurface = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, vboSurface);
    var surface = [
    0.0, 0.0,
    1.0, 0.0,
    1.0, 1.0,
    0.0, 1.0,

    1.0, 0.0,
    1.0, 1.0,
    0.0, 1.0,
    0.0, 0.0,

    0.0, 1.0,
    0.0, 0.0,
    1.0, 0.0,
    1.0, 1.0,

    1.0, 1.0,
    0.0, 1.0,
    0.0, 0.0,
    1.0, 0.0,

    1.0, 0.0,
    1.0, 1.0,
    0.0, 1.0,
    0.0, 0.0,

    0.0, 0.0,
    1.0, 0.0,
    1.0, 1.0,
    0.0, 1.0,
    ];
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(surface), gl.STATIC_DRAW);

    vboIndex = gl.createBuffer();
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, vboIndex);
    var index = [
    0, 1, 2,
    0, 2, 3,
    4, 5, 6,
    4, 6, 7,
    8, 9, 10,
    8, 10, 11,
    12, 13, 14,
    12, 14, 15,
    16, 17, 18,
    16, 18, 19,
    20, 21, 22,
    20, 22, 23
    ];
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(index), gl.STATIC_DRAW);

    cubeTexture = gl.createTexture();
    cubeImage = new Image();
    cubeImage.src = “./images/logo.gif”;
    cubeImage.onload = function () { mappingTexture(cubeTexture, cubeImage) }

    数字ばかりで頭が痛くなりますね。
    これを理解するのに髪の毛が50~100本ほど抜けたのを覚えています。。。

    モデルデータは数値がマトリックス上になっているものです。(分かり易くするため改行していますが、実際は単純な一次元配列です)
    座標、法線、テクスチャ座標、頂点色などそれぞれモデルデータが必要になります。
    分かり易い、頂点座標のモデルデータについて細かく書きます。
    今回は立方体オブジェクトを作成するため、正方形の面が合計6つ必要となりますので、モデルデータは以下のようになります。

    「X、Y、Zの座標情報」×「4つの頂点」×「6つの面」=> 72個の座標情報

    position = [
    -1.0, -1.0, 1.0,
    1.0, -1.0, 1.0,
    1.0, 1.0, 1.0,
    -1.0, 1.0, 1.0,

    -1.0, -1.0, -1.0,
    -1.0, 1.0, -1.0,
    1.0, 1.0, -1.0,
    1.0, -1.0, -1.0,

    -1.0, 1.0, -1.0,
    -1.0, 1.0, 1.0,
    1.0, 1.0, 1.0,
    1.0, 1.0, -1.0,

    -1.0, -1.0, -1.0,
    1.0, -1.0, -1.0,
    1.0, -1.0, 1.0,
    -1.0, -1.0, 1.0,

    1.0, -1.0, -1.0,
    1.0, 1.0, -1.0,
    1.0, 1.0, 1.0,
    1.0, -1.0, 1.0,

    -1.0, -1.0, -1.0,
    -1.0, -1.0, 1.0,
    -1.0, 1.0, 1.0,
    -1.0, 1.0, -1.0,
    ];

    次に上記モデルデータをVBO(VertexBufferObject)に設定するため、VBOを生成します。

    vboPosition = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, vboPosition);

    createBufferでバッファオブジェクトを生成し、bindBufferで生成したバッファオブジェクトをWebGLにバインドしています。
    WebGLにバインドできるバッファは一度に一種類だけになるため、他のバッファをバインドしたい場合は都度バインドする必要がありますので注意してください。
    これでVBOの生成までできたので、bufferDataでモデルデータ(position)をVBOに設定します。

    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(position), gl.STATIC_DRAW);

    3DCGの世界では小数点以下の数値の精度が重要になるため、Float32Arrayで浮動小数点としてデータを渡しています。
    また、今回のオブジェクト(立方体)は静的オブジェクトとなるため、モデルデータは静的(STATIC_DRAW)で指定しています。
    この一連の流れでモデルデータの用意、VBOへのデータセットまでが完了(頂点シェーダを利用できる状態)となりますが、まだシェーダ自体にデータを渡せている訳ではありません。
    シェーダへのデータの受け渡しはSTEP6「座標変換行列を生成&通知」で書かせていただきます。

    その他のモデルデータであるvboSurfaceはテクスチャ座標の情報、vboIndexはエレメントの情報となります。
    最後の以下ロジックではテクスチャへ画像データをマッピングしております。

    cubeTexture = gl.createTexture();
    cubeImage = new Image();
    cubeImage.src = “./images/logo.gif”;
    cubeImage.onload = function () { mappingTexture(cubeTexture, cubeImage) }

    残すところあと3STEP(6~8)!!
    次で終わるはず!!

    ではでは。