3.5 法线(Normals)
表面法线(surface normal)(或简称法线(normal))是一个在特定位置上与表面垂直的向量。它可以定义为在某一点上与表面相切的任意两个不平行向量的叉积。尽管法线上看起来与向量相似,但区分二者是很重要的:因为法线是根据其与特定表面的关系来定义的,所以在某些情况下,特别是在应用变换时,它们的行为与向量不同。(这一差异在第 3.10 节中讨论。)
/** Normal3 定义 */
template <typename T>
class Normal3 : public Tuple3<Normal3, T> {
public:
/** Normal3 公有方法 */
};
/** Normal3 定义 */
using Normal3f = Normal3<Float>;
Normal3 和 Vector3 的实现非常相似。像向量一样,法线由三个分量 x 、 y 和 z 表示;它们可以相加和相减以计算新的法线;并且可以进行缩放和归一化。然而,法线不能与点相加,也不能对两个法线进行叉乘。请注意,不幸的是,法线 不 一定是归一化的。
除了通常的构造函数(此处不包括), Normal3 允许在给定显式类型转换的情况下从 Vector3 值进行转换,类似于其他基于 Tuple2 和 Tuple3 的类。
/** Normal3 公有方法 */
template <typename U>
explicit Normal3<T>(Vector3<U> v)
: Tuple3<pbrt::Normal3, T>(T(v.x), T(v.y), T(v.z)) {}
Dot() 和 AbsDot() 函数也被重载以计算法线和向量之间各种可能组合的点积。此代码将在此文本中不予包含。我们也不会在此包含所有其他 Normal3 方法的实现,因为它们与向量的实现类似。
一个新的操作实现源于这样一个事实:通常需要翻转一个表面法线,使其位于与给定向量相同的半球内——例如,常常需要与离开表面的光线位于同一半球的表面法线。 FaceForward() 工具函数封装了这个小计算。( pbrt 还为其他三种 Vector3 和 Normal3 的组合提供了该函数的变体。)不过,在使用其他实例时要小心:例如,当使用接受两个 Vector3 的版本时,确保第一个参数是应该返回的(可能翻转的)法线,而第二个是要进行测试的向量。反转这两个参数将会产生意想不到的结果。
/** Normal3 内联函数 */
template <typename T>
Normal3<T> FaceForward(Normal3<T> n, Vector3<T> v) {
return (Dot(n, v) < 0.f) ? -n : n;
}