pbrt-v3中的投影变换与栅格空间

931 阅读2分钟

投影变换

pbrt\text{pbrt} 第六章投影相机模型中讲到,其利用持有一系列变换矩阵来简化在相机和视口之间采样的转换。但其中关于投影变换矩阵的推导,与我以往在别的书籍资料中看到的都不太一样。

pbrt\text{pbrt} 中默认就是左手坐标系,其正交投影矩阵的目的是将视体变换为近平面为 z=0z=0,远平面为 z=1z=1 的范围,因此其变换的过程为:

  1. 一次平移变换将近平面 z=nz=n 变换到 z=0z=0 处。
  2. 经过前面的变换,远平面 z=fz=f 被平移到了z=fnz=f-n 处。再进行一次缩放变换,将远平面 z=fnz=f-n 压缩到 z=1z=1 ,对于x,y x,y 坐标没做任何的平移和缩放操作。
Scale(1, 1, 1 / (f - n)) * Translate({0, 0, -n});

image.png

其他资料中的推导,一般是经过投影变换到齐次裁剪空间,再经过透视除法到 NDC\text{NDC} 空间。NDC\text{NDC} 空间是一个取值范围在 [1,1]3[-1,1]^3 的立方体,且视体中心位于坐标原点,而 pbrt\text{pbrt} 中视体经过正交投影变换后还不是 NDC\text{NDC} 空间。

pbrt\text{pbrt} 中,经过投影变换后,视体 zz 坐标范围是 [0,1][0,1]

  1. 根据分辨率计算出宽高比。
  2. 如果宽比高长,则高的取值范围为 [1,1][-1,1],宽的取值范围为 [-aspect,aspect][\text{-aspect},\text{aspect}]
  3. 如果高比宽长,则高的取值范围为 [1aspect,1aspect][\frac{-1}{\text{aspect}},\frac{1}{\text{aspect}}],宽的取值范围为 [-1,1][\text{-1},\text{1}]
float aspect = float(film->resolution.x) / float(film->resolution.y);
Bounds2f screen;
if(aspect > 1.0f){
    screen = Bounds2f(Point2f(-aspect, -1.0f), Point2f(aspect, 1.0f))
} else{
    screen = Bounds2f(Point2f(-1.0f, -1.0f / aspect), Point2f(1.0f, 1.0f / aspect));
}

栅格空间

pbrt\text{pbrt} 的相机中持有了屏幕空间到栅格空间的变换 ScreenToRaster\text{ScreenToRaster},也就是说 pbrt\text{pbrt} 定义经过投影变换后就是屏幕空间。注意,pbrt\text{pbrt} 中的 NDC\text{NDC} 空间的近平面左上角为原点,且边长为 11 ,而不是其他常见资料里中心为原点、边长实际为 22 、取值范围在 [1,1]3[-1,1]^3 的标准立方体。

  1. 进行一次平移变换,将视体的近平面左上角变换到原点。
  2. 进行一次缩放变换将视体变换为边长为 11 的立方体,即归一化
  3. 再一次缩放变换将归一化后的 NDC\text{NDC} 空间下的视体,变换到分辨率的倍数,即栅格化空间。

变换到栅格空间后,极大的简化了对屏幕像素采样点的计算,我们只需要在需要的时候对点或者向量进行相应的变换即可。因此 pbrt\text{pbrt} 还存储了一系列的逆变换,以方便计算。

rasterToScreen = Inverse(screenToRaster);
rasterToCamera = Inverse(cameraToScreen) * rasterToScreen;
  1. 在栅格空间里采样点,将该点从栅格空间变换回相机空间:RasterToCamera\text{RasterToCamera}
  2. 再在相机空间与坐标系原点组成方向并生成射线。
  3. 再将相机空间下的射线转为世界空间:CameraToWorld\text{CameraToWorld}
Point3f origin = Point3f(0, 0, 0);
Point3f pCamera = rasterToCamera.TransPoint(Point3f(sample.x, sample.y, 0));
Vector3f dir = Normalize(pCamera - origin);
return cameraToWorld.TransRay(origin, dir);

注:代码是参考pbrt到自制软渲染器中后的代码,并非pbrt的源代码,仅供参考。