3D数学基础:图形和游戏开发(第2版)第7章笔记

本文详细介绍了二维和三维极坐标系统,包括极坐标的定义、转换以及在二维和三维空间中的应用。讨论了极坐标相对于笛卡尔坐标的优势,并在3D游戏开发的上下文中,提出了航向和俯仰的球面坐标概念,同时阐述了坐标别名和万向节死锁的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


7.1节描述了二维极坐标。
7.2节给出了一些极坐标比笛卡尔坐标更可取的例子。
7.3节展示了极空间如何在三维中工作,并介绍了圆柱坐标和球面坐标。
最后,7.4节明确指出,极坐标空间既可以用来描述位置,也可以用来描述矢量。

7.1 关于二维极坐标空间

在这里插入图片描述
极坐标空间只有一个轴(极轴),它通常被描述为来自原点的射线。在数学文献中,极轴通常在图表中指向右,因此在笛卡尔坐标系中,它对应于+x轴。

使用二维极坐标定位一个点(r, θ)

  • 步骤1。从原点开始,面向极轴的方向,旋转θ角。θ的正值通常解释为平均逆时针旋转,负值则解释为顺时针旋转。
  • 步骤2。现在从原点向前移动r个单位的距离。到达了极坐标(r, θ)所描述的点。

在这里插入图片描述

r定义了点到原点的距离,θ定义了点到原点的方向。

注意:我们喜欢用度数表示角度,但是计算机更喜欢使用弧度来表示角度。

对于任何给定的点,有无穷多个极坐标对可以用来描述这个点。这种现象被称为别名( Aliasing)。如果两个坐标对的数值不同,但指向空间中的同一点,则它们被称为彼此的别名。注意,在笛卡尔空间中不会出现别名——空间中的每个点都被分配了一个(x, y)坐标对:点到坐标对的映射是一对一的。

一般来说,对于除原点以外的任何一点(r, θ),所有作为(r, θ)别名的极坐标都可以表示为(其中k是任意整数):
( ( − 1 ) k r , θ + 180 ° ) ((-1)^kr, θ+180°) ((1)kr,θ+180°)
一个点的最佳极坐标描述:

  • r >= 0:我们不“向后”测量距离。
  • −180° < θ ≤ 180度:角度限制在1/2圈,使用+180°表示朝“西”。
  • r = 0 => θ = 0。在原点,把角度设为零。

将极坐标对(r, θ)转换为标准形式

  1. 如果r = 0,则赋值θ = 0。
  2. 如果r < 0,则r为负,并向θ加180°。
  3. 如果θ≤−180°,则将θ加360°,直到θ >−180°。
  4. 如果θ > 180°,则将θ减360°,直到θ≤180°。

将极坐标对(r, θ)转换为标准形式C代码:

/**
*@r: 径向距离
*@theta: 弧度为单位的角度
*/
void canonical(float &r, float &theta) {
	//声明一个2 * pi (360度) 的常量
	static float TWOPI = 2.0f * PI;
	//检查我们是否正好在原点
	if(r == 0.0f) {
	//在原点,则强制theta为零
		theta = 0.0f;
	} else {
		//处理负距离
		if(r < 0.0f) {
			r = -r;
			theta += PI;
		}
		/*
		theta是否超出范围了?
		注意,这个if()检查不是严格必要的,
		但如果浮点运算不是必需的,我们会尽量避免它们。
		如果不需要,为什么要损失浮点精度呢?
		*/
		if(fabs(theta) > PI) {
			//按 PI 值弥补
			theta += PI;
			//包含在范围 [0, TWOPI]
			theta -= floor(theta / TWOPI) * TWOPI;
			//撤消弥补,将角度转换回范围(-PI, PI]
			theta -= PI;
		}
	}
}

直角坐标和极坐标之间的转换
将二维极坐标转换为笛卡尔坐标
在这里插入图片描述
二维笛卡尔坐标到极坐标的转换
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
二维笛卡尔坐标到极坐标的转换C代码:

