カメラから見た座標のことを、ここではカメラ座標と呼ぶことにする(※あまり一般的な呼び方ではない)。パラメータを決定したら、あとは平行移動と回転をするだけだ。カメラ座標の原点(カメラの位置)をワールド座標の原点へ持ってくるように平行移動する。-Cz)だけ平行移動すればいい。次に回転行列について考える。はじめに、カメラ座標についての情報が必要なので、カメラ座標の基底ベクトルを求める。Lが正面に来るようにするのだから、CとLを通る直線がZ軸になる。右手座標系ではZ軸のマイナス方向が奥だということを考慮すると、LからCへのベクトルを求め正規化すればZ軸が求まる。よってカメラのZ軸をZ’とすると、Z’=(C-L)/|C-L|となる。X軸はZとUに垂直なので、外積を求めてから正規化する。よってX’=(U×Z)/|U×Z|。同様にして、Y’=(Z×X)/|Z×X|=Z×X。ここでY≠Uであることに注意したい。カメラが少し斜め上を向いているとすると、実際のY軸は少し傾くので、Uと一致しない。Uはせいぜい「Z軸から見てどちら側が上か」を表す程度になる。ここで求めたカメラ座標の基底は、それぞれ直交しており、長さも正規化されているので、正規直交基底となる。あとはカメラ座標の軸をワールド座標の軸と一致するように回転するだけだ。回転行列を使って回転してもいいだろうが、ワールド座標もカメラ座標も、どちらも正規直交基底なので、基底を変換してやる方が早いだろう。z’)で表すと、2つの座標の間に以下の関係が成り立つ。これを変換行列の形で表記すると、となる。次に左側から逆行列をかけて、と変形できる。あとはこの逆行列なのだが、カメラ座標の基底は正規直交基底なので、X、Y、Zはそれぞれ直交していて、かつ長さが1だ。それぞれ内積を計算すると、X’・X’=1、X’・Y’=0、X’・Z’=0、...のようになる。転置してやるだけで逆行列になる。わからないなら、実際に手計算で転置した行列を掛けてみるといいだろう。これで平行移動と回転の行列が求まったので、2つを掛けたものが、求めるビュー変換行列となる。Dartでは以下のようになる。1.0]);プロジェクション変換行列プロジェクション変換行列は、どこまでのものが、どのように見えるかを決定する。わかりやすく言えば、視野を決めるということになる。変換後の座標は、クリップ座標と呼ばれている。クリップ座標はプロジェクション変換によって得られる有限の空間で、座標の外にある物は描画されない。クリップ座標からは左手座標系になるので、右手座標系を使っている場合は、ここでZ座標の符号を反転しておく。いわゆる「パースをつける」こともプロジェクション変換行列の仕事になる。遠くの物を小さく表示したいのなら、ここで縮小しておこう。プロジェクション変換行列で用いられるのは主に2種類で、パースのつかない平行投影と、パースのついた透視投影だ。Projection)平行投影は、近くの物も遠くの物も同じ大きさで表示する。視野の広がりも持たず、四角い窓を通して見たような表示結果になる。数学の授業で使う立体図形などは、平行投影だ。nearとfarには0や負の値を指定することも可能だ。Volume)という。平行投影のビューボリューム図を見ればわかると思うが、平行投影の場合は、ビューボリュームは単なる直方体で、パースも考慮しなくていい。平行投影の場合、クリップ座標と正規化デバイス座標は一致する。三次元である正規化デバイス座標と、四次元で表されるクリップ座標とは、厳密には違うのだが、平行投影ではwの値が常に1になり、何の影響も与えないので無視してよい。よって、クリップ座標のことは考えずに、正規化デバイス座標へ変換すると考えたほうが単純でいいだろう。正規化デバイス座標へ変換するには、この直方体をそのまま各軸について-1.0から1.0までの値を取るような長さ2の立方体に変換するだけでいい。具体的な変換手順は以下の通りだ。ビューボリュームの中心を原点へ移動する各辺をそれぞれの長さで割って、長さ1の立方体にする各辺を2倍に拡大して長さ2の立方体にするZ座標を反転して左手座標系に変換するこれを行列にまとめると、となる。この行列を列優先で表記すると以下のようになる。Projection)透視投影は、遠くの物ほど小さく見える、パースのついた表示になる。こちらを使うことの方が多いだろう。必要なパラメータは平行投影と同じだ。nearとfarには正の値を指定する必要がある。Frustum)とも呼ばれる。透視投影によるクリップ座標への変換方法については、別記事で解説予定なので、ここでは割愛する。変換行列は以下のようになる。列優先で表記すると以下のようになる。この作業はバーテックスシェーダの仕事なので、ユーザがやる必要はない。3D描画のための設定(一部)WebGLには細かい描画設定があるので、その中からよく使うものを紹介したい。Clockwise)に頂点を配置するとポリゴンが表に......と書いたが、反時計回りは単にデフォルト値であるだけで、実際には変更できる。この順序のことをワインディングオーダーと呼ぶ。ここでは変更はしないが、必要ならば設定しておこう。Culling)負担を軽減するために描画に不要なポリゴンを省くことをカリングという。背面カリングはもっとも単純なカリング方法で、裏を向いているポリゴンを描画しないようにする。逆に言えば、背面カリングを有効にしないかぎり、ポリゴンの裏側も描画される。3Dグラフィックスを扱うならば有効にしておこう。背面カリングは、enableメソッドを使用することで有効化できる。context.enable(CULL_FACE);無効化するには、disableメソッドを使用する。Test)デプステスト、あるいは深度テストというのは、その名の通り描画対象の深度を調べる工程だ。デフォルトでは奥行きを無視して後に描画したオブジェクトほど上に表示されるが、通常は手前のオブジェクトが奥のオブジェクトを隠すように描画したいだろう。デプステストを使えばこれを実現することができる。WebGLではデプスバッファと呼ばれるバッファが用意されており、ピクセルごとに現在の深度値がここに書き込まれる。デプステストを有効化していると、書き込もうとしているフラグメントの深度値とデプスバッファの深度値を比較し、デプステストに合格しなかったフラグメントは描画されない。context.enable(DEPTH_TEST);また、必要であれば比較関数を変更することもできる。NOTEQUALの8つ。デフォルトの比較関数はLESS(<)になっている。デプステストは正規化デバイス座標で行われるので、奥の方が深度値は大きい。型抜きやマスクと呼ばれる機能が実現できる。ステンシルバッファというマスクデータ用のバッファを用意しておき、ステンシルバッファと比較することで、ピクセルを描画する・しないを決定する。画面の部分的な合成や、3Dモデルの縁取りなどに使われることが多い。ステンシルテストについては、ここでは解説しない。Test)シザーテストは描画可能領域を設定する。デフォルトではビューポートと同一の範囲になっている。画面の一部のみをクリアしたい場合などに使用するといいだろう。矩形領域を指定3D描画のプログラムを組む大きな流れは2Dのときと変わらないので、前回の記事で説明した部分については省略する。前回と大差無い部分だけを先に書き出せば、だいたい以下のような形になる。シェーダを読み込んでから開始する。表示用キャンバスの用意。WebGL描画コンテキスト。デプステストなどを有効にする。uniform変数を設定する。