射线与椭球碰撞检测及法线计算原理探究(含非原点场景)
射线与椭球的碰撞检测及法线计算原理解析
一、原点处椭球的实现原理
碰撞检测逻辑
标准椭球(中心在原点,三个轴半径为 ra=(rx, ry, rz))的方程是:
(x/rx)² + (y/ry)² + (z/rz)² = 1
射线的参数方程为 ro + t*rd,其中 ro 是射线起点,rd 是单位方向向量,t 是射线前进的参数(正数表示射线前方)。
把射线方程代入椭球方程后,会得到一个关于 t 的二次方程。为了简化计算,代码里先做了坐标缩放变换:将椭球沿三个轴缩放成单位球——把射线起点和方向向量分别除以对应轴的半径,得到 ocn = ro/ra、rdn = rd/ra。此时原方程转化为单位球的方程:
dot(ocn + trdn, ocn + trdn) = 1
展开后整理为标准二次方程 a*t² + 2b*t + (c-1) = 0,其中:
a = dot(rdn, rdn):二次项系数b = dot(ocn, rdn):一次项系数的一半c = dot(ocn, ocn):常数项的一部分
接下来计算判别式 h = b² - a*(c-1):
- 若
h < 0:二次方程无实根,射线与椭球无交点,返回vec2(-1.0) - 若
h ≥ 0:计算两个实根t = (-b ± √h)/a,返回的两个值分别是射线与椭球的近交点参数和远交点参数(t值越小,距离射线起点越近)
法线计算逻辑
椭球表面某点的法线可以通过方程的梯度求得。标准椭球方程的梯度为 (2x/rx², 2y/ry², 2z/rz²),归一化后就得到单位法线向量。代码中直接用 pos/(ra*ra) 再归一化,和梯度计算的结果一致(常数因子不影响方向)。
二、椭球不位于原点时的处理
只需要对射线起点做平移变换,把椭球中心移到原点即可,方向向量不受平移影响:
修改后的碰撞检测函数
// 椭球中心为center,半径为ra vec2 eliIntersectAtCenter( in vec3 ro, in vec3 rd, in vec3 center, in vec3 ra ) { vec3 ro_shifted = ro - center; // 将射线起点平移到椭球中心为原点的坐标系 vec3 ocn = ro_shifted / ra; vec3 rdn = rd / ra; float a = dot( rdn, rdn ); float b = dot( ocn, rdn ); float c = dot( ocn, ocn ); float h = b*b - a*(c-1.0); if( h<0.0 ) return vec2(-1.0); // 无交点 h = sqrt(h); return vec2(-b-h,-b+h)/a; }
修改后的法线计算函数
vec3 eliNormalAtCenter( in vec3 pos, in vec3 center, in vec3 ra ) { vec3 pos_shifted = pos - center; // 将碰撞点平移到椭球中心为原点的坐标系 return normalize( pos_shifted/(ra*ra) ); }
核心逻辑和原点处的实现完全一致,只是多了一步坐标系平移,把非原点的椭球转化为原点处的椭球来计算。
内容的提问来源于stack exchange,提问作者ChaoSXDemon
相关产品推荐
相关产品推荐