/**
*@x: 笛卡尔坐标x
*@y: 笛卡尔坐标y
*@r: 径向距离
*@theta: 弧度为单位的角度
*/
void conversion(float x, float y, float &r, float &theta) {
	//检查是否在原点
	if (x == 0.0f && y == 0.0f){
		//在原点,两个极坐标都为零
		r = 0.0f;
		theta = 0.0f;
	}else {
		//计算值。atan2函数是不是很棒吧?
		r = sqrt(x * x + y * y);
		theta = atan2(y, x);
	}
}

7.2 为什么有人会使用极坐标?

极坐标的出现是因为人们很自然地用距离和方向来考虑位置。(当然,在使用极坐标时,我们通常不是很精确,但精确并不是大脑的强项。)笛卡尔坐标不是我们的母语。计算机的情况正好相反——一般来说,当使用计算机解决几何问题时,使用笛卡尔坐标比使用极坐标更容易。

7.3 关于三维极坐标空间

圆柱坐标(一角两长度)
在二维极坐标的基础上添加z轴
在这里插入图片描述
球面坐标(两角一长度)
在三维球面空间中也有两个极轴。第一个轴是“水平的”,对应于2D极坐标中的极轴或3D笛卡尔坐标中的+x轴。另一个轴是垂直的,对应于三维笛卡尔坐标中的+y。
在这里插入图片描述
水平角θ称为方位角(azimuth),φ为天顶(zenith)。
你可能听说过的其他术语还有经度和纬度。经度和θ基本相同,纬度是倾斜角90°−φ。

在三维虚拟世界中有用的一些极坐标约定

  • 默认水平方向在θ = 0点方向+x。这很不幸,因为对我们来说,+x点“向右”或者“东方”,这两个方向都不是大多数人心目中的“默认”方向。类似于时钟上的数字从顶部开始的方式,如果水平极轴指向+z,即“向前”或“向北”,对我们来说会更好。
  • 关于φ的惯例在几个方面都是不幸的。如果二维极坐标(r, θ)能简单地通过添加第三个零坐标扩展到三维就更好了,就像我们把笛卡尔坐标系从二维扩展到三维一样。但是球面坐标(r, θ, 0)与我们想要的二维极坐标(r, θ)并不对应。事实上,赋值φ = 0会使我们陷入万向节死锁(Gimbal lock)的尴尬境地(奇点)。相反,二维平面上的点表示为(r, θ, 90°)。测量纬度可能比测量天顶更直观。大多数人会将默认值认为是水平的,而向上是极端的情况。

