记录一下:
在看Muli3D的RayTracing 的过程中,看到在PS中进行处理 Ray 和 Sphere 是否相交,并且计算交点,现在推导一下过程。
具体的代码:
const vector3 vSphereOrigin( vSphereData.r, vSphereData.g, vSphereData.b );
const float32 fSphereRadius = vSphereData.a;
// vDiff 是ray.origin 指向球心 的向量
const vector3 vDiff = vSphereOrigin - i_vRayOrigin;
// vDiff 在 i_vRayDir的投影
const float32 fV = fVector3Dot( vDiff, i_vRayDir );
// fVector3Dot( vDiff, vDiff )就是 vDiff 的长度的平方
// (是ray.origin 指向球心 的向量的长度的平方)
float32 fDist = fSphereRadius * fSphereRadius + fV * fV - fVector3Dot( vDiff, vDiff );
// ray 碰不到 shpere的情况下
if( fDist < 0.0f )
continue;
// 计算距离
fDist = fV - sqrtf( fDist );
if( fDist >= 0.0f )
{
// collision with sphere
// 计算出ray 碰撞到的 最小的距离 的球
if( fDist < fCollisionDistance )
{
fCollisionDistance = fDist;
iCollsionSphere = iSphere;
// 计算 交点 (World space)
vCollisionPoint = i_vRayOrigin + i_vRayDir * fCollisionDistance;
// 计算 法线 (World space)
vCollisionNormal = vCollisionPoint - vSphereOrigin;
}
}
1. 判断圆与Ray是否相交
vDiff = B - A, 那就是 A 指向 B 的向量
fV 就是 AB向量在Ray的投影,投影长度, (代码i_vRayDir 是一个单位向量),所以,fV = AC
代码中 float32 fDist = fSphereRadius * fSphereRadius + fV * fV - fVector3Dot( vDiff, vDiff );
fVector3Dot( vDiff, vDiff ) -> vDiff 长度的平方,那就是AB^2
fDist 结合图中得到的就是
fDist = R^2 + fV ^2 - vDiff ^ 2
->R^2 + AC ^2 - AB ^ 2
根据勾股定理:
d^2 = AB ^ 2 - AC ^2
所以得到
fDist = R^2 - d^2
所以,fDist 就是图中的 y^2 ,
那么到这里就知道,其实代码是利用 圆心到Ray的距离 与 圆的半径 的大小比较 来判断 Ray是否与圆相交。
2. 计算圆与Ray的交点
代码 : fDist = fV - sqrtf( fDist ); 表示什么意思呢,结合图
fDist = AC - sqrtf( R^2 - d^2 )
留意图,其实还有一个关系:
x^2 = R^2 - d^2
得到的
fDist = AC - x
那么就可以得到 fDist 就是 Ray 的原点 与 圆相交点的距离,距离知道了,就可以直接计算交点了。
所以就有
vCollisionPoint = i_vRayOrigin + i_vRayDir * fCollisionDistance;