这个系列是Physically Based Rendering: From Theory To Implementation的读书笔记,本节主要介绍相机模型的相关内容。
为了生成具有真实感的图像我们需要去模拟相机的成像过程。在PBRT中使用投影相机模型(projective camera models)来描述相机的行为,同时为了描述成像过程中物体的位置我们还需要引入3个坐标系:
- 屏幕空间(screen space):屏幕空间定义了渲染生成图像的范围,它的原点位于相机中心且物体的深度被规范化到[0, 1]区间上。
- 标准化设备坐标(normalized device coordinate, NDC):NDC是规范化后的屏幕空间,此时x和y坐标都被规范化到[0, 1]区间上。
- 光栅空间(raster space):光栅空间表示实际图像定义的空间,x和y坐标由图像的分辨率来定义。
在PBRT中使用4×4的转换矩阵来描述不同坐标系之间的变换关系,而在生成图像时则是从相机发射光线并将光线变换到世界坐标系中来进行计算。不同类型的相机定义了不同的坐标变换关系,这就导致了即使位于相同的位置设置不同类型的相机仍会得到不同的渲染结果。
Orthographic Camera
正交投影相机(orthographic camera)是最基本的投影相机模型。正交投影相机在投影时会直接压缩物体的深度,这样的过程可以通过平移和缩放来描述:
\[P = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & \frac{1}{z_\text{far} - z_\text{near}} & -z_\text{near} \\ 0 & 0 & 0 & 1 \\ \end{bmatrix}\]类似地,在生成光线时正交投影相机发出的光线起点位于光栅平面上,而方向则指向z轴正半轴:
float OrthographicCamera::GenerateRay(const CameraSample &sample,
Ray *ray) const {
// Compute raster and camera sample positions
Point3f pFilm = Point3f(sample.pFilm.x, sample.pFilm.y, 0);
Point3f pCamera = RasterToCamera(pFilm);
*ray = Ray(pCamera, Vector3f(0, 0, 1));
*ray = CameraToWorld(*ray);
return 1;
}
Perspective Camera
正交投影相机的缺陷在于它无法表达物体的透视关系,因此更为常用的相机模型是透视投影相机(perspective camera)。在透视投影相机中,空间中的物体首先会通过投影变换来获得透视关系:
\[P = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & \frac{z_\text{far}}{z_\text{far} - z_\text{near}} & -\frac{z_\text{far} \cdot z_\text{near}}{z_\text{far} - z_\text{near}} \\ 0 & 0 & 1 & 0 \\ \end{bmatrix}\]然后在根据给定的视野(field of view, FoV)来规范化到NDC上:
\[P = \begin{bmatrix} \frac{1}{\tan{\frac{\theta}{2}}} & 0 & 0 & 0 \\ 0 & \frac{1}{\tan{\frac{\theta}{2}}} & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \\ \end{bmatrix} \times \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & \frac{z_\text{far}}{z_\text{far} - z_\text{near}} & -\frac{z_\text{far} \cdot z_\text{near}}{z_\text{far} - z_\text{near}} \\ 0 & 0 & 1 & 0 \\ \end{bmatrix}\]透视投影相机在发射光线时,光线起点固定在原点(相机中心),方向指向光栅平面上的坐标。
float PerspectiveCamera::GenerateRay(const CameraSample &sample,
Ray *ray) const {
// Compute raster and camera sample positions
Point3f pFilm = Point3f(sample.pFilm.x, sample.pFilm.y, 0);
Point3f pCamera = RasterToCamera(pFilm);
*ray = Ray(Point3f(0, 0, 0), Normalize(Vector3f(pCamera)));
*ray = CameraToWorld(*ray);
return 1;
}
Thin Lens Model
正交投影相机和透视投影相机的缺陷在于它们假设了相机服从理想针孔相机模型,因此无论物体的深度如何都能够清晰地成像。然而现实中的相机则不满足这一假设,由于透镜的存在相机只能对深度位于一定范围内的物体来成像,这个范围称为景深(depth of field)。
要描述相机的这一行为,我们需要介绍薄透镜模型(thin lens model)。我们假设透镜位于$z=0$平面上,而且平行于主光轴的光线会汇聚到$p$点,称为焦点(focal point);$p$点到透镜的距离称为焦距(focal length),记为$f$。
薄透镜模型的一个基本性质是成像公式(Gaussian lens equation):对于深度为$z$的物体,成像的距离满足:
\[\frac{1}{z'} - \frac{1}{z} = \frac{1}{f}\]不难发现,平行光实际上可以看做是$z=-\infty$情况下成像公式的特例。
利用成像公式可以解出$z$点的像距:
\[z' = \frac{f z}{f + z}\]当像距不满足上式时成像就会模糊。在实际成像时一般无法保证像距严格满足成像公式,但只要成像平面在像距的一定范围内成像的模糊都是可以接受的。当我们固定像距时物距不等于$z$的物体会投影成一个圆盘,也称为circle of confusion,它的大小取决于光圈、焦距、物体的深度以及透镜本身。在成像时只要circle of confusion的大小小于1个像素就可以认为成像是清晰的。
我们可以利用几何关系来计算出circle of confusion的大小。假设镜头的直径为$d_l$,根据相似三角形有:
\[\frac{d_l}{z'} = \frac{d_c}{\vert z' - z_f' \vert}\]带入成像公式有:
\[d_c = \bigg\vert \frac{d_l f(z - z_f)}{z(f + z_f)} \bigg\vert\]在光线追踪框架中想要模拟景深的效果非常简单,我们只需要在透镜上随机采样出一个点并从该点发射出光线即可。更新后的光线起点位于透镜内,方向指向焦距平面上的点:
为了模拟景深的效果我们需要为相机添加透镜半径lensRadius
和焦距focalDistance
两个属性,然后在生成光线时添加如下代码:
// Modify ray for depth of field
if (lensRadius > 0) {
// Sample point on lens
Point2f pLens = lensRadius * ConcentricSampleDisk(sample.pLens);
// Compute point on plane of focus
float ft = focalDistance / ray->d.z;
Point3f pFocus = (*ray)(ft);
// Update ray for effect of lens
ray->o = Point3f(pLens.x, pLens.y, 0);
ray->d = Normalize(pFocus - ray->o);
}
除了上面介绍的三种相机模型外,PBRT中还包括了全景相机和真实相机模型。这些模型暂时还用不到,等后面有机会再补上。