如果球面坐标的两个角和欧拉角(第8章)的前两个角一样就好了,本书描述一些更适合我们的目的的球面坐标:
水平角θ改名为h, h是航向(Heading)的缩写,类似于指南针的航向。零表示“向前”或“向北”的方向,这取决于上下文。方向为0对应于三维笛卡尔坐标的+z。此外,由于本书用的是左手坐标系,从上面看,正旋转将顺时针旋转。
对顶角φ被重新命名为p,它是俯仰(pitch)的缩写,用来衡量我们向上或向下看的程度。默认的俯仰值0表示水平方向,这是我们大多数人直觉上期望的。也许不那么直观,正的俯仰值是向下旋转的,这意味着俯仰值实际上测量了偏斜角(angle of declination)。
在这里插入图片描述
球面坐标也有别名现象(坐标系包含角度都有别名现象)
二维极空间中的奇点出现在原点,因为当r = 0时,角坐标无关紧要。在球面坐标系下,两个角度在原点都是无关的。
当俯仰角设置为±90°时(或这些值的任何别名),出现奇点。在这种情况下,即所谓的万向节死锁(Gimbal lock),所指示的方向是纯垂直的(直上或直下),而方向角是无关紧要的。
ps:第一次看见万向节死锁把它看成了万圣节死锁(Halloween lock)૧(●´৺`●)૭

在这里插入图片描述

规范球面坐标所满足的条件

  • r >= 0:我们不“向后”测量距离。
  • −180° < h ≤ 180°:航行限制为1/2圈。使用+180°表示朝“南”。
  • −90° ≤ p ≤ 90°:俯仰限制为直上直下,不能“向后”俯仰。
  • r = 0 => h = p = 0:在原点,把角度设为零。
  • |p| = 90° => h = 0:当我们直接向上或向下看的时候,我们把航向设为零。

将球面坐标(r, h, p)转换为标准形式

  1. 如果r = 0,那么赋值h = p = 0。
  2. 如果r < 0,则r变负,h加上180°, 并使p变负。
  3. p < 90°,则对p加360°,直到p ≥ 90°。
  4. 如果p > 270°,则从p减去360°,直到p ≤ 270°。
  5. 如果p > 90°, h加180°,设p = 180°−p。
  6. h≤−180°,则对h加360°,直至h > −180°。
  7. 如果h > 180°,则从h减去360°,直到h ≤ 180°。

将球面坐标(r, h, p)转换为标准形式C代码:

/**
*@r: 径向距离
*@heading: 弧度为单位的角度
*@pitch: 弧度为单位的角度
*/
void canonical(float &r, float &heading, float &pitch) {
	//声明一个2 * pi (360度) 的常量
	static float TWOPI = 2.0f * PI;
	//声明一个pi / 2 (90度) 的常量
	static float PIOVERTWO = PI / 2.0f;
	//检查我们是否正好在原点
	if(r == 0.0f) {
	//在原点,则强制角度为零
		heading = pitch = 0.0f;
	} else {
		//处理负距离
		if(r < 0.0f) {
			r = -r;
			theta += PI;
			pitch = -pitch;
		}
		//俯仰是否超出了范围?
		if (fabs(pitch) > PIOVERTWO){
			//按 PIOVERTWO 值弥补
			pitch += PIOVERTWO;
			//包含在范围 [0, TWOPI]
			pitch -= floor(pitch / TWOPI) * TWOPI;
			//是否超出了范围?
			if (pitch > PI){
				//翻转航行 
				heading += PI;
				//撤消弥补,并设置pitch = 180 - pitch
				pitch = 3.0f * PI / 2.0f - pitch;	//p = 270度 - p 
			}else{
				//撤消弥补,将角度转换回范围(-PIOVERTWO, PIOVERTWO]
				pitch -= PIOVERTWO;
			}
		}
		/*
		是否为万向节死锁?
		在这里使用相对较小的公差进行测试,接近单一精度的极限。
		*/
		if(fabs(heading) > PI) {
			//按 PI 值弥补
			heading += PI;
			//包含在范围 [0, TWOPI]
			heading -= floor(heading / TWOPI) ? TWOPI;
			//撤消弥补,将角度转换回范围(-PI, PI]
			heading -= PI;
		}
	}
}

将数学爱好者使用的球面坐标转换为三维笛卡儿坐标
在这里插入图片描述
在这里插入图片描述

本书中使用的规则的球面坐标到三维笛卡尔坐标的转换
在这里插入图片描述
三维笛卡尔坐标到本书中使用的规则的球面坐标的转换
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
原点处的奇点,即r = 0,被当作一种特殊情况处理。
三维笛卡尔坐标到本书中使用的规则的球面坐标的转换C代码:

/**
*@x: 笛卡尔坐标x
*@y: 笛卡尔坐标y
*@z: 笛卡尔坐标z
*@r: 径向距离
*@heading: 弧度为单位的角度
*@picth: 弧度为单位的角度
*/
void conversion(float x, float y, float z, float &r, float &heading, float &picth) {
	//声明一个2 * pi (360度) 的常量
	static float TWOPI = 2.0f * PI;
	//声明一个pi / 2 (90度) 的常量
	static float PIOVERTWO = PI / 2.0f;
	//计算径向距离
	r = sqrt(x * x + y * y + z * z);
	//检查是否在原点
	if (r == 0.0f){
		//在原点,两个极坐标都为零
		heading = picth = 0.0f;
	}else {
		//计算俯仰角 
		picth = asin(-y / r);
		/*
			检查是否万向节死锁,
			因为atan2库函数在(二维)原点处未定义
			在这里使用相对较小的公差进行测试,接近单一精度的极限
		*/
		if (fabs(picth) >= PIOVERTWO * 0.9999){
			heading = 0.0f;
		} else {
			heading = atan2(x, z);
		}
	}
}

7.4 使用极坐标指定矢量

对极坐标点的运算对于极坐标矢量同样有效。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值