跳轉到內容

OpenGL 程式設計/景深

來自華夏公益教科書,開放的書籍,開放的世界
正常。
景深,聚焦在玻璃上。
景深,聚焦在水池上。
景深,聚焦在無窮遠處。

在現實生活中,使用透鏡的相機原則上無法一次將所有看到的東西都聚焦。只有距離相機一定距離(焦點距離)附近的物體才會顯得清晰。這個區域被稱為景深。距離相機更近或更遠的物體看起來就不清晰了。不在焦點距離上的物體模糊程度取決於相機光圈的形狀和大小。通常情況下,GPU 會渲染所有東西無限清晰,但有一些技術可以模擬景深效果。最準確的方法是使用累積緩衝區,而且它也很容易實現。基本上,我們用略微不同的 MVP 矩陣多次渲染場景,以模擬光線穿過透鏡光圈的不同部分。由透鏡光圈形成的光圈形狀影響了失焦物體的模糊方式。這被稱為散景

使用累積緩衝區模擬景深

[編輯 | 編輯原始碼]

假設我們從以下程式碼開始,它設定模型-檢視-投影矩陣,然後渲染一幀

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 是一個描述哪個方向是上方的向量。為了模擬圓形光圈,我們在垂直於我們正在觀察的方向的平面上繞著相機以圓周運動。我們可以很容易地使用叉積得到兩個描述該平面的向量。

如果我們只是移動相機而不改變它所看的方向,我們只會模糊大部分場景。然而,訣竅是我們一直看著我們要在中心和焦點中的物體。這樣做很簡單,我們只需要將該物體的座標作為 glm::lookAt() 的第二個引數。我們正在觀察的物體將始終在螢幕的正中央,不會移動,因此不會被模糊。其餘部分將根據相對於中心物體的深度而被模糊。

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 = right * cosf(i * 2 * M_PI / n) + p_up * sinf(i * 2 * M_PI / n);
  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() 的先前教程。
  • 更改 naperture 的值。
  • 大多數相機光圈並不是真正圓形的,而是多邊形的。嘗試模擬一個方形或六邊形的光圈。
  • 你能夠有效地將此技術與抗鋸齒和/或運動模糊結合起來嗎?
華夏公益教科書