屈折

refraction.png

用語

index of refraftion 屈折率のこと。

OpenGLで屈折を表現するには?

まず、OpenGLの組み込み関数にはそういう機能は存在しない。
なので屈折はシェーダで実装する必要がある。

GLSLのrefract関数

refract(入射ベクトル,法線ベクトル,屈折率)
関数の中身ではスネルの法則が利用されている。

屈折率の計算

波長によっても屈折率は違う。
長波長は短波長より屈折率が小さい
だから虹が見える。
屈折は散乱と密接な関係がある。

スネルの法則

二つの媒質中の進行波の伝播速度と入射角・屈折角の関係を表した法則のこと

  • $\theta_{I}$…入射角
  • $\theta_{T}$…屈折角
  • $n_{1}$…媒質1の屈折率
  • $n_{2}$…媒質2の屈折率
(1)
\begin{align} n_{1}\sin\theta_{I}=n_{2}\sin\theta_{T} \end{align}

後の式の便利のためこうしておく。

(2)
\begin{align} \sin\theta_{I}=\frac{n_2}{n_1}\sin\theta_{T} \end{align}

屈折ベクトルの求め方

refractionlighten.png

スネルの法則を利用して、視線ベクトル、法線ベクトル、媒質の屈折率から求めることができる。

法線ベクトルNとそれに垂直な単位ベクトルeは既に長さ1に正規化されてるとする。
視線ベクトルVを成分に分解して表現すると、

(3)
\begin{align} \vec{V}=-\vec{N}cosθ_I+\vec{e}sinθ_I \end{align}

と表現できる。
同様に、求めたい屈折ベクトルは次のように表現される。

(4)
\begin{align} \vec{T}=-\vec{N}cosθ_T+\vec{e}sinθ_T \end{align}
(5)
\begin{align} cos\theta_{I}=(-\vec{V}\cdot \vec{N}) \end{align}

$\vec{V},\vec{N}$はすでに長さ1に正規化してあるので、内積からこうなる。すると、$\vec{e}$が表現できる。

(6)
\begin{align} \vec{e}=\frac{\vec{V}-(\vec{V}\cdot \vec{N})\vec{N}}{\sin\theta_I} \end{align}

ここかたスネルの法則(式1)を利用して

(7)
\begin{align} \vec{T}=\frac{\vec{V}-(\sqrt{n^2-1+\cos^2\theta_{T}})N}{n} \end{align}
  • T..屈折ベクトル
  • n…屈折率の比$\frac{n_{2}}{n_{1}}$

根号の中が負になったら全反射が起こる。
n>1.0の時、根号の中が負になることはない。これは空気中から水やガラスに入射する場合。
逆に水やガラスから空気中に入射する場合はn<1.0となり、根号の内部が負になることがある。

屈折ベクトル= refract(視線ベクトル,法線ベクトル,屈折率);

GLSLのreafract関数の中身

refract(I,N,eta)

k=1.0-eta*eta*(1.0-dot(N,I)*dot(N,I))
if(k<0.0)
    result=0.0;
else
    result=eta*I-(eta*dot(N,I)*sqrt(k))*N

I,Nはこの関数を呼ぶ前に正規化(normalize(vec3))されてる必要がある。(望む結果を得たいなら)
これを式にすると(8)
\begin{align} \vec{T}=n\vec{V}-\biggl( n\vec{N}\cdot\vec{V}\sqrt{1-n^2\bigl(1-(\vec{N}\cdot\vec{V})^2\bigl)}\biggl)\vec{N} \end{align}

反射率

反射率とは、反射した光と、屈折する光の割合である。
正確にはフレネルの式で表現するが、CGではChristophe Schlickによる近似が用いられる。

(9)
\begin{align} F=f+(1-f)(1-V \cdot N)^5 \end{align}
  • V…視線ベクトル
  • N…法線ベクトル
  • f…θが0の時の物質の反射率(reflectance)
(10)
\begin{align} f=\frac{(1.0-\frac{n_1}{n_2})^2}{(1.0+\frac{n_1}{n_2})^2} \end{align}
  • $\frac{n_1}{n_2}$…物質1と物質2の屈折率。空気=1.0,水=1.3

エラー

refraction関数がうまく働いてくれないように見える

strange_refract.png
ちゃんと順番通りにターゲットを指定してないせい。
悪いコード 良いコード
GLenum target[]={
        GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
        GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
        GL_TEXTURE_CUBE_MAP_NEGATIVE_Z,
        GL_TEXTURE_CUBE_MAP_POSITIVE_X,
        GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
        GL_TEXTURE_CUBE_MAP_POSITIVE_Z
    };
GLenum target[]={
        GL_TEXTURE_CUBE_MAP_POSITIVE_X,
        GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
        GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
        GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
        GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
        GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
    };

正しい順番はglew.hに書いてある。


snell

サポートサイト Wikidot.com snell