立体モデル(トーラス)の描画

実行結果

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

立体的なモデル

今回はちょっとした息抜きに、立体モデルのレンダリングを行なってみたいと思います。

ここで[ 息抜きに ]と書いたのは、今回のテキストでは新しい技術が何も出てこないからです。今まで当サイトのテキストで実践してきたことをそのまま利用して、立体的なトーラス(ドーナツ型)のモデルを描画してみます。

今までのサンプルでは、三角形か、せいぜい四角形だけをレンダリングに使ってきました。もちろん、これらの簡易なポリゴンモデルであっても三次元空間上に描かれることは間違いありませんが、いまひとつインパクトに欠けますね。今回のトーラス描画では、いかにも 3D らしい描画結果が得られます。

トーラスの正確な定義の話をしてしまうと非常にややこしくなるのですが、3D モデルでトーラスと言えば大抵の場合ドーナツの形をしたモデルのことを言います。トーラスのモデルデータを用意するには、サインやコサインを駆使しながら、三次元空間上の頂点の位置を計算してやります。

イメージとしては、まず原点から一定の距離( R )に円を描くように頂点を配置します。

一定の距離に円を描く

上の図は、三次元空間の原点を真横から見ている状態ですね。

次に、Y 軸を中心にしてコンパスの要領で回転させながら、次々と円を描く頂点を追加していきます。

Y軸による回転

上の図は、斜め上の方向から見たようなイメージですね。こうして円周上をコンパスが一周すれば、見事にドーナツ状の頂点配列が出来上がります。今回はこれをプログラム上でシミュレートします。

トーラスの頂点データを生成

今回はトーラスのモデルデータを生成する関数をまず作ります。

トーラスは、パイプ状のモデルが輪を作っている状態とも考えることができますね。原点からパイプの中心部分までの距離と、パイプ自体の太さ(半径)がはっきりしていれば、トーラスを生成することが可能です。また、パイプを形成する頂点を増やせば増やすほど、トーラスは滑らかな曲線を描きます。今回作成する関数では、これらのパラメータを受け取り、モデルの頂点属性配列などを返すように実装します。

トーラスのモデルデータを生成する関数

function torus(row, column, irad, orad){
	var pos = new Array(), col = new Array(), idx = new Array();
	for(var i = 0; i <= row; i++){
		var r = Math.PI * 2 / row * i;
		var rr = Math.cos(r);
		var ry = Math.sin(r);
		for(var ii = 0; ii <= column; ii++){
			var tr = Math.PI * 2 / column * ii;
			var tx = (rr * irad + orad) * Math.cos(tr);
			var ty = ry * irad;
			var tz = (rr * irad + orad) * Math.sin(tr);
			pos.push(tx, ty, tz);
			var tc = hsva(360 / column * ii, 1, 1, 1);
			col.push(tc[0], tc[1], tc[2], tc[3]);
		}
	}
	for(i = 0; i < row; i++){
		for(ii = 0; ii < column; ii++){
			r = (column + 1) * i + ii;
			idx.push(r, r + column + 1, r + 1);
			idx.push(r + column + 1, r + column + 2, r + 1);
		}
	}
	return [pos, col, idx];
}

この torus と名づけられた関数では、引数を四つ取ります。

第一引数はパイプを形成する円をいくつの頂点で表現するのかを指定します。大きな数値を指定すればパイプの断面が円形に近づきますが、逆に小さな数値を指定すればパイプの断面はカクカクになっていきます。

第二引数はパイプをどれくらい分割するのかを指定します。この数値を大きくすると、トーラスは滑らかな輪を形成するようになり、小さな数値を指定すればカクカクの輪になります。

第三引数は生成されるパイプそのものの半径です。そして第四引数が原点からパイプの中心までの距離になります。

@izmhratsさんに教えてもらってインデックスの振り方を修正しました

HSV カラーから RGB カラーへの変換

トーラスのモデルデータを用意する先ほどの関数の中に、怪しげな関数がもう一つ潜んでいたことに気が付いたでしょうか。変数 tc に値を返している hsva という関数がそれです。

今回のサンプルでは、トーラスに HSV カラーを適用します。HSV は RGB とは異なる方法で色を表現します。色の[ 色相 = Hue ]・[ 彩度 = Saturation ]・[ 明度 = Value ]を用いて表現することがその名の由来です。

RGB で綺麗に色を補完しながら指定していくのは非常に面倒ですが、HSV ではそれが非常に簡単に行なえます。今回のサンプルでは、HSV から RGB への色の変換を関数で実装し、それによりトーラスに美しいレインボーカラーが付くようにしたいと思います。

HSV から RGB への変換を行なう関数

function hsva(h, s, v, a){
	if(s > 1 || v > 1 || a > 1){return;}
	var th = h % 360;
	var i = Math.floor(th / 60);
	var f = th / 60 - i;
	var m = v * (1 - s);
	var n = v * (1 - s * f);
	var k = v * (1 - s * (1 - f));
	var color = new Array();
	if(!s > 0 && !s < 0){
		color.push(v, v, v, a); 
	} else {
		var r = new Array(v, n, m, m, k, v);
		var g = new Array(k, v, v, n, m, m);
		var b = new Array(m, m, k, v, v, n);
		color.push(r[i], g[i], b[i], a);
	}
	return color;
}

この関数には、純粋に HSV で色を表現するためのパラメータを渡します。第四引数はアルファ成分で、これは関数内で変更されることはなく単純に RGBA として結果を返すために付加してあります。

HSV では、色相は 0 ~ 360 の範囲に収まっている必要がありますが、それ以上に大きな数値を指定しても計算が破綻しないように関数内で処理しています。また、彩度や明度に不正な値が指定されている場合には正しい値を返しませんので注意しましょう。彩度・明度・透明度はいずれも 0 ~ 1 の範囲で指定してください。

トーラスのモデルデータ生成関数にしても、HSV から RGB への変換関数にしても、中身で何をやっているのかを詳細に説明することはしません。もし詳しく知りたいという方は各自で調べていただくことをオススメします。

まとめ

今回は、WebGL に関する新しい技術的解説は一切ありませんでした。ただ、実際にサンプルを動作させてみるとわかると思いますが、やっぱり単なる板状のポリゴンを描画するだけよりも、トーラスのような立体的なモデルを描画することのほうが楽しいです。

サンプルでは、今まで使っていたシェーダをそのまま使っていますので、HTML に変更はありません。また、javascript のほうに関してもトーラス生成用の関数と、HSV 変換関数が追加されたくらいで、特別大きな変更点はありません。

サンプルで描画されるトーラスは、Y 軸と Z 軸を中心とした回転を行いながらレンダリングされます。非常にカラフルなので見ているだけでなんとなく楽しい気分になる……かもしれません。実際に動作するサンプルには以下のリンクから。

次回は簡単なライティングをやろうかと思っています。

entry

PR

press Z key