OpenGLプログラミング/Depth of Field
はじめに
[編集]実生活では、レンズを使うカメラは、原理的に、見ているすべてのもの一度に焦点を合わせることはできません。 カメラ(焦点距離)から一定の距離の近くのオブジェクトだけがシャープに表示されます。 この領域は、 depth of fieldと呼ばれています。 よりカメラの近くか遠くにあるオブジェクトは、シャープでなく表示されます。 焦点距離にないオブジェクトがどのくらいアンシャープになるかは、カメラの絞りの形状や大きさに依存します。 通常、GPUはすべてを限りなくシャープにレンダリングしますが、しかし被写界深度の効果をシミュレートするための様々なテクニックがあります。 最も正確なのは、アキュムレーションバッファーを使用することで、また実装も非常に容易です。 基本的には、わずかに異なるMVP行列でシーンを複数回レンダリングすることで、レンズ絞りの異なる部分を通過した光線をシミュレートします。 レンズ diaphragmによって形成される開口部の形状は、オブジェクトが焦点から外れたときのぼやけ方に影響を与えます。 これは bokehと呼ばれています。
バッファを使用してDepth of fieldをシミュレートする
[編集]次のコードを使用して model-view-projection 行列を設定するところから始め、その後フレームをレンダリングしていこうと思います:
glm::mat4 modelview = glm::lookAt(eye, object, up);
glm::mat4 projection = glm::perspective(...);
glm::mat4 mvp = projection * modelview;
glUniformMatrix4fv(uniform_mvp, 1, GL_FALSE, glm::value_ptr(mvp));
draw_scene();
glSwapBuffers();
ここで eye
は目やカメラの位置を含むベクトルで、 object
は私たちが中心とフォーカスにしたいオブジェクトの位置で、 up
はどの方向が上かを記述するベクトルです。
円形の開口部をシミュレートするために、見ている方向に垂直な平面の円のなかでカメラを移動させます。
平面を記述する2つのベクトルは、クロス積を使用することで簡単に得ることができます。
カメラをあちこちに動かすだけで、見ている方向を変更しない場合には、シーンのほとんどがぼやけることになります。
しかし、それに対するトリックがあり、私たちが中心にしてフォーカスさせたいオブジェクトを直接見ておくというものです。
これは簡単にでき、そのオブジェクトの座標を glm::lookAt()
の2番目のパラメータとして渡す必要があります。
見ている物体がいつでもきっかり画面の中央になり、あちこちにに動かなくなるので、それがぼやけることがなくなります。
残りは私たちのセンターのオブジェクトとの相対的な深さによってぼかされます。
int n = 10; // number of light rays
float aperture = 0.05;
glm::mat4 projection = glm::perspective(...);
glm::vec3 right = glm::normalize(glm::cross(object - eye, up));
glm::vec3 p_up = glm::normalize(glm::cross(object - eye, right));
for(int i = 0; i < n; i++) {
glm::vec3 bokeh = glm::vec3(right * cosf(i * 2 * M_PI / n), p_up * sinf(i * 2 * M_PI / n), 0);
glm::mat4 modelview = glm::lookAt(eye + aperture * bokeh, object, p_up);
glm::mat4 mvp = projection * modelview;
glUniformMatrix4fv(uniform_mvp, 1, GL_FALSE, glm::value_ptr(mvp));
draw_scene();
glAccum(i ? GL_ACCUM : GL_LOAD, 1.0 / n);
}
glAccum(GL_RETURN, 1);
glSwapBuffers();
エクササイズ
[編集]- 前のいずれかの
glm::lookAt()
を使用するチュートリアルに、この手法を適用してみましょう。 n
とaperture
の値を変更してみましょう。- ほとんどのカメラ diaphragmsは本当は円形ではなく、多角形です。 正方形または六角形のdiaphraghmをシミュレートしてみてください。
- この手法をアンチエイリアシングおよび/またはモーションブラーと効率的に組み合わせることはできるでしょうか?