マルチテクスチャ

実行結果

今回のサンプルの実行結果

複数のテクスチャを使う

前回は WebGL におけるテクスチャの使い方を解説しました。単純な板状の四角形ポリゴンにテクスチャを貼り付けるだけの簡易的なプログラムでしたが、やはりイメージデータを使うことができるとプログラムの柔軟性が向上しますね。

さて、今回はマルチテクスチャと題して複数のテクスチャを使ってイメージを合成する方法を解説します。この方法を習得すれば、一枚のポリゴンに対して複数のテクスチャを適用することが可能になります。さらなる表現力アップを目指してがんばってください。

それでは早速ですが、複数のテクスチャを利用してレンダリングを行なうためには、いったい何をすればいいのか想像がつくでしょうか。

前回のテキストでも軽く触れましたが、WebGL にはテクスチャを管理する方法としてテクスチャユニットという概念がありました。このテクスチャユニットの仕組みをうまく活用することで、マルチテクスチャレンダリングが実現できます。

今回のサンプルでは、以下の二つの画像データを使います。

一枚目のテクスチャ用画像(texture0.png)

一枚目のテクスチャ用画像

二枚目のテクスチャ用画像(texture1.png)

二枚目のテクスチャ用画像

これらの二つの画像を使って、マルチテクスチャレンダリングを行なってみましょう。

シェーダ側に変更を加える

今回はまず、シェーダから修正してみましょう。

マルチテクスチャレンダリングを実現するために、シェーダ側では複数のテクスチャユニットを受け取れるように uniform 変数を追加しなければなりません。ただし、今回はポリゴンのテクスチャ座標は一枚目のテクスチャでも二枚目のテクスチャでも同じものを使いますので、変更を加えるべきはフラグメントシェーダのみということになります。

フラグメントシェーダのソースを修正

precision mediump float;

uniform sampler2D texture0;
uniform sampler2D texture1;
varying vec4      vColor;
varying vec2      vTextureCoord;

void main(void){
	vec4 smpColor0 = texture2D(texture0, vTextureCoord);
	vec4 smpColor1 = texture2D(texture1, vTextureCoord);
	gl_FragColor   = vColor * smpColor0 * smpColor1;
}

ご覧のとおり、テクスチャの情報を受け取る sampler2D 型の uniform 変数が二つ用意されています。やっていることは前回と似ている……というか要は同じです。GLSL のビルトイン関数である texture2D 関数を使ってテクスチャからフラグメントの情報を抜き出し、頂点色と二つのテクスチャカラーを使って最終的に出力される色を算出しています。

先にも書いたとおり、今回はフラグメントシェーダだけを修正します。頂点シェーダに変更はありません。

javascript の修正

続いて javascript のソースを修正します。こちらでは、新たに追加された uniform 変数へ適切にテクスチャデータを送ることが必要ですね。

まずは定石どおり、uniformLocation の取得から。

uniformLocation を取得している部分を抜粋

// uniformLocationを配列に取得
var uniLocation = new Array();
uniLocation[0]  = gl.getUniformLocation(prg, 'mvpMatrix');
uniLocation[1]  = gl.getUniformLocation(prg, 'texture0');
uniLocation[2]  = gl.getUniformLocation(prg, 'texture1');

これは簡単ですね。純粋にシェーダから uniformLocation を取得しているだけです。

次に、テクスチャオブジェクトを準備します。これには、自作関数の create_texture を使っています。

テクスチャオブジェクトを生成する部分を抜粋

// テクスチャ用変数の宣言と生成
var texture0 = null, texture1 = null;
create_texture('texture0.png', 0);
create_texture('texture1.png', 1);

今回は複数のテクスチャを使うことになるため、ここで呼び出されている自作関数 create_texture を修正して、テクスチャのユニット番号を受け取るようにしています。

create_texture 関数

function create_texture(source, number){
	// イメージオブジェクトの生成
	var img = new Image();
	
	// データのオンロードをトリガーにする
	img.onload = function(){
		// テクスチャオブジェクトの生成
		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);
		
		// 生成したテクスチャを変数に代入
		switch(number){
			case 0:
				texture0 = tex;
				break;
			case 1:
				texture1 = tex;
				break;
			default:
				break;
		}
	};
	
	// イメージオブジェクトのソースを指定
	img.src = source;
}

第二引数に受け取ったテクスチャのユニット番号に応じて、最終的に生成されたテクスチャオブジェクトの代入先を変えています。それ以外は、前回のサンプルと何も変わっていません。

さて、あとは恒常ループ内で行なうシェーダへのテクスチャの登録処理だけです。

恒常ループ内の処理を抜粋

// テクスチャユニットを指定してバインドし登録する
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture0);
gl.uniform1i(uniLocation[1], 0);

// テクスチャユニットを指定してバインドし登録する
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, texture1);
gl.uniform1i(uniLocation[2], 1);

ここでポイントとなるのは、テクスチャユニットの有効化と、テクスチャオブジェクトのバインド、そしてシェーダへのユニット番号の登録が全てワンセットになっていることです。

また、 activeTexture メソッド内で指定しているテクスチャユニット番号と、 uniform1i メソッドの第二引数に指定する整数値が、必ず一致するということに注意しましょう。

あとは通常どおり、座標変換行列などを適用しながら描画すれば、ポリゴンに二つのテクスチャが合成された状態でマッピングされます。意外と簡単だなぁという印象ではないでしょうか。

まとめ

マルチテクスチャレンダリングで注意するべきことは、正しくテクスチャユニットを指定しながらデータを処理すること、これに尽きます。

ちょっと紛らわしいかもしれませんが、要は activeTexture メソッドと uniform1i メソッドの使い方さえ間違えなければ、あとはシェーダ側で好きなようにテクスチャデータを利用できます。

今回は単純にシェーダ内で二つのテクスチャデータを乗算しているだけですが、個々のテクスチャデータに異なる処理を施すだけで、まったく別のレンダリング結果を生み出すことも可能です。固定機能パイプラインではなく、GLSL によるプログラマブルシェーダを使わざるを得ない実装になっているからこその醍醐味でもありますね。

サンプルへのリンクはいつものように下記にあります。

次回はテクスチャパラメータについて詳しく説明するつもりです。

entry

PR

press Z key