どうも、TIGER-TANIOKAです。

今回は、WebGLさわってみます。
がんばります。

デモ

1.データを管理するクラスをつくります。

頂点座標、color値、index値を保持するクラスをつくります。

  var COLUMN_NUM = 100;
  var LINE_NUM = 100;

  var TigerData = (function(){
    var vertexArr = [];
    var indexArr = [];
    var colorArr = [];
    var texCoord = [];
    var constructor = function(){

    }
    constructor.prototype = {
      'addIndex' : addIndex,
      'getIndex' : getIndex,
      'addVertex' : addVertex,
      'getPosition' : getPosition,
      'getVertexLength' : getVertexLength,
      'addColor' : addColor,
      'getColor' : getColor,
      'addTexCoord' : addTexCoord,
      'getTexCoord' : getTexCoord
    }
    return new constructor();


    function addTexCoord(_arr){
      for(var i = 0; i < _arr.length; i++){
        texCoord.push(_arr[i]);
      }
    }


    function getTexCoord(){
      return texCoord;
    }


    function addColor(_arr){
      for(var i = 0; i < _arr.length; i++){
        colorArr.push(_arr[i]);
      }
    }


    function getColor(){
      return colorArr;
    }


    function addIndex(_arr){
      for(var i = 0; i < _arr.length; i++){
        indexArr.push(_arr[i]);
      }
    }


    function getIndex(){
      return indexArr;
    }


    function addVertex(_vtx , _index){
      vertexArr[_index] = _vtx;
    }


    function getVertexLength(){
      return vertexArr.length;
    }



    function getPosition(){
      return vertexArr;
    }

    })()

2.頂点座標、color値、index値生成

100 * 100の四角形を描画するのに必要な頂点座標、color値、index値つくっていきます。 index値はIBO(Index Buffer Object)で使います。

IBO(Index Buffer Object)とは

重なる頂点座標(全く同じ頂点座標)を共通で使うことができます。
例えば四角形を描画したい場合ですが
四角形を描画するには二つの三角形が必要です。
二つ三角形を描画するには頂点座標が6つ必要になります。
四角形を描画したのに頂点座標が6つって無駄ですよね。

そこでIBOです。

alt
赤色の頂点座標を共通で使うことができます
結果、頂点座標を4つに減らすことができるのです。

    createIndex();
    for(var y = 0, _rectIndex = 0; y < LINE_NUM + 1; y++){
      for(var x = 0; x < COLUMN_NUM + 1; x++){
        new TigerPoint(x, y , _rectIndex);
        _rectIndex++;
      }
    }


    function createIndex(){
      for(var y = 0; y < LINE_NUM; y++){
        for(var x = 0; x < COLUMN_NUM; x++){
          var _index1 = x + (y * (COLUMN_NUM + 1));
          var _index2 = _index1 + COLUMN_NUM + 1;
          var _index3 = _index1 + COLUMN_NUM + 2;
          var _index4 = _index1 + COLUMN_NUM + 2;
          var _index5 = _index1;
          var _index6 = _index1 + 1;

          TigerData.addIndex([
            _index1 , _index2 , _index3,
            _index4 , _index5 , _index6
          ]);
        }
      }
    }

    ~~~~~~~~~~~~~~~~~~~~~~


    var TigerPoint = function(_x , _y , _index){

      var constructor = function(){
        var _p = [];
        var _texP = [];
        this.x = (_x / COLUMN_NUM) * 2 - 1;
        this.y = (_y / LINE_NUM) * 2 - 1;

        _p = [
          this.x , this.y ,0
        ];

        // テクスチャ正規化 0 ~ 1
        _texP = [
          (_x / COLUMN_NUM) , (_y / LINE_NUM) * -1
        ];

        TigerData.addVertex(_p[0] , _index * 3);
        TigerData.addVertex(_p[1] , (_index * 3 + 1));
        TigerData.addVertex(_p[1] , (_index * 3 + 2));
        TigerData.addTexCoord(_texP);


        switch(_index % 3){
          case 0:
            TigerData.addColor([
              1.0, 0.0, 0.0, 1.0
            ]);
          break;
          case 1:
            TigerData.addColor([
              0.0, 1.0, 0.0, 1.0
            ]);

          break;
          case 2:
            TigerData.addColor([
              0.0, 0.0, 1.0, 1.0
            ]);
          break;
        }

        EventDispatcher.addEvent('vtxUpdate' , vtxUpdateHandler)

      }
      constructor.prototype = {}
      return new constructor();


      function vtxUpdateHandler(){
        this.x = (_x / COLUMN_NUM) * 2 - 1;
        this.y = (_y / LINE_NUM) * 2 - 1;

        _p = [
          (Math.random()* 0.1 - 0.05) + this.x , (Math.random()* 0.1 - 0.05) + this.y ,0
        ];
        TigerData.addVertex(_p[0] , _index * 3);
        TigerData.addVertex(_p[1] , (_index * 3 + 1));
        TigerData.addVertex(_p[1] , (_index * 3 + 2));
      }

3.コンテキストの取得、プログラムオブジェクト生成、シェーダー生成

コンテキストを取得、シェーダーをコンパイルしコンテキストに紐付けていきます。

コンテキストとは

WebGLコンテキストは描画等様々な関数、プロパティをもってます。
例)
gl.viewport()
gl.drawArrays()
gl.drawElements()
等、他にもたくさんあります。
コンテキストを取得することによりメソッドが使えるようになります。

