WebGL学習記録(4) ポリゴンのレンダリングと色


参考[webglorg](http://wgld.org/);
原則として上記ページを見ながら学習した記録です。



##ポリゴンのレンダリング
![Screen Shot 2015-10-28 at 17.57.19.png](https://qiita-image-store.s3.amazonaws.com/0/54225/9d984752-d04a-7b84-677a-76d04a21b0d6.png "Screen Shot 2015-10-28 at 17.57.19.png")

三角形のポリゴンを描写するサンプルコード

```javascript
onload = function(){
    // canvasエレメントを取得
    var c = document.getElementById('canvas');
    c.width = 300;
    c.height = 300;

    // webglコンテキストを取得
    var gl = c.getContext('webgl') || c.getContext('experimental-webgl');
    
    // canvasを初期化する色を設定する
    gl.clearColor(0.0, 0.0, 0.0, 1.0);
    
    // canvasを初期化する際の深度を設定する
    gl.clearDepth(1.0);
    
    // canvasを初期化
    gl.clear(gl.COLOR_BUFFER_BIT | 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;
    
    // モデル(頂点)データ
    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);
    
    // 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.drawArrays(gl.TRIANGLES, 0, 3);
    
    // コンテキストの再描画
    gl.flush();
    
    // シェーダを生成する関数
    function create_shader(id){
        // シェーダを格納する変数
        var shader;
        
        // HTMLからscriptタグへの参照を取得
        var scriptElement = document.getElementById(id);
        
        // scriptタグが存在しない場合は抜ける
        if(!scriptElement){return;}
        
        // scriptタグのtype属性をチェック
        switch(scriptElement.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;
            default :
                return;
        }
        
        // 生成されたシェーダにソースを割り当てる
        gl.shaderSource(shader, scriptElement.text);
        
        // シェーダをコンパイルする
        gl.compileShader(shader);
        
        // シェーダが正しくコンパイルされたかチェック
        if(gl.getShaderParameter(shader, gl.COMPILE_STATUS)){
            
            // 成功していたらシェーダを返して終了
            return shader;
        }else{
            
            // 失敗していたらエラーログをアラートする
            alert(gl.getShaderInfoLog(shader));
        }
    }
    
    // プログラムオブジェクトを生成しシェーダをリンクする関数
    function create_program(vs, fs){
        // プログラムオブジェクトの生成
        var program = gl.createProgram();
        
        // プログラムオブジェクトにシェーダを割り当てる
        gl.attachShader(program, vs);
        gl.attachShader(program, fs);
        
        // シェーダをリンク
        gl.linkProgram(program);
        
        // シェーダのリンクが正しく行なわれたかチェック
        if(gl.getProgramParameter(program, gl.LINK_STATUS)){
        
            // 成功していたらプログラムオブジェクトを有効にする
            gl.useProgram(program);
            
            // プログラムオブジェクトを返して終了
            return program;
        }else{
            
            // 失敗していたらエラーログをアラートする
            alert(gl.getProgramInfoLog(program));
        }
    }
    
    // VBOを生成する関数
    function create_vbo(data){
        // バッファオブジェクトの生成
        var vbo = gl.createBuffer();
        
        // バッファをバインドする
        gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
        
        // バッファにデータをセット
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.STATIC_DRAW);
        
        // バッファのバインドを無効化
        gl.bindBuffer(gl.ARRAY_BUFFER, null);
        
        // 生成した VBO を返して終了
        return vbo;
    }

};
```


###初期化処理

```javascript
// canvasエレメントを取得
var c = document.getElementById('canvas');
c.width = 300;
c.height = 300;

// webglコンテキストを取得
var gl = c.getContext('webgl') || c.getContext('experimental-webgl');

// canvasを初期化する色を設定する
gl.clearColor(0.0, 0.0, 0.0, 1.0);

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

// canvasを初期化
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
```

clearDepth メソッドはコンテキストをクリアする際の深度を設定するために使う。


###シェーダとプログラムオブジェクトの生成
頂点シェーダとフラグメントシェーダを生成し、プログラムオブジェクトでリンクさせる。

```javascript
// 頂点シェーダとフラグメントシェーダの生成
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;

```
create_shaderとcreate_program は組み込みメソッドではなく、オリジナル関数であることに注意。

シェーダのソースはこちら

```GLSL
attribute vec3 position;
uniform   mat4 mvpMatrix;

void main(void){
    gl_Position = mvpMatrix * vec4(position, 1.0);
}
```

###VBOの生成と通知
```javascript
// モデル(頂点)データ
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);

```

###座標変換行列の生成と通知
計算にはminMatrix.jsを使用。どのように利用するのかは今回は割愛。

```javascript
// 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);

```

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

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

// コンテキストの再描画
gl.flush();

```

##ポリゴンに色をつけよう

![Screen Shot 2015-10-28 at 19.14.37.png](https://qiita-image-store.s3.amazonaws.com/0/54225/19453066-ed48-97eb-1436-6ef8253751a7.png "Screen Shot 2015-10-28 at 19.14.37.png")


色の情報を持たせるということは、もう一つ頂点バッファを用意する必要がある。

```GLSL
attribute vec3 position;
attribute vec4 color;
uniform mat4 mvpMatrix;
varying vec4 vColor;

void main(void) {
	vColor = color;
	gl_Position = mvpMatrix * vec4(position, 1.0);
}

```
attribute 変数が二つ用意されており、colorが頂点の色座標。
varying は頂点とフラグメントシェーダの橋渡しをしてくれる変数。

このように処理すると、頂点シェーダに入ってくる頂点属性の情報を、フラグメントシェーダに渡すことができる。

ではフラグメントシェーダーはどうなるか。


```GLSL
precision mediump float;

varying vec4 vColor;

void main(void){
	gl_FragColor = vColor;
}

```

precision は数値の精度を指定するためのキーワードで、 precision に続けて精度修飾子を記述することで利用できる。

lowp   ：精度低

mediump：精度中

highp  ：精度高

### 頂点バッファ周りの処理

```javascript
// attributeLocationを配列に取得
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;

// 頂点の位置情報を格納する配列
var vertex_position = [
     0.0, 1.0, 0.0,
     1.0, 0.0, 0.0,
    -1.0, 0.0, 0.0
];

// 頂点の色情報を格納する配列
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を作成

```javascript
// VBOの生成
var position_vbo = create_vbo(vertex_position);
var color_vbo = create_vbo(vertex_color);

// VBOをバインドし登録する(位置情報)
gl.bindBuffer(gl.ARRAY_BUFFER, position_vbo);
gl.enableVertexAttribArray(attLocation[0]);
gl.vertexAttribPointer(attLocation[0], attStride[0], gl.FLOAT, false, 0, 0);

// VBOをバインドし登録する(色情報)
gl.bindBuffer(gl.ARRAY_BUFFER, color_vbo);
gl.enableVertexAttribArray(attLocation[1]);
gl.vertexAttribPointer(attLocation[1], attStride[1], gl.FLOAT, false, 0, 0);

```

HTMLのサンプル

```HTML
<html>
    <head>
        <title>WebGL TEST</title>
        <script src="script.js" type="text/javascript"></script>
        <script src="minMatrix.js" type="text/javascript"></script>
        
        <script id="vs" type="x-shader/x-vertex">
attribute vec3 position;
attribute vec4 color;
uniform   mat4 mvpMatrix;
varying   vec4 vColor;

void main(void){
    vColor = color;
    gl_Position = mvpMatrix * vec4(position, 1.0);
}
        </script>
        
        <script id="fs" type="x-shader/x-fragment">
precision mediump float;

varying vec4 vColor;

void main(void){
    gl_FragColor = vColor;
}
        </script>
    </head>
    <body>
        <canvas id="canvas"></canvas>
    </body>
</html>