視錐台を構成する6つの平面をもとめるには?

frustum-extract-plane.png

まず視錐台を構成する8つの頂点を計算します。
そしたら、その8つの頂点から平面が定まります。(平面は最低3つの頂点さえあれば定義できるので)
https://github.com/jasmingirl/miffy/blob/master/math/cull/frustum.h#L194-L229

別のやり方

テストしたい頂点をvとする。
4x4の投影行列Pがあるとする。

(1)
\begin{equation} Pv=v' \end{equation}

細かく書くとこう

(2)
\begin{align} \left[ \begin{array}{cc} x・p_{11} + y・p_{12} + z・p_{13}+w・p_{14} \\ x・p_{21} +y・p_{22} * z・p_{23} +w・p_{24} \\ x・p_{31} + y・p_{32} * z・p_{33}+w・p_{34} \\ x・p_{41} + y・p_{42} * z・p_{43}+w・p_{44}\\ \end{array} \right] = \left[ \begin{array}{cc} v・row_1 \\ v・row_2 \\ v・row_3 \\ v・row_4\\ \end{array} \right] \end{align}

・は内積を意味する。
$row_{i}=(m_{i1} m_{i2} m_{i3} m_{i4})$
はi番目の行って意味である。
この変換の後、v'はhomogeneous clipping spaceになる
homogeneous って何? ホモの天才という意味ではない。同次って意味だ。
同次っていうのは、x,y,zだけじゃなくてwもある座標系のことだ。
つまり、w付きのクリッピング座標になるのである。
このクリッピング座標においては、視錐台は軸に対して平行な箱になる。サイズはAPIによりけり。
もしv'がこの箱の中にあるなら、変換前の頂点vは「変換前の視錐台」の中にあるってことになる。
もし次の不等式がすべてのv'について成り立つなら、

(3)
\begin{align} -w'<x'<w'\\ -w'<y'<w'\\ -w'<z'<w' \end{align}
条件 結果
-w'<x' x'は視錐台の左平面の内側
x'<w' x'は視錐台の右平面の内側
-w'<y' y'は視錐台の下平面の内側
y'<w' y'は視錐台の上平面の内側
-w'<z' z'は視錐台の近平面の内側
z'<w' z'は視錐台の遠平面の内側

ためしに一個取り出して

(4)
\begin{equation} -w'<x' \end{equation}

この式を

(5)
\begin{align} \left[ \begin{array}{cc} v・row_1 \\ v・row_2 \\ v・row_3 \\ v・row_4\\ \end{array} \right]= \left[ \begin{array}{cc} x' \\ y' \\ z' \\ w'\\ \end{array} \right] \end{align}

を思い出して書き換えるとこうなる

(6)
\begin{equation} -(v・row_4)<(v・row_1) \end{equation}

これは次と同じ

(7)
\begin{align} 0<(v・row_4)+(v・row_1)\\ 0<v・(row_4+row_1) \end{align}

これを更にv=(x,y,z,w)であることを思い出して書き換えると、

(8)
\begin{equation} x(m_{41}+m_{11})+y(m_{42}+m_{12})+z(m_{43}+m_{13})+w(m_{44}+m_{14})=0 \end{equation}

w=1なので、もっとシンプルにできます

(9)
\begin{equation} x(m_{41}+m_{11})+y(m_{42}+m_{12})+z(m_{43}+m_{13})+(m_{44}+m_{14})=0 \end{equation}

これは、普通の平面の方程式ax+by+cz+d=0と同じ形です

(10)
\begin{align} a=(m_{41}+m_{11})\\ b=(m_{42}+m_{12})\\ c=(m_{43}+m_{13})\\ d=(m_{44}+m_{14}) \end{align}

なので、これで視錐台の左平面は投影行列からもとめられるってことがわかります。
注意しなきゃいけないのが、この時点では、この平面の方程式は正規化されていないってことです。
まとめると

平面 係数a 係数b 係数c 係数d
m41+m11 m42+m12 m43+m13 m44+m14
m41-m11 m42-m12 m43-m13 m44-m14
m41+m21 m42+m22 m43+m23 m44+m24
m41-m21 m42-m22 m43-m23 m44-m24
m41+m31 m42+m32 m43+m33 m44+m34
m41-m31 m42-m32 m43-m33 m44-m34

これを配列風にかくとこうなる

平面 係数a 係数b 係数c 係数d
m30+m00 m31+m01 m32+m02 m33+m03
m30-m00 m31-m01 m32-m02 m33-m03
m30+m10 m31+m11 m32+m12 m33+m13
m30-m10 m31-m11 m32-m12 m33-m13
m30+m20 m31+m21 m32+m22 m33+m23
m30-m20 m31-m21 m32-m22 m33-m23

視錐台の8つの頂点を求める。

視錐台の平面は 視錐台に向かって内側を向いている法線で定義できる。
オブジェクトが視錐台の内側にいるかどうかテストするのだ。

モデル行列が単位行列じゃない場合はどうする?

モデル行列が単位行列の場合は、上記のコードでイケイケなんだけど、
モデル行列に変なのがはいっているとさあ大変なのである。
基本的に、テストする頂点と、視錐台の座標系は合わせとかないといけない。
上のやりかたなら、投影行列をかけざんしたモデルビュー投影行列におきかえれば解決です。

参考にした書籍

サポートサイト Wikidot.com