頂点位置から面法線を算出
Graphical Web 2 !
当テキストは Graphical Web Advent Calendar 2013 - Adventar への参加記事です。
さて、前回のテキストでは three.js を使うことなく、WebGL で JSON データを読み込んで、お馴染みのあのモデルをレンダリングしてみました。相当いろいろな部分で説明を省略したにもかかわらず、なんとも冗長なテキストになりました。自分で言うのもなんですが、こういったことは好きでないとなかなかじっくりと理解することさえ難しいですね。基本を理解することは大事ですが、時間と心に余裕のあるときに、ゆっくりまったりと読むことをオススメします。
さて、寄稿記事の第二段となる今回は、頂点位置から面法線を算出する方法について書いてみようと思います。
頂点に含まれている諸情報
頂点には、さまざまな情報が含まれていることがほとんどです。
最低限、位置に関する情報は頂点に欠かせません。さらに、法線や色、テクスチャ座標などを保持していることが多いと思います。しかし、場合によっては頂点の位置しか情報がなく、法線を自前で計算しなくてはならないケースがあります。こういった場合、どのように法線を算出すればいいのでしょうか。
実は、法線とひとくちに言っても頂点法線と面法線はまったく違うものです。しかし、頂点の位置、そしてその頂点がどの面を構成する頂点なのかさえわかれば、面法線を算出することができます。そしてひとたび面法線が算出できれば、そこからさらに頂点法線を算出することも可能です。今回のテーマはまさにここですね。
面と法線
法線とは、基本的には面に対して使われる言葉で面に対して垂直なベクトルをさす言葉です。
まず大前提として、面は最低でも三つ以上の頂点がなくては成り立ちません。三つの頂点を結んで、三角形という面ができるわけですね。3D プログラミングでは、この面ひとつのことを一般にポリゴンなどと呼んだりしますね。
面を構成する三つの頂点
面法線は、面を構成する頂点同士を結んだベクトルから算出することができます。以下の図を引用するなら、頂点 A から B へと延びるベクトルと、頂点 A から C へと延びるベクトルの二つから計算します。下の図で言うところの青い線がそれらのベクトルにあたりますね。
頂点同士を結ぶベクトル
この二つのベクトルの外積を計算すると、とあるベクトルが現れます。これが面法線です。仮に、頂点の位置を元に面法線を算出するコードを書くとすれば、例えば以下のようになりますね。
function faceNormal(v0, v1, v2){
var n = new Array();
// 頂点を結ぶベクトルを算出
var vec1 = [v1[0] - v0[0], v1[1] - v0[1], v1[2] - v0[2]];
var vec2 = [v2[0] - v0[0], v2[1] - v0[1], v2[2] - v0[2]];
// ベクトル同士の外積
n[0] = vec1[1] * vec2[2] - vec1[2] * vec2[1];
n[1] = vec1[2] * vec2[0] - vec1[0] * vec2[2];
n[2] = vec1[0] * vec2[1] - vec1[1] * vec2[0];
return n;
}
この faceNormal
という関数は、引数に XYZ の要素を持つ配列、つまり頂点の情報を三つ受け取り外積の結果を返します。ただし関数内ではベクトルの正規化を行っていないので、コードを引用する場合は注意!
この関数で返されるのはあくまでも面法線です。面法線は、先程も解説したように面に垂直なベクトルであり、面の向きを表す指標になるものです。ただし、ここでちょっと考えてみてください。WebGL では、ライティングなどを行う際に何の法線情報を使っていたでしょうか。
そう、頂点シェーダの中身を思い出してみれば自然とわかると思いますが、ライティングなどを行う上で利用する法線は面法線ではなく頂点法線です。WebGL で簡単に利用できるようにするためには、面法線ではなく頂点法線を算出してやる必要があるのです。
面法線から頂点法線を計算
面の法線が定まれば、そこからさらに頂点の法線を算出してやることも可能です。
頂点の法線は、その頂点が属する面の法線を全て加算して正規化すれば定まります。例えば以下の図で言うと、頂点 A は二つの面を構成していますよね。このとき、頂点 A の法線を求めるためには、二つの面から延びている法線(緑色の線で描かれてるベクトル)を加算して正規化してやればいいわけです。
複数の面と頂点
頂点がいくつの面に属しているのかは、モデルの形状により様々で一概には言えません。スクリプトなどで頂点の位置から、面法線、そして頂点法線と連続して計算していく処理を書く場合には、まず対象となる頂点がいくつの面を構成する頂点なのかを先に調査しておく必要がありますので注意しましょう。
まとめ
さて、頂点の位置から面法線と頂点法線を計算する方法について、理解できたでしょうか。
数学を専攻した人にとっては他愛もない知識かもしれませんが、WebGL ではじめて 3D プログラミングに触れたという人もいるでしょうし、こういった基本的なことも意外と大切なのではと思い解説させていただきました。実は、このテキストを書いている私自身も、数学をちゃんと勉強してこなかったことを今更ながらに後悔している一人だったりします。
今回のテキストを書こうと思ったきっかけは、実は前回のサンプルで利用したウサギのモデルデータを用意しようとしたときでした。自分が見つけてきたウサギのモデルデータには、頂点の位置情報しか含まれていませんでした。しかし頂点法線抜きではライティングを行うことができません。そこで面法線の算出と、そこからさらに頂点法線を算出するプログラムを書き、JSON にエクスポートしたわけです。
3D プログラミングの初歩の初歩という内容のテキストでしたが、少しでも参考になる方がいらっしゃったなら幸いです。