c++中射线表示_射线与球的相交测试
本節將描述兩種射線與球體的相交測試方法,方法一是采用參數方程求解的方法,可以計算出直線、射線、線段與球的交點,方法二在Akenine-M?ller等[1]中有描述,只涉及射線與球的相交測試,采用幾何關系來優化測試。此外,對二維空間下,射線與圓的相交測試,也可以采用本節介紹的兩種方法,由于維度更小,所以更加簡單,這里就不再贅述,最后提供描述算法的源碼實現。
文章目錄:
- 參數方程法
- 優化法
- 源碼實現
- 參考
射線
與以為球心、為半徑的球體的相交測試,不需要保證是單位長度。參數方程法
球面可以用參數方程表示為
現在考慮直線與球面的相交,把直線的參數方程代入等式(1)中,得
這是類似于
的一元二次方程,直接求解該方程的解,得令
,則解可以分為三種情況: + i. 若,方程無解,直線與球體不相交; + ii. 若,方程有一個解,直線與球體相切; + iii. 若,方程有兩個解,直線與球體相交。圖1. 直線與球相交的三種情況直線與球體相交的三種情況,如圖1所示。圖(a)就是第i種情況,兩者不相交;圖(b)就是第ii種情況,相切于一點;圖(c)就是第iii種情況,相交于兩點。算法的偽碼如下所示:
直線與以為球心、為半徑的球體的相交測試,可以是非單位長度,若相交,求計算出所有交點return (交點的個數)
1. ;
2. ;
3. ;
4. if , then
5. ????return 0;
6. else if , then
7. ????;
8. ????;
9. ????;
10. ????return 1;
11. else
12. ????;
13. ????;
14. ????;
15. ????;
16. ????return 2;
17. end if;
對于射線與球體的相交測試中,需要保證
的值限定的區間范圍內;對于線段與球體的相交測試中,需要保證的值限定的區間范圍內。算法的C++實現代碼如下所示:
優化法
設射線的參數方程為
,其中是單位長度。如圖2(a)所示,計算出從射線原點出發到球體中心的向量,計算出該向量的長度,若,那么說明射線的原點在球體內部,一定與球體相交,如圖2(c)所示,若只是想判斷它們是否相交,算法至此就可以結束了。否則,計算向量沿著方向的投影:,若,說明球體在射線原點的后面,且射線的原點在球體外,因此可以判定射線與球體一定不相交,完成第一次排除測試,如圖2(b)所示。否則,利用勾股定理,計算出球心與投影之間距離的平方:,若,則可以判定射線與球體一定不相交,完成第二次排除測試。如果射線和球體經過兩次排除測試,則就可以判定它們一定相交。如果只希望判定它們是否相交,算法至此就可以結束了。圖2. 射線與球體相交測試的優化方法接下來,計算射線與球體的交點,計算出距離的平方,
,因為,所以計算出的值。相交點與射線原點之間的距離為,求第一個相交點,若,說明射線原點在球體外,取,否則取,最后把值代入射線的參數方程中,就求出第一個相交點。射線與球體相交測試并計算交點的算法偽碼如下所示:
射線與以為球心、為半徑的球體的相交測試,是單位長度,若相交,計算出第一個交點return ({DISJOINT, INTERSECTING})
1. ;
2. ;
3.
4. if and , then
5. ????return DISJOINT;
6. end if;
7. ;
8. if , then
9. ????return DISJOINT;
10. end if;
11. ;
12. if , then
13. ????;
14. else
15. ????;
16. end if;
17. ;
18. return INTERSECTING;
若不需要計算射線與球體的交點,只需要判斷它們是否相交,那么偽碼如下所示:
射線與以為球心、為半徑的球體的相交測試,是單位長度,不需要計算交點return ({DISJOINT, INTERSECTING})
1. ;
2. ;
3. if , then
4. ????return INTERSECTING;
5. end if;
6. ;
7. if , then
8. ????return DISJOINT;
9. end if;
10. ;
11. if , then
12. ????return DISJOINT;
13. end if;
14. return INTERSECTING;
與參數方程法相比,具有相同的運算次數,但最大的不同是該方法較早地進行篩除計算,從而使這個算法總的開銷要低一些。
源碼實現
基礎庫源碼鏈接,參見這里,下面是前面所描述的算法的實現。
#include "SrGeometricTools.h" #include "SrDataType.h"#include <stdio.h> #include <time.h>namespace {/************************************************************************ description 采用參數方程法和優化法,實現射線與球體的相交測試。 ****************************************************************************//** brief 3D sphere class.This is a 3D sphere class with public data members. It includes center and radius. */class SrSphere3D{public:/**brief Default constructor. Set center to (0,0) and radius to 0.*/SrSphere3D(){mCenter.set(0, 0, 0);mRadius = -1.0;}/**brief The circle is initialized by center and radius.*/SrSphere3D(const SrPoint3D& ct, SrReal rd){mCenter = ct;mRadius = rd;}public:SrPoint3D mCenter;SrReal mRadius;};/**brief 3D ray class.This is a 3D ray class with public data members.The ray is parameterized as X(t) = P+t*d,in which P is the 'base' data,d is the 'direction' data,t>=0.the 'direction' is unit length.*/class SrRay3D{public:/**brief Default constructor, base and direction is set to (0,0).*/SrRay3D(){mBase.set(0, 0, 0);mDirection.set(0, 0, 0);}/**brief The line is initialized by two points.The point p1 is the endpoint of the ray.*/SrRay3D(const SrPoint3D& p1, const SrPoint3D& p2){mBase = p1;mDirection = p2 - p1;mDirection.normalize();}/**brief The ray is valid if the direction of the line is unit.*/bool isValid()const{if (EQUAL(mDirection.magnitudeSquared(), 1.0))return true;return false;}public:SrPoint3D mBase;SrVector3D mDirection;};/*brief 采用參數方程法,實現射線與球體的相交測試。param[out] result 射線可以用參數方程P+tD表示,返回第一個相切點的t值。return true 若射線與球體相交或者相切;false 若射線與球體分離(無交點)。*/bool RayHitTestSphere_Parameter(const SrRay3D& ray, const SrSphere3D& sphere, SrReal& result){SrVector3D e = ray.mBase - sphere.mCenter;SrReal b = e.dot(ray.mDirection), c = e.dot(e) - sphere.mRadius*sphere.mRadius;SrReal delta = b * b - c;if (LESS(delta, 0))return false;else if (EQUAL(delta, 0)){if (LESS(-b, 0))return false;result = -b;return true;}else{SrReal d = sqrt(delta);SrReal t1 = -b - d, t2 = -b + d;if (LESS(t2, 0))return false;else if (LESS(t1, 0))result = t2;elseresult = t1;return true;}return true;}/*brief 采用優化法,實現射線與球體的相交測試。param[out] result 射線可以用參數方程P+tD表示,返回第一個相切點的t值。return true 若射線與球體相交或者相切;false 若射線與球體分離(無交點)。*/bool RayHitTestSphere_Optimized(const SrRay3D& ray, const SrSphere3D& sphere, SrReal& result){SrVector3D l = sphere.mCenter - ray.mBase;SrReal s = l.dot(ray.mDirection);SrReal squaredL = l.dot(l);SrReal squaredRadius = sphere.mRadius * sphere.mRadius;if (LESS(s, 0) && GREATER(squaredL, squaredRadius))return false;SrReal squaredM = squaredL - s * s;if (GREATER(squaredM, squaredRadius))return false;SrReal q = sqrt(squaredRadius - squaredM);if (squaredL > squaredRadius)result = s - q;elseresult = s + q;return true;}/*brief 隨機生成射線數據,測試兩種相交測試方法的正確性,并測試兩種方法的CPU時鐘開銷。*/void Test_RayHitTestSphere(){int numRay = 100000;SrRay3D* ray = new SrRay3D[numRay];//確定測試球體SrSphere3D sphere(SrPoint3D(1000, 1000, 1000), 1000);int i;SrPoint3D p0, p1;//隨機生成測試的射線for (i = 0; i < numRay; i++){do{p0.x = rand() % 2000;p0.y = rand() % 2000;p0.z = rand() % 2000;p1.x = rand() % 2000;p1.y = rand() % 2000;p1.z = rand() % 2000;ray[i] = SrRay3D(p0, p1);} while (!ray[i].isValid());}SrReal t0, t1;bool ret0, ret1;//測試算法的正確性for (i = 0; i < numRay; i++){ret0 = RayHitTestSphere_Parameter(ray[i], sphere, t0);ret1 = RayHitTestSphere_Optimized(ray[i], sphere, t1);ASSERT(ret0 == ret1);//printf("%f,%fn",t0, t1);//ASSERT(EQUAL(t0,t1));}//參數方程法,消耗時間的統計double mTime = clock();for (i = 0; i < numRay; i++){ret0 = RayHitTestSphere_Parameter(ray[i], sphere, t0);}mTime = (clock() - mTime) / CLOCKS_PER_SEC;printf("時間:%fn", mTime);//優化法,消耗時間的統計mTime = clock();for (i = 0; i < numRay; i++){ret0 = RayHitTestSphere_Optimized(ray[i], sphere, t0);}mTime = (clock() - mTime) / CLOCKS_PER_SEC;printf("時間:%fn", mTime);} }int main() {Test_RayHitTestSphere();return 0; }參考
[1] Tomas Akenine-M?ller, Eric Haines, and Naty Hoffman. Real-time rendering, second edition. AK, 2002.
總結
以上是生活随笔為你收集整理的c++中射线表示_射线与球的相交测试的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ZZULIOJ 1136: 首字母变大写
- 下一篇: APT的源列表--sources.lis