平面の鏡面反射(Cubemap)

平面の鏡面反射(Cubemap)

 以前にReflectionProbeやReflectionMatrixを用いた鏡面反射について記事を掲載しました(平面の鏡面反射)。その際はRenderTextureを用いていましたが、その代わりにCubemapが得られないかとScriptを変更したところ問題が発生しました。CubemapはCamera.RenderToCubemapにより得られますが、これを実行すると、変更したworldToCameraMatrixが元に戻ってしまい反射画像が得られませんでした。そこで、ReflectionMatrixによってworldToCameraMatrixを変更するのではなく、鏡面位置におけるカメラの座標と回転を求め、鏡面反射用のCubemapが得られないか試してみました。

鏡面反射用カメラの座標と回転

鏡面位置の座標

 座標\(P\)を中心とする平面の法線ベクトル\(\vec{n}\)及びメインカメラの座標\(C\)より、点\(C\)から平面へ垂直に下した線と平面の交点\(A\)が求まります。 そして、 ベクトル \(\vec{CA}\)を求め、交点\(A\)を\(\vec{CA}\)で移動するとカメラの鏡面位置\(R\)が求まります。

図1. 鏡面におけるカメラ座標Cと鏡面位置Rの関係

鏡面位置の座標Rは以下の式より求めることができます。

$$ R=A+A-C=2A-C \tag{1} $$

まず、交点\(A\)を求めます。点\(C\)を通り、\(\vec{n}\)に平行な直線はベクトル方程式より

$$ \begin{align} \left\{ \begin{array}{l} x&=c_{x}+n_{x}t\\ y&=c_{y}+n_{y}t\\ z&=c_{z}+n_{z}t \end{array} \right. \tag{2} \end{align} $$

となります。また、平面の方程式は

$$ \begin{align} n_{x}x+n_{y}y+n_{z}z+d=0\\ d=-n_{x}p_{x}-n_{y}p_{y}-n_{z}p_{z} \end{align} \tag{3} $$

です。式\((2)\)を式\((3)\)へ代入すると

$$ t=-\frac{n_{x}c_{x}+n_{y}c_{y}+n_{z}c_{z}+d}{n_{x}n_{x}+n_{y}n_{y}+n_{z}n_{z}} \tag{4} $$

と求まります。この式を内積を用いて表すと

$$ t=-\frac{\vec{n}\cdot \vec{c}-\vec{n}\cdot \vec{p}}{\vec{n}\cdot \vec{n}} \tag{5} $$

となります。ここで、\(\vec{n}\)を単位ベクトルとすると

$$ t=\vec{n}\cdot \vec{p}-\vec{n}\cdot \vec{c} \tag{6} $$

となります。この\(t\)を式\((1)\)に代入することで、交点\(A\)が求まります。求められた点\(A\)を式\((1)\)へ代入するとことで、鏡面反転したカメラの位置\(R\)が求まります。

回転

 オブジェクトの正面を示すベクトルと上方を示すベクトルから、Quaternion.LookRotationによってオブジェクトの回転を表すクォータニオンを計算することができます。つまり、鏡面で反転させた二つのベクトルを求めることで鏡面位置における回転を計算できます。鏡面反転座標ベクトルを求める式は座標を求める式とほぼ同じですが、座標ではなくベクトルなので平面及びベクトルを原点\(O\)に移動し計算します。よって、\(P=(0, 0, 0)\)となります。

図2. 鏡面における任意のベクトル\(\vec{u}\)と鏡面反転したベクトル\(\vec{s}\)の関係

\(\vec{b}\)及び \(\vec{s}\)は

$$ \vec{b}=\vec{h}-\vec{u},\quad \vec{s}=\vec{h}+\vec{b}\\ \tag{7} $$

なので、\(\vec{s}\)は

$$ \vec{s}=2\vec{h}-\vec{u} \tag{8} $$

となります。ベクトル方程式は式\((2)\)と同様に

$$ \begin{align} \left\{ \begin{array}{l} x&=u_{x}+n_{x}t\\ y&=u_{y}+n_{y}t\\ z&=u_{z}+n_{z}t \end{array} \right. \tag{9} \end{align} $$

また、式\((6)\)より

$$ t=-\vec{n}\cdot \vec{u} \tag{10} $$

となります。この\(t\)を式\((9)\)に代入することで、\(\vec{h}\)が求まります。よって、式\((8)\)より鏡面で反転したベクトルを求めることができます。

Script

 作成したScriptは以下の通りです。先ほど求めた式より反射用カメラの位置と回転を求めています。

Shader

 Cubemapを表示するだけのシェーダーです。

実行結果

 上記Scriptの実行結果は以下の通りです。Cubemapによる平面の鏡面反射が描画できました。しかし、CalculateObliqueMatrixによってクリップ平面を変更すると正しい結果が得られませんでした。

RenderTextureに変更

 以上で説明した方法を用いて、以前の記事 (平面の鏡面反射)で行っていたように鏡面の画像をRenderTextureで取得するように変更してみました。

Script

 作成したScriptは以下の通りです。CalculateObliqueMatrixによって得られるprojection matrixでは画像が反転してしまうので、Matrix4x4.Scaleによって反転しています。

実行結果

 上記Scriptの実行結果は以下の通りです。問題なく平面の鏡面反射を描画することができました。

 このコンテンツはユニティちゃんライセンス条項の元に提供されています