计算机图形学数学基础译自 ScratchaPixel 网站,数学基础部分共八篇,本篇为第七篇,感兴趣的同学可以参考我的 GitBook 镜像。
三角函数 Trigonometric Functions
通常用2D坐标中的 单位圆(unit circle) 表示三角函数的性质。在单位圆上绘制一点P,由原点和P点的连线与x轴的夹角为。P点的x坐标对应于
的余弦(cosine)值,同样,其y坐标对应
的正弦(sine)值。需要注意的是C++中计算使用的是 弧度(radians),需要将 角度(degrees) 转换为弧度(radians):
。
图形学中一个比较重要的函数是 反正切(arctangent),即正切(tangent)的反函数。假如有一点P坐标为(0.707, 0.707),可以得到 的值为
。假如P的坐标为(-0.707, -0.707),
的值应为
。但是
tan
函数计算-0.707/-0.707的值为1,因此得到的角度仍为 。解决方法是采用C/C++中的
atan2
函数。下面是我们讨论的公式:
atan2
函数计算的结果为正表示 是逆时针角度(
y>0
的象限),结果为负表示 是顺时针角度(
y<0
的象限),结果的范围在 之间。
在球面坐标表示向量 Representing Vectors with Spherical Coordinates
目前我们知道在 笛卡尔坐标系(Cartesian Coordinates) 表示向量(三个值,每个值表示一个轴)。同样,在 球面坐标系(Spherical Coordinates) 可以用两个值表示与笛卡尔坐标系相同的向量。如图所示:
向量与球面垂直的平面所成的角称为 。向量的投影与球面横切面所成的角称为
。图形学中
的范围为
,
的范围为
, 如下图:
球面坐标系的正式定义还包括一些专业术语,如r
表示径向距离(radial distance)。
和
分别被称为 极角(polar angle) 和 方位角(azimuth angle)。 如果不需要考虑向量的长度,球面坐标系是表示向量的另一种方式,这点在 着色(shading) 时非常重要。
z轴向上 Z is up
在数学和物理学中,坐标轴的表示通常采用左手定则,z轴向上,y轴向右,x轴指向自己。如图所示:
笛卡尔坐标系与球面坐标系的转换 Converting Cartesian to Spherical Coordinates
如图所示
单位圆内向量与z轴的夹角为: ,向量在z轴上的坐标为
Vz
。这里可以容易得到公式:
在C++中可以写为:
float theta = acos(Vz);
对于 角的计算。
根据图中所示,可以方便得到公式:
在C++中可以写为:
float phi = atan2(Vy, Vx);
球面坐标系与笛卡尔坐标系的转换 Spherical Coordinates to Cartesian Coordinates
根据球面坐标系的图,以下公式容易得到:
以下是C++的转换代码:
template<typename T>
Vec3<T> sphericalToCartesian(const T &theta, const T &phi) {
return Vec3<T>(cos(phi) * sin(theta), sin(phi) * sin(theta),cos(theta));
}
更多三角函数的技巧 More Tricks with Trigonometric Functions
- 计算
值,虽然仅需要z轴坐标就可以计算,但是为了安全,使用
clamp
函数限制z的值在 [-1,1] 之间。
template<typename T>
inline T sphericalTheta(const Vec3<T> &v) {
return acos(clamp<T>(v[2], -1, 1));
}
- 计算
值,C++的
atan2
函数的返回值在之间,我们需要将其转换至
之间:
template<typename T>
inline T sphericalPhi(const Vec3<T> &v) {
T p = atan2(v[1],v[0]);
return (p < 0) ? p + 2 * M_PI : p;
}
- 大部分场景我们并不需要计算出
或
的值。我们只需计算出
,
,
,
的值。计算
的值比较直接:
template<typename T>
inline T cosTheta(const Vec3<T> &w) {
return w[2];
}
根据 勾股定理(Pythagorean theorem),我们可以得到: 。假如
,且
。则可以得到:
因此有如下代码:
template<typename T>
inline T sinTheta(const Vec3<T> &w) {
return std::max(T(0), 1- cosTheta(w) * cosTheta(w));
}
template<typename T>
inline T sinTheta(const Vec3<T> &w) {
return sqrt(sinTheta2(w));
}
对于 的计算:
template<typename T>
inline T cosPhi(const Vec3<T> &w) {
T sintheta = sinTheta(w);
if (sintheta == 0) return 1;
return clamp<T>(w[0] / sintheta, -1, 1);
}
template<typename T>
inline T shiPhi(const Vec3<T> &w) {
T sintheta = sinTheta(w);
if (sintheta) return 0;
return clamp<T>(w[1] / sintheta, -1, 1);
}