昨天,看了Ogre的代码,关于取射线到面的交点坐标的函数,研究了半天,怪自己立体几何没学好啊
代码如下:
//-----------------------------------------------------------------------
std::pair<bool, Real> Math::intersects(const Ray& ray, const Plane& plane)
{
Real denom = plane.normal.dotProduct(ray.getDirection());
if (Math::Abs(denom) < std::numeric_limits<Real>::epsilon())
{
// Parallel
return std::pair<bool, Real>(false, 0);
}
else
{
Real nom = plane.normal.dotProduct(ray.getOrigin()) + plane.d;
Real t = -(nom/denom);
return std::pair<bool, Real>(t >= 0, t);
}
}
//-----------------------------------------------------------------------
分析如下:
Plane.normal是取平面的法向量
ray.getDirection()是取射线的方向向量
函数dotProduct()是用来做向量的点乘,函数结构如下:
inline Real dotProduct(const Vector3& vec) const
{
return x * vec.x + y * vec.y + z * vec.z;
}
把返回的点乘的值赋予demon,通过这个值可以来判断射线与平面是否平行。
//-----------------------------------------邪恶的分割线------------------------------------------
数学原理:两个向量相乘 a*b = |a|*|b|cosθ , 因为a和b向量都是方向向量,标量值不为0,θ就是两个方向向量之间的角度,当cosθ 趋近于0时,θ角度趋近于90°,由此可以说明a和b垂直。(高中的知识啊。。。)
//-----------------------------------------邪恶的分割线------------------------------------------
函数中把点成的值在用Math::Abs(denom) 其实就是fabs()函数,取其绝对值,在跟std::numeric_limits<Real>::epsilon()比较,这里的epslion是一个趋近于0的数,形如:
#define FLT_EPSILON 1.192092896e-07F /* smallest such that 1.0+FLT_EPSILON != 1.0 */
当demon的值比其还小的时候,就可以近似的认为两个向量点乘的值为0,即两个向量垂直。
由于两个都是方向向量,所以,射线和平面就平行了。返回一个bool类型为false的值,和一个real值为0(也就是下面说的t值)。
//-----------------------------------------邪恶的分割线------------------------------------------
下面就是重点部分了,如果不平行,先恶补一下数学的知识
一条射线可以表示为:p(t) = p1+tu ;其中,p1为起点坐标,u为射线的方向向量,t为参数。如果t = 0,得到的是起点的坐标。
一个平面可以表示为:np+d = 0;其中,n为平面的法向量,p为平面上任意一点,d为参数。
如果射线与平面相交,把两个表达式合并,即:n*(p1+tu)+d = 0;经过变化以后得:t = -((d+n*p1)/(nu))
//-----------------------------------------邪恶的分割线------------------------------------------
现在,在把公式和代码比较就很好理解了。
Real nom = plane.normal.dotProduct(ray.getOrigin()) + plane.d; // 求的就是 d+n*p1的值,ray.getOrigin()得到的就是起点的坐标,plane.d得到的就是平面的d值。
Real t = -(nom/denom); //denom上面已将求过了,就是n*u,此代码就实现了t = -((d+n*p1)/(nu))
函数返回bool值,且t要大于等于0为真,如果t小于0,就得到了一条三维空间的直线(就不是射线了 ) ,然后在返回t值。
然后利用p(t) = p1+tu 就可以很容易的求出交点坐标了