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()
的先前教程。 - 更改
n
和aperture
的值。 - 大多数相机光圈并不是真正圆形的,而是多边形的。尝试模拟一个方形或六边形的光圈。
- 你能够有效地将此技术与抗锯齿和/或运动模糊结合起来吗?