Um einen (Phaser)-Strahl zu beschreiben, bedarf es seiner Abschussposition A, seiner Abschussrichtung Dir sowie seiner Strahllänge l:
P = A + l*Dir (Geradengleichung)
Eine Kugeloberfläche wird durch alle Punkte P mit gleichem quadratischem Abstand (Kugelradius) vom Kugelmittelpunkt M aufgespannt:
(P – M)2 = Kugelradius2 (Kugelgleichung)
Ein Ellipsoid lässt sich durch folgende Gleichung beschreiben:
((x-xMittelpunkt)/a)2 + ((y-yMittelpunkt)/b)2 + ((z-zMittelpunkt)/c)2 = 1 (Ellipsoidgleichung)
Trifft nun der Phaserstrahl den Schutzschild, dann kann man auf mathematischem Weg die zugehörige Strahllänge l sowie die Trefferposition bestimmen und mit diesen Informationen Strahl und Schild korrekt rendern sowie mit zusätzlichen Effekten (Schildbeleuchtung, Einschlagsanimation, usw.) versehen.
Für den Fall, dass der Strahl die Kugel (Ellipsoid) nicht nur streift, gibt es neben dem Eintrittspunkt immer auch einen zweiten Schnittpunkt, bei dem der Strahl wieder aus der Kugel (Ellipsoid) austritt. Normalerweise ist man jedoch nur am Eintrittspunkt interessiert.
Auf eine mathematische Herleitung der Schnittpunktgleichungen soll hier verzichtet werden, denn dafür gibt es genügend viele Mathematikbücher; aus Sicht des Spieleprogrammierers ist das Endergebnis relevant:
inline bool Sphere_Ray_Intersections(float* pIntersectionParam,
D3DXVECTOR3* pRayDirection, D3DXVECTOR3* pRayStartPosition,
D3DXVECTOR3* pSpherePosition, float* pSphereScaleSq)
{
float a = pRayDirection->x*pRayDirection->x +
pRayDirection->y*pRayDirection->y +
pRayDirection->z*pRayDirection->z;
D3DXVECTOR3 RayHelp = *pRayStartPosition - *pSpherePosition;
float b = 2.0f*(RayHelp.x*pRayDirection->x +
RayHelp.y*pRayDirection->y +
RayHelp.z*pRayDirection->z);
float c = RayHelp.x*RayHelp.x +
RayHelp.y*RayHelp.y +
RayHelp.z*RayHelp.z - *pSphereScaleSq;
float det = b*b - 4.0f*a*c;
if(det < 0.0f)
return false;
det = FastSqrtExact(det);
a *= 2.0f;
a = 1.0f/a;
float s1 = (-b+det)*a;
float s2 = (-b-det)*a;
if( s1 < s2)
*pIntersectionParam = s1;
else
*pIntersectionParam = s2;
return true;
}
D3DXVECTOR3* pRayDirection, D3DXVECTOR3* pRayStartPosition,
D3DXVECTOR3* pSpherePosition, float* pSphereScaleSq)
{
float a = pRayDirection->x*pRayDirection->x +
pRayDirection->y*pRayDirection->y +
pRayDirection->z*pRayDirection->z;
D3DXVECTOR3 RayHelp = *pRayStartPosition - *pSpherePosition;
float b = 2.0f*(RayHelp.x*pRayDirection->x +
RayHelp.y*pRayDirection->y +
RayHelp.z*pRayDirection->z);
float c = RayHelp.x*RayHelp.x +
RayHelp.y*RayHelp.y +
RayHelp.z*RayHelp.z - *pSphereScaleSq;
float det = b*b - 4.0f*a*c;
if(det < 0.0f)
return false;
det = FastSqrtExact(det);
a *= 2.0f;
a = 1.0f/a;
float s1 = (-b+det)*a;
float s2 = (-b-det)*a;
if( s1 < s2)
*pIntersectionParam = s1;
else
*pIntersectionParam = s2;
return true;
}
D3DXVECTOR3* pRayDirection, D3DXVECTOR3* pRayStartPosition,
D3DXVECTOR3* pEllipsoidPosition,
D3DXVECTOR3* pEllipsoidScaleSq)
{
D3DXVECTOR3 RayHelp = *pRayStartPosition - *pEllipsoidPosition;
float a = pRayDirection->x*pRayDirection->x/pEllipsoidScaleSq->x +
pRayDirection->y*pRayDirection->y/pEllipsoidScaleSq->y +
pRayDirection->z*pRayDirection->z/pEllipsoidScaleSq->z;
float b = 2.0f*(RayHelp.x*pRayDirection->x/pEllipsoidScaleSq->x +
RayHelp.y*pRayDirection->y/pEllipsoidScaleSq->y +
RayHelp.z*pRayDirection->z/pEllipsoidScaleSq->z);
float c = RayHelp.x*RayHelp.x/pEllipsoidScaleSq->x +
RayHelp.y*RayHelp.y/pEllipsoidScaleSq->y +
RayHelp.z*RayHelp.z/pEllipsoidScaleSq->z - 1.0f;
float det = b*b - 4.0f*a*c;
if(det < 0.0f)
return false;
det = FastSqrtExact(det);
a *= 2.0f;
a = 1.0f/a;
float s1 = (-b + det)*a;
float s2 = (-b - det)*a;
if(s1 < s2)
*pIntersectionParam = s1;
else
*pIntersectionParam = s2;
return true;
}