プログラムオブジェクトとは

プログラムオブジェクトをつかうことにより、
バーテックスシェーダー、フラグメントシェーダー間での
共通(varying)変数を使用することが可能になります。

また、WebGLコンテキストと各シェーダーとのデータのやりとりで必要になります。
uniform変数、attribute変数等。
例)
gl.getAttribLocation()
gl.getUniformLocation()

  //シェーダー
    <script id="vs" type="x-shader/x-vertex">
      attribute vec3 position;
      attribute vec4 color;
      attribute vec2 textureCoord;
      uniform mat4 mvpMatrix;
      varying vec4 vColor;
      varying vec2 vTextureCoord;


      void main(void){
        vColor = color;
        vTextureCoord = textureCoord;
        gl_Position = mvpMatrix * vec4(position.x ,position.y, 0.0, 1.0);
      }

    </script>
    <script id="fs" type="x-shader/x-fragment">
      precision mediump float;
      uniform sampler2D texture;
      varying vec2 vTextureCoord;
      varying vec4 vColor;
      void main(void){
        vec4 txrColor = texture2D(texture, vTextureCoord);
        gl_FragColor = vColor * txrColor;
      }
    </script>
    //コンテキストの取得
    cvs = doc.getElementById('world');
    gl = cvs.getContext('webgl') || cvs.
    getContext('experimental-webgl');

    //シェーダーのコンパイル
    vtxShader = createShader('vs');
    frgShader = createShader('fs');

    //プログラムオブジェクト生成、シェーダーリンク
    prg = createProgram(vtxShader , frgShader);


    function createShader(_id){

      var _shader;
      var _ele = doc.getElementById(_id);

      if(!_ele) return;

      var _str = '';
      var _script_child = _ele.firstChild;


      while(_script_child){
        if(_script_child.nodeType == 3){
          _str += _script_child.textContent;
        }
        _script_child = _script_child.nextSibling;
      }

      switch(_ele.type){
        case 'x-shader/x-vertex':
        _shader = gl.createShader(gl.VERTEX_SHADER);
        break;
        case 'x-shader/x-fragment':
        _shader = gl.createShader(gl.FRAGMENT_SHADER);
        break;
      }

      gl.shaderSource(_shader, _str);
      gl.compileShader(_shader);

      if(gl.getShaderParameter(_shader , gl.COMPILE_STATUS)){
        return _shader;
      }else{
        alert('shaderError' + gl.getShaderInfoLog(_shader));
      }
    }


    function createProgram(_vs , _fs){
      var _prg = gl.createProgram();
      gl.attachShader(_prg , _vs);
      gl.attachShader(_prg , _fs);
      gl.linkProgram(_prg);
      if(gl.getProgramParameter(_prg, gl.LINK_STATUS)){
        gl.useProgram(_prg);
        return _prg;
      }else{
        alert('programError' + gl.getProgramInfoLog(_prg));
      }

    }

