计算机图形学数学基础译自 ScratchaPixel 网站,数学基础部分共八篇,本篇为第七篇,感兴趣的同学可以参考我的 GitBook 镜像。
通常用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
的象限),结果的范围在 之间。
目前我们知道在 笛卡尔坐标系(Cartesian Coordinates) 表示向量(三个值,每个值表示一个轴)。同样,在 球面坐标系(Spherical Coordinates) 可以用两个值表示与笛卡尔坐标系相同的向量。如图所示:
向量与球面垂直的平面所成的角称为 。向量的投影与球面横切面所成的角称为 。图形学中 的范围为 , 的范围为 , 如下图:
球面坐标系的正式定义还包括一些专业术语,如r
表示径向距离(radial distance)。
和 分别被称为 极角(polar angle) 和 方位角(azimuth angle)。 如果不需要考虑向量的长度,球面坐标系是表示向量的另一种方式,这点在 着色(shading) 时非常重要。
在数学和物理学中,坐标轴的表示通常采用左手定则,z轴向上,y轴向右,x轴指向自己。如图所示:
如图所示
单位圆内向量与z轴的夹角为: ,向量在z轴上的坐标为Vz
。这里可以容易得到公式:
在C++中可以写为:
float theta = acos(Vz);
对于 角的计算。
根据图中所示,可以方便得到公式:
在C++中可以写为:
float phi = atan2(Vy, Vx);
根据球面坐标系的图,以下公式容易得到:
以下是C++的转换代码:
template<typename T>
Vec3 sphericalToCartesian(const T &theta, const T &phi) {
return Vec3(cos(phi) * sin(theta), sin(phi) * sin(theta),cos(theta));
}
clamp
函数限制z的值在 [-1,1] 之间。template<typename T>
inline T sphericalTheta(const Vec3 &v) {
return acos(clamp(v[2], -1, 1));
}
atan2
函数的返回值在 之间,我们需要将其转换至 之间:template<typename T>
inline T sphericalPhi(const Vec3 &v) {
T p = atan2(v[1],v[0]);
return (p < 0) ? p + 2 * M_PI : p;
}
template<typename T>
inline T cosTheta(const Vec3 &w) {
return w[2];
}
根据 勾股定理(Pythagorean theorem),我们可以得到: 。假如 ,且 。则可以得到:
因此有如下代码:
template<typename T>
inline T sinTheta(const Vec3 &w) {
return std::max(T(0), 1- cosTheta(w) * cosTheta(w));
}
template<typename T>
inline T sinTheta(const Vec3 &w) {
return sqrt(sinTheta2(w));
}
对于 的计算:
templateT>
inline T cosPhi(const Vec3<T> &w) {
T sintheta = sinTheta(w);
if (sintheta == 0) return 1;
return clamp<T>(w[0] / sintheta, -1, 1);
}
templateT>
inline T shiPhi(const Vec3<T> &w) {
T sintheta = sinTheta(w);
if (sintheta) return 0;
return clamp<T>(w[1] / sintheta, -1, 1);
}