判断射线与是否球体相交, 并计算交点位置
在Ray-tracing中, 计算并判断射线与球体是否相交是不可少的
那么如何来判断一条已知的射线是否交于给定的球体呢?
要计算球体射线交点,我们首先要先给出球与直线的方程
首先是球面方程
$$|x-c|^2=r^2$$
- x为球面上的点
- c为球心
- r为球的半径 然后是直线方程
$$x = o +dl$$
- x为直线上的点
- o为直线起点
- d为交点到原点的距离(一个标量)
- l为射线方向的单位向量
相信方程都很清晰明了,那下面我们要做的就是通过上述两个式子中共有的 X 将方程联立起来
联立,得
$$|o+dl-c|^2=r^2$$
展开,得
$$d^2l^2+2dl(o-c)+(o-c)^2=r^2$$
整理,得
$$l^2d^2+2l(o-c)d+(o-c)^2-r^2=0$$
这时我们便得到了一个关于d的二元一次方程, 不难看出判别式$$\Delta=B^2-4AC$$
其中:
$$A=l^2$$
$$B=2l*(0-c)$$
$$C=(o-c)^2-r^2$$
根据其解的个数, 我们便能判断射线与球体的相交情况了
根的判别式Δ | 交点个数 | 相交情况 |
---|---|---|
<0 | 0 | 直线与球无交点 |
=0 | 1 | 直线与球相切 |
0 | 2 | 直线传过球体
此时我们根据求根公式, 便能快速算出d的值, 再通过射线的方程$$x=o+dl$$便可求出其交点
一个c++风格的 判断直线与球是否相交的代码实现如下
1 |
|
1 注意, 射线(ray)和直线(line)的区别, 射线是有其方向的, 直线方程的解不一定符合射线, 我们需要将负解舍去
2
r.Direction()
即上述公式中的 L , 其实并不需要一定是单位向量, 只要保证其正负与单位向量一致即可
如果在绘制球体的过程, 需要求出交点的具体位置, 比如根据球面法相来输出像素颜色(将三维向量直接作为颜色输出, 在调试你的程序时是十分常用的), 那么上面的代码就不太够看了, 我们对其略做修改
1 |
|
1 这里其实可以化简, 判别式上下同时消掉了一个2
2 根据求根公式, 我们会得到两个根, 求交点时, 我们选取较小的根d, 即离射线原点(往往是camera)较近的那个点
3 根据RayTracingInOneWeekend书中的代码, 这里的
r.Direction()
仍不需一定为单位向量, 只要在后续计算中保持公式 x = r + dl 的一致便仍能得出正确的交点, 读者可自行根据上述公式进行验算。但我个人认为如wikipedia中, 在一开始将Ray
类中的方向向量初始化为单位向量会比较方便, 避免一些忘记手动Normalize而导致的潜在的bug
一个初始化的例子
1 |
|
reference:
https://en.wikipedia.org/wiki/Line-sphere_intersection
https://raytracing.github.io/books/RayTracingInOneWeekend.html
判断射线与是否球体相交, 并计算交点位置
https://matrix4f.com/Math/Geometry/line-sphere-intersection/