4.attribute変数、uniform変数

シェーダー側へ渡す変数を定義

attribute変数、uniform変数とは

各頂点ごとに異なったデータを渡したい時に使う変数定義
例えば3つの頂点があった場合に、 1つめは赤色のカラー情報、2つめは青、3つめは緑といったときは、 attribute変数を使います。

逆に全ての頂点で必要な値が共通で使える場合はuniform変数を定義
といった使い方をします。

    //attribute変数の紐付け
    attLocation = gl.getAttribLocation(prg , 'position');
    attColorLocation = gl.getAttribLocation(prg , 'color');
    attTexLocation = gl.getAttribLocation(prg , 'textureCoord');


    //uniform変数の紐付け
    uniLocation = gl.getUniformLocation(prg , 'mvpMatrix');
    uniTexLocation = gl.getUniformLocation(prg , 'texture');

5.VBO ( vertex buffer object )生成、IBO(Index Buffer Object)生成 ,テクスチャ生成

VBO、IBO、テクスチャを生成。
これでシェーダー側でテクスチャ、attribute変数が使えるようになります。

VBO ( vertex buffer object )とは

頂点バッファのことをいいます。
よく使用するのが、座標、カラー値等ですね。
各頂点に渡したい値を格納するところってな感じで自分は認識してます。

    //vbo生成
    vbo = createVbo(TigerData.getPosition());
    setAttribute(vbo , attLocation , vtxStride);
    colorVbo = createVbo(TigerData.getColor());
    setAttribute(colorVbo , attColorLocation , colorStride);
    texVbo = createVbo(TigerData.getTexCoord());
    setAttribute(texVbo , attTexLocation , texStride);

    //ibo生成
    vbo = createVbo(TigerData.getPosition());
    ibo = createIbo(TigerData.getIndex());
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo);

    win.addEventListener('mousemove' , mouseMoveHandler);


    // 有効にするテクスチャユニットを指定
    gl.activeTexture(gl.TEXTURE0);

    img = new Image();
    img.onload = imgLoadDone;
    img.src = '画像のぱす';



    function setAttribute(_vbo , _attL ,  _attS){
      gl.bindBuffer(gl.ARRAY_BUFFER , _vbo);
      gl.enableVertexAttribArray(_attL);
      gl.vertexAttribPointer(_attL , _attS , gl.FLOAT , false, 0  , 0);
    }

    function createVbo(_data){
      var _vbo = gl.createBuffer();
      gl.bindBuffer(gl.ARRAY_BUFFER , _vbo);
      gl.bufferData(gl.ARRAY_BUFFER , new Float32Array(_data) , gl.DYNAMIC_DRAW);
      gl.bindBuffer(gl.ARRAY_BUFFER , null);
      return _vbo;
    }

    function imgLoadDone(){
      var tex = gl.createTexture();
      gl.bindTexture(gl.TEXTURE_2D, tex);
      gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);
      gl.generateMipmap(gl.TEXTURE_2D);
      gl.bindTexture(gl.TEXTURE_2D, null);
      texture = tex;
      draw();
    }

6.描画

アップデートしたvbo引っ付けて描画します。

  function draw(){
    var tmpMtx = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
    gl.bindTexture(gl.TEXTURE_2D, texture);
    gl.uniform1i(uniTexLocation, 0);
    vbo = createVbo(TigerData.getPosition());
    setAttribute(vbo , attLocation , vtxStride);
    gl.uniformMatrix4fv(uniLocation , false , tmpMtx);
    gl.drawElements(gl.TRIANGLES, TigerData.getIndex().length, gl.UNSIGNED_SHORT, 0);
    gl.flush();
  }

10000個の頂点をうごかしてみました。
けっこう動きました。
たのしかったです。
今回はこのへんで。