Defocus Blur
Defocus Blur有一个更熟悉的说法,景深。
实现景深的思路相对来说很简单,我们只需要修改相机的模型即可,这主要涉及到相机的两个参数:
- 焦距距离(Focus Distance):指定一个距离,在这个距离上的物体会清晰对焦
- 光圈大小(Aperture Size):控制模糊的程度,光圈越大,则模糊越明显
13.1 Generating Sample Rays
在没有散焦模糊的情况下,所有光线来自相机中心。为了实现散焦模糊,我们构建一个以相机为中心的圆作为相机的光圈。同时,光圈的大小应该作为相机的一个参数。
我们将从光圈中随机选择光线的起点,这需要一个新的函数来执行这个操作。思路与vec3::randomVectorOnUnitSphere()
类似,只是这次转换到了二维平面上:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
...
inline vec3 unitVectorLength(const vec3& v) {...}
inline vec3 randomVectorOnUnitDisk()
{
while (true)
{
if (point3 p = vec3(randomMinToMax(-1, 1), randomMinToMax(-1, 1), 0);
p.lengthSquared() < 1)
{
return p;
}
}
}
现在,我们可以再次重构camera
类了,需要留意的是,我们使用角度值来表示光圈的大小,而非直接定义光圈的半径:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
class camera
{
public:
...
double defocusAngle = 0;
double focusDistance = 10; // distance from camera lookFrom point to plane of perfect focus
...
private:
...
vec3 u, v, w; // camera basis vectors
vec3 apertureU; // defocus disk horizontal radius
vec3 apertureV; // defocus disk vertical radius
void initialize()
{
imageHeight = static_cast<int> (imageWidth / aspectRatio);
imageHeight = imageHeight < 1 ? 1 : imageHeight;
sampleScaleFactor = 1.0 / samplesPerPixel;
center = lookFrom;
// determine viewport dimensions
double focalLength = (lookFrom - lookAt).length();
double theta = degreesToRadians(verticalFOV);
double h = tan(theta / 2);
double viewportHeight = 2 * h * focusDistance;
double viewportWidth = static_cast<double>(imageWidth) / imageHeight * viewportHeight;
// calculate the u, v, w unit basis vectors for the camera current position
w = unitVectorLength(lookFrom - lookAt);
u = unitVectorLength(cross(viewUp, w));
v = cross(w, u);
// calculate the vectors across the horizontal and vertical viewport edges
vec3 viewportU = viewportWidth * u;
vec3 viewportV = viewportHeight * -v;
// Calculate the horizontal and vertical delta vectors from pixel to pixel
pixelDeltaU = viewportU / imageWidth;
pixelDeltaV = viewportV / imageHeight;
// calculate the location of the upper left pixel
point3 viewportUpperLeft = center - focusDistance * w - viewportU / 2 - viewportV / 2;
firstPixelLocation = viewportUpperLeft + 0.5 * (pixelDeltaU + pixelDeltaV);
// calculate the camera defocus disk basis vectors
double apertureRadius = focusDistance * tan(degreesToRadians(defocusAngle / 2));
apertureU = u * apertureRadius;
apertureV = v * apertureRadius;
}
[[nodiscard]]
ray getRay(int i, int j) const
{
// construct a camera ray originating from the aperture and
// directed at random sampled point around the pixel location i, j
vec3 offset = sampleFromSquare();
point3 randomSampleLocation =
firstPixelLocation + (i + offset.x()) * pixelDeltaU + (j + offset.y()) * pixelDeltaV;
point3 rayOrigin = defocusAngle <= 0 ? center : apertureSample();
vec3 rayDirection = randomSampleLocation - rayOrigin;
return {rayOrigin, rayDirection};
}
[[nodiscard]]
static vec3 sampleFromSquare() {...}
[[nodiscard]]
point3 apertureSample() const
{
// returns a random point in the camera aperture
point3 p = randomVectorOnUnitDisk();
return center + (p[0] * apertureU + p[1] * apertureV);
}
[[nodiscard]]
static color rayColor(const ray& rayIncoming, int depth, const hittable& world) {...}
};
最后,我们将测试场景中的相机的光圈调大一些:
1
2
cam.defocusAngle = 10.0;
cam.focusDistance = 3.4;
渲染中。。。
本文由作者按照 CC BY 4.0 进行授权