「OpenGLプログラミング/Depth of Field」の版間の差分

削除された内容 追加された内容
en:OpenGL Programming/Depth of Fieldの 15:13, 21 November 2012 版から翻訳
 
! 0鰕 (トーク | 投稿記録)
ページの白紙化
タグ: 白紙化 blanking
1 行
[[File:OpenGL Tutorial Glescraft-nodof.png|thumb|Normal.]]
[[File:OpenGL Tutorial Glescraft-dof-front.png|thumb|Depth-of-field, focus on glass.]]
[[File:OpenGL Tutorial Glescraft-dof-middle.png|thumb|Depth-of-field, focus on pool.]]
[[File:OpenGL Tutorial Glescraft-dof-back.png|thumb|Depth-of-field, focus at infinity.]]
 
= はじめに =
 
実生活では、レンズを使うカメラは、原理的に、見ているすべてのもの一度に焦点を合わせることはできません。
カメラ(焦点距離)から一定の距離の近くのオブジェクトだけがシャープに表示されます。
この領域は、 [[w:Depth of field|depth of field]]と呼ばれています。
よりカメラの近くか遠くにあるオブジェクトは、シャープでなく表示されます。
焦点距離にないオブジェクトがどのくらいアンシャープになるかは、カメラの絞りの形状や大きさに依存します。
通常、GPUはすべてを限りなくシャープにレンダリングしますが、しかし被写界深度の効果をシミュレートするための様々なテクニックがあります。
最も正確なのは、アキュムレーションバッファーを使用することで、また実装も非常に容易です。
基本的には、わずかに異なるMVP行列でシーンを複数回レンダリングすることで、レンズ絞りの異なる部分を通過した光線をシミュレートします。
レンズ [[w:Diaphragm (optics)|diaphragm]]によって形成される開口部の形状は、オブジェクトが焦点から外れたときのぼやけ方に影響を与えます。 これは [[w:Bokeh|bokeh]]と呼ばれています。
 
= Simulating depth of field using the accumulation buffer =
 
次のコードを使用して model-view-projection 行列を設定するところから始め、その後フレームをレンダリングしていこうと思います:
 
<source lang="cpp">
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();
</source>
 
ここで <code>eye</code>は目やカメラの位置を含むベクトルで、 <code>object</code>は私たちが中心とフォーカスにしたいオブジェクトの位置で、 <code>up</code>はどの方向が上かを記述するベクトルです。
円形の開口部をシミュレートするために、見ている方向に垂直な平面の円のなかでカメラを移動させます。
平面を記述する2つのベクトルは、クロス積を使用することで簡単に得ることができます。
 
カメラをあちこちに動かすだけで、見ている方向を変更しない場合には、シーンのほとんどがぼやけることになります。
しかし、それに対するトリックがあり、私たちが中心にしてフォーカスさせたいオブジェクトを直接見ておくというものです。
これは簡単にでき、そのオブジェクトの座標を <code>glm::lookAt()</code>の2番目のパラメータとして渡す必要があります。
見ている物体がいつでもきっかり画面の中央になり、あちこちにに動かなくなるので、それがぼやけることがなくなります。
残りは私たちのセンターのオブジェクトとの相対的な深さによってぼかされます。
 
<source lang="cpp">
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();
</source>
 
= エクササイズ =
 
* 前のいずれかの <code>glm::lookAt()</code>を使用するチュートリアルに、この手法を適用してみましょう。
* <code>n</code> と <code>aperture</code>の値を変更してみましょう。
* ほとんどのカメラ [[w:Diaphragm (optics)|diaphragms]]は本当は円形ではなく、多角形です。 正方形または六角形のdiaphraghmをシミュレートしてみてください。
* この手法をアンチエイリアシングおよび/またはモーションブラーと効率的に組み合わせることはできるでしょうか?
{{BookCat}}
 
[[en:OpenGL Programming/Depth of Field]]