テクスチャマッピング解説
glではポリゴンに貼り付けることができる
どんな画像が扱えるか
WebGL では、
HTML で一般的に利用できる画像フォーマット
(gif、jpg、pngなど)
のイメージデータをそのままテクスチャとして利用可能
canvasつかったりとかも可能
WebGL テクスチャの制約
htmlで埋め込める画像なら使える
ただし WebGL のテクスチャにはちょっとした注意点があり、
利用できる画像データのサイズが2 の累乗である必要があります。
ちょっとした工夫をすれば 2 の累乗以外の画像データを使うことも可能
画像の読み込みには時間がかかる
テクスチャに画像データを適用する処理は、画像の読み込みが完了した後にやる
ちょっと特殊な記述を行なわなければなりません。
さて、これでテクスチャオブジェクトを生成する方法はわかりました
テクスチャ座標と頂点属性
しかし、肝心なのはこのテクスチャをどうやってポリゴンに貼り付けるかです。
ポリゴンにテクスチャを貼り付けるためには、
ポリゴンを形成している頂点に対して、テクスチャをどのように貼り付けたらいいのか
という情報を持たせておく必要があります。
これは、頂点に対して新しい頂点属性を付加しなければならないということを意味します。 頂点属性を付加するには、VBOを用意しないといけない 今回当たらに追加するVBOは、「頂点のテクスチャ座標」
テクスチャ座標は、
画像の上の縦横を、正規化した座標で指定する
0 ~ 1 の範囲
(0.0, 0.0)みたいなことになる
座標系が面倒
ふつう、パソコンでの座標系は、左上が原点
https://gyazo.com/035a1b6fea86fb0d88f9ef3bbc29aee6
ブラウザとか画像とか、普通はこう
しかし、WebGL のテクスチャの座標系は、原点は左下
数学近い
そういえばカリングも反時計回りがデフォルト
2つめの軸が反転している
https://gyazo.com/0201e0f82ae4c0f1cebc09edfc7f6653
javascript を修正
さて、それではプログラムをテクスチャが利用できる状態に修正していきましょう。
今回はライティングを全部切っておきます。
レンダリングするのはテクスチャを貼り付けた板状のポリゴンだけ
トーラスや球体でやってもいいのですが若干紛らわしい
初心に戻り簡素なポリゴンモデルでやってみます
単なる板ポリ一枚描画するだけですので今回はカリングも切っておきます
まずは頂点データの準備
code:js
// attributeLocationを配列に取得
var attLocation = new Array();
attLocation0 = gl.getAttribLocation(prg, 'position'); attLocation1 = gl.getAttribLocation(prg, 'color'); attLocation2 = gl.getAttribLocation(prg, 'textureCoord'); // attributeの要素数を配列に格納
var attStride = new Array();
// 頂点の位置
var position = [
-1.0, 1.0, 0.0,
1.0, 1.0, 0.0,
-1.0, -1.0, 0.0,
1.0, -1.0, 0.0
];
// 頂点色
var color = [
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
];
// テクスチャ座標
var textureCoord = [
0.0, 0.0,
1.0, 0.0,
0.0, 1.0,
1.0, 1.0
];
// 頂点インデックス
var index = [
0, 1, 2,
3, 2, 1
];
// VBOとIBOの生成
var vPosition = create_vbo(position);
var vColor = create_vbo(color);
var vTextureCoord = create_vbo(textureCoord);
var iIndex = create_ibo(index);
// VBOとIBOの登録
set_attribute(VBOList, attLocation, attStride);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, iIndex);
四つの頂点からなる四角形ポリゴンを定義しています。
頂点データが配列として準備できたら、
いつものように VBO と IBO を生成して登録しておきます。
これでシェーダ内で attribute 変数として処理されるデータの準備は OK です。
uniform 変数としてシェーダ内で扱われるデータの準備
座標変換行列を準備すれば頂点を処理することが可能になります。
今回ライティングはやらない
逆行列やライトの位置など、前回まで使っていたようなライティングに関するデータはいりません。
画像の中身をuniformでわたさないといけない
uniform 関連処理
code:js
// uniformLocationを配列に取得
var uniLocation = new Array();
uniLocation0 = gl.getUniformLocation(prg, 'mvpMatrix'); uniLocation1 = gl.getUniformLocation(prg, 'texture'); 今回利用する uniform 変数は二つ。一つは座標変換行列を扱うために使います。そしてもう一つはテクスチャオブジェクトのデータをシェーダにプッシュするために使います。
テクスチャを有効にする
テクスチャにはユニットという概念があります。
これはテクスチャに番号をつけて管理するためのもので、
既定では 0 番のテクスチャユニットが有効になります。
テクスチャユニットは、複数のテクスチャを扱う場合などに力を発揮しますが、
今回はとりあえず使うテクスチャが一つなので、そのまま 0 番目のユニットを使います。
特定のテクスチャユニットを有効化するには、 activeTexture メソッドを使います。
テクスチャユニットを有効にする
code:js
// 有効にするテクスチャユニットを指定
gl.activeTexture(gl.TEXTURE0);
このとき引数に指定されている gl.TEXTURE0 という組み込み定数は、そのままテクスチャのユニット番号を表しています。
1 番目のテクスチャユニットを有効化したければ gl.TEXTURE1 とすればいいわけですね。
ただ、特別な理由がない限りは、テクスチャユニットは小さい数値から順番に使っていくようにしましょう。
テクスチャデータをシェーダに送る
さてそろそろ大詰めです。適切なテクスチャユニットを有効にしたら、次にするべきはテクスチャを WebGL にバインドすることです。
これは、テクスチャにイメージデータを適用する際にもやりましたので簡単ですね。
テクスチャを WebGL にバインドする
code:js
// テクスチャをバインドする
gl.bindTexture(gl.TEXTURE_2D, texture);
そして、バインドしたテクスチャの情報をシェーダに送るために、uniform 変数としてシェーダに送る処理が必要です。
あらかじめ取得してあった uniformLocation を使って、テクスチャデータを送る処理は次のようになります。
テクスチャデータをシェーダに送る
code:js
// uniform変数にテクスチャを登録
gl.uniform1i(uniLocation1, 0); ここでのポイントは、行列やベクトルをシェーダに送るのとは違い、
行列やベクトルは、jsの配列とかを渡すが
あくまでもテクスチャユニットの番号を送っているだけという点です。
uniform1i メソッドは、その名称からもわかるとおり、一つの整数値をシェーダに送るために使われるメソッドです。
第二引数を見ればわかるとおり、シェーダに送られるのは 0 という整数値一つだけです。
これは有効化されているテクスチャユニットの番号と一致していますね。
シェーダを修正する
さて続いてはシェーダのソースを修正します。まずは頂点シェーダ。
頂点シェーダのソース
code:glsl
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, 1.0);
}
頂点シェーダでは、
attribute 修飾子付き変数としてプログラムから受け取るのは
頂点の位置、頂点の色、そして頂点のテクスチャ座標を
また、フラグメントシェーダには頂点の色とテクスチャ座標をそのまま渡します。
フラグメントシェーダのソース
code:glsl
precision mediump float;
uniform sampler2D texture;
varying vec4 vColor;
varying vec2 vTextureCoord;
void main(void){
vec4 smpColor = texture2D(texture, vTextureCoord);
gl_FragColor = vColor * smpColor;
}
フラグメントシェーダでは uniform 修飾子付き変数としてテクスチャデータが入ってきます。
このとき、変数の型として sampler2D が使われることに注意しましょう。
サンプラーというのはサンプリングされたテクスチャということを意味する言葉
要するにテクスチャのデータなのだと考えておけば実用的には問題ありません。
そして、テクスチャデータからフラグメントの情報を抜き出すために使われるのが texture2D 関数です。
この関数は引数を二つ取り、
第一引数にサンプラー型のテクスチャデータを、
第二引数にテクスチャ座標を表す vec2 型のデータを渡します。
このように texture2D 関数を使ってテクスチャの色情報を取り出したあと、
点の色を表す varying 変数 vColor と掛け合わせた色が最終的に出力される色となります。
なので白色にすれば画像がそのまま見える
まとめ