跳转到内容

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 的值。
  • 大多数相机光圈并不是真正圆形的,而是多边形的。尝试模拟一个方形或六边形的光圈。
  • 你能够有效地将此技术与抗锯齿和/或运动模糊结合起来吗?
华夏公益教科书