二维中的OBB相交测试

from:http://blog.csdn.net/ryfdizuo/article/details/12655855

1. 背景知识

OBB全称oriented bounding box,比AABB(axis-aligned bounding box)多了一个方向性。

求交核心思想:向量点积的投影意义,unitX为(1,0)单位向量, A.dot( unitX )  结果值为为A点的x分量,表示意义是A点在x轴上的投影。

1)一维求交

 

如图1,线段[t1, t2]和线段[t3, t4]相交 当且仅当 两个线段在T轴上是投影区间相交,即 

 

  1. t3 < t4 && t1 < t2 && !(t3 >= t2 || t1 >= t4)  

2)二维AABB相交

 

 

如图2,蓝色OBB在X轴投影区间为[x1, x3],在Y轴投影区间为[y1, y3];橙色矩形在X轴投影为[x2, x4],在Y轴投影区间为[y2, y4]。二维的OBB求交通过投影,转化为两个一维的求交运算。蓝色、红色矩形相交当且仅当它们在X、Y轴上的投影区间同时相交。

 

 

  1. !(x2 >= x3 || x1 >= x4) && !(y2 >= y3 || y1 >= y4)  

3)二维OBB求交

 

如图3,蓝色、橙色为任意朝向包围矩形。我们选橙色矩形相邻两条正交边为投影轴S和T轴,且橙色矩阵在S轴上投影区间为[s1, s2],在T轴上投影区间为[t1, t2];蓝色矩形在S轴投影区间为[s3, s4],在T轴上投影为[t3, t4]。与AABB求交思路类似,OBB橙色矩形和蓝色矩形相交则一定得出他们在坐标系S-T轴上的投影区间分别相交,但不是充分必要件:

左图中,首先将蓝色、橙色矩形分别投影在橙色矩阵确定的S、T轴上,投影区间都相交,但是目测两个矩形其实并没有相交;再次将蓝色、橙色矩形分别投影到在蓝色矩形确定的S、T轴上,结果二者在S轴上的投影区间并不相交,从而得出蓝色、橙色矩形其实并不相交。

因此,OBB相交测试中需要投影到橙色的坐标系上做投影测试,如果通过则投影到蓝色矩形坐标系上做测试,只有两次都相交才可以。代码实际执行时,第一次相交测试已经能过滤至少50%的case,只有剩余的50%再次执行第二次相交测试。

2. 代码

google obb求交,第一个资料就是flipcode的教程:http://www.flipcode.com/archives/2D_OBB_Intersection.shtml

我实际测试发现flip code上的obb代码并不能正确运行。

下面是我修改后的程序: 2dobb.h 

 

  1. class Vector2  
  2. {  
  3. public:   
  4.     typedef float data_type;  
  5.       
  6.     Vector2() {  
  7.         m_Data[0] = m_Data[1] = 0.0f;  
  8.     }  
  9.   
  10.     Vector2(const Vector2& other) {  
  11.         m_Data[0] = other.m_Data[0];  
  12.         m_Data[1] = other.m_Data[1];  
  13.     }  
  14.   
  15.     Vector2(data_type x, data_type y) {  
  16.         m_Data[0] = x;  
  17.         m_Data[1] = y;  
  18.     }  
  19.   
  20.     double dot(const Vector2& other) const {  
  21.         return other.m_Data[0] * m_Data[0] + other.m_Data[1] * m_Data[1];  
  22.     }  
  23.   
  24.     double squaredLength() const {  
  25.         return sqrtf(m_Data[0]*m_Data[0] + m_Data[1]*m_Data[1]);  
  26.     }  
  27.   
  28.     Vector2& operator/(data_type factor) {  
  29.         m_Data[0] /= factor;  
  30.         m_Data[1] /= factor;  
  31.         return *this;  
  32.     }  
  33.   
  34.     Vector2& operator/=(data_type factor) {  
  35.         m_Data[0] /= factor;  
  36.         m_Data[1] /= factor;  
  37.         return *this;  
  38.     }  
  39.   
  40.     Vector2& operator*(data_type factor) {  
  41.         m_Data[0] *= factor;  
  42.         m_Data[1] *= factor;  
  43.         return *this;  
  44.     }  
  45.   
  46.     Vector2& operator*=(data_type factor) {  
  47.         m_Data[0] *= factor;  
  48.         m_Data[1] *= factor;  
  49.         return *this;  
  50.     }  
  51.   
  52.     Vector2& operator+=(const Vector2& other) {  
  53.         m_Data[0] += other.m_Data[0];  
  54.         m_Data[1] += other.m_Data[1];  
  55.         return *this;  
  56.     }  
  57.   
  58.     Vector2& operator=(const Vector2& other) {  
  59.         if (this==&other) {  
  60.             return *this;  
  61.         }  
  62.         m_Data[0] = other.m_Data[0];  
  63.         m_Data[1] = other.m_Data[1];  
  64.         return *this;  
  65.     }  
  66.       
  67.     operator const data_type* () const {  
  68.         return &(m_Data[0]);  
  69.     }  
  70.   
  71.     const data_type* data() const {  
  72.         return &(m_Data[0]);  
  73.     }  
  74.   
  75.     friend static Vector2 operator-(const Vector2& first, const Vector2& second) {  
  76.         data_type x = first.m_Data[0] - second.m_Data[0];  
  77.         data_type y = first.m_Data[1] - second.m_Data[1];  
  78.         return Vector2(x, y);  
  79.     }  
  80.   
  81.     friend static Vector2 operator+(const Vector2& first, const Vector2& second) {  
  82.         data_type x = first.m_Data[0] + second.m_Data[0];  
  83.         data_type y = first.m_Data[1] + second.m_Data[1];  
  84.         return Vector2(x, y);  
  85.     }  
  86.   
  87. private:  
  88.     data_type m_Data[2];  
  89.   
  90. };  
  91.   
  92. class OBB2D {  
  93. private:  
  94.     /** Corners of the box, where 0 is the lower left. */  
  95.     Vector2         corner[4];  
  96.   
  97.     /** Two edges of the box extended away from corner[0]. */  
  98.     Vector2         axis[2];  
  99.   
  100.     /** origin[a] = corner[0].dot(axis[a]); */  
  101.     double          minProjLength[2];       // 原点 0点在两个轴上的投影  
  102.     double          maxProjLength[2];       // 2点在两个轴上的投影  
  103.   
  104.     /** Returns true if other overlaps one dimension of this. */  
  105.     bool overlaps1Way(const OBB2D& other) const {  
  106.         for (int a = 0; a < 2; ++a) {  
  107.   
  108.             double t = other.corner[0].dot(axis[a]);  
  109.   
  110.             // Find the extent of box 2 on axis a  
  111.             double tMin = t;  
  112.             double tMax = t;  
  113.   
  114.             for (int c = 1; c < 4; ++c) {  
  115.                 t = other.corner[c].dot(axis[a]);  
  116.   
  117.                 if (t < tMin) {  
  118.                     tMin = t;  
  119.                 } else if (t > tMax) {  
  120.                     tMax = t;  
  121.                 }  
  122.             }  
  123.   
  124.             // We have to subtract off the origin  
  125.   
  126.             // See if [tMin, tMax] intersects [minProjLength, maxProjLength]  
  127.             if (tMin > maxProjLength[a] || tMax < minProjLength[a]) {  
  128.                 // There was no intersection along this dimension;  
  129.                 // the boxes cannot possibly overlap.  
  130.                 return false;  
  131.             }  
  132.         }  
  133.   
  134.         // There was no dimension along which there is no intersection.  
  135.         // Therefore the boxes overlap.  
  136.         return true;  
  137.     }  
  138.   
  139.   
  140.     /** Updates the axes after the corners move.  Assumes the 
  141.         corners actually form a rectangle. */  
  142.     void computeAxes() {  
  143.         axis[0] = corner[1] - corner[0];   
  144.         axis[1] = corner[3] - corner[0];   
  145.   
  146.         // Make the length of each axis 1/edge length so we know any  
  147.         // dot product must be less than 1 to fall within the edge.  
  148.   
  149.         for (int a = 0; a < 2; ++a) {  
  150.             axis[a] /= axis[a].squaredLength();  
  151.               
  152.             minProjLength[a] = corner[0].dot(axis[a]);  
  153.             maxProjLength[a] = corner[2].dot(axis[a]);  
  154.         }  
  155.     }  
  156.   
  157. public:  
  158.   
  159.     OBB2D(const Vector2& center, const double w, const double h, double angle)  
  160.     {  
  161.         Vector2 X( cos(angle), sin(angle));  
  162.         Vector2 Y(-sin(angle), cos(angle));  
  163.   
  164.         X *= w / 2;  
  165.         Y *= h / 2;  
  166.   
  167.         corner[0] = center - X - Y;  
  168.         corner[1] = center + X - Y;  
  169.         corner[2] = center + X + Y;  
  170.         corner[3] = center - X + Y;  
  171.   
  172.         computeAxes();  
  173.     }  
  174.   
  175.     void updateAngle(const Vector2& center, const double w, const double h, double angle) {  
  176.         Vector2 X( cos(angle), sin(angle));  
  177.         Vector2 Y(-sin(angle), cos(angle));  
  178.   
  179.         X *= w / 2;  
  180.         Y *= h / 2;  
  181.   
  182.         corner[0] = center - X - Y;  
  183.         corner[1] = center + X - Y;  
  184.         corner[2] = center + X + Y;  
  185.         corner[3] = center - X + Y;  
  186.   
  187.         computeAxes();  
  188.     }  
  189.   
  190.     /** For testing purposes. */  
  191.     void moveTo(const Vector2& center) {  
  192.         Vector2 centroid = (corner[0] + corner[1] + corner[2] + corner[3]) / 4;  
  193.   
  194.         Vector2 translation = center - centroid;  
  195.   
  196.         for (int c = 0; c < 4; ++c) {  
  197.             corner[c] += translation;  
  198.         }  
  199.   
  200.         computeAxes();  
  201.     }  
  202.   
  203.     /** Returns true if the intersection of the boxes is non-empty. */  
  204.     bool overlaps(const OBB2D& other) const {  
  205.         return overlaps1Way(other) && other.overlaps1Way(*this);  
  206.     }  
  207.   
  208.     void render() const {  
  209.         glBegin(GL_LINE_LOOP);  
  210.             for (int c = 0; c < 5; ++c) {  
  211.               glVertex2fv(corner[c & 3]);  
  212.             }  
  213.         glEnd();  
  214.     }  
  215. };  


相交时候用绿色绘制线框, 不相交时候用红色绘制线框。

 

      

glut的测试框架:

 

    1. #include <stdio.h>  
    2. #include <string.h>  
    3. #include <stdlib.h>  
    4. #include <assert.h>  
    5. #include <time.h>  
    6. #include <math.h>  
    7. #include <gl/glut.h>  
    8. #include <iostream>  
    9. using namespace std;  
    10.   
    11. #include "2dobb.h"  
    12.   
    13. const int g_window_size = 512;  
    14. int g_window_width = g_window_size;  
    15. int g_window_height = g_window_size;  
    16. const char* g_app_string = "OBB2DDemo";  
    17.   
    18. OBB2D*      g_rotateObbPtr;  
    19. OBB2D*      g_moveObbPtr;  
    20. float       g_obbAngle = 10;  
    21. Vector2     g_moveObbCenter(50, 50);  
    22.   
    23. GLenum checkForError(char *loc);  
    24.   
    25. void Init(void)   
    26. {     
    27.     g_rotateObbPtr = new OBB2D( Vector2(100, 100), 50,100, g_obbAngle );  
    28.     g_moveObbPtr = new OBB2D( g_moveObbCenter, 50,100, 0 );  
    29.     glEnable(GL_CULL_FACE);  
    30. }  
    31.   
    32. void Render(void)  
    33. {  
    34.     glClearColor(0.0f, 0.0f, 0.0f, 0.0f);  
    35.     glClearDepth(0.0f);  
    36.     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);  
    37.   
    38.     if (g_rotateObbPtr->overlaps(*g_moveObbPtr))   
    39.     {  
    40.         glColor3f(0, 1, 0);  
    41.     } else   
    42.     {  
    43.         glColor3f(1, 0, 0);  
    44.     }  
    45.     g_rotateObbPtr->render();  
    46.     g_moveObbPtr->render();  
    47.   
    48.     glutSwapBuffers();  
    49.     checkForError("swap");  
    50. }  
    51.   
    52. void Reshape(int width, int height)  
    53. {  
    54.     g_window_width = width; g_window_height = height;  
    55.     glMatrixMode(GL_PROJECTION);  
    56.     glLoadIdentity();  
    57.     glOrtho(0.0f, (GLfloat) g_window_width, 0.0f,   
    58.         (GLfloat) g_window_height, -1.0f, 1.0f);  
    59.     glMatrixMode(GL_MODELVIEW);  
    60.     glLoadIdentity();  
    61.     glViewport(0, 0, g_window_width, g_window_height);  
    62. }  
    63.   
    64. void keyboard(unsigned char key, int x, int y) {  
    65.     int state = -1;  
    66.   
    67.     switch (key)  
    68.     {  
    69.     case '1':  
    70.         g_obbAngle += 0.1;  
    71.         g_rotateObbPtr->updateAngle(Vector2(100, 100), 50, 100, g_obbAngle);  
    72.         break;  
    73.     case 'w':   // move up  
    74.         state = 0;  
    75.         break;  
    76.     case 's':  
    77.         state = 1;  
    78.         break;  
    79.     case 'a':  
    80.         state = 3;  
    81.         break;  
    82.     case 'd':  
    83.         state = 2;  
    84.         break;  
    85.     case 'q':  
    86.         delete g_rotateObbPtr;  
    87.         delete g_moveObbPtr;  
    88.         exit(0);  
    89.     }  
    90.   
    91.     if (state >= 0) {  
    92.         Vector2::data_type dx = 10 * (state < 2 ? 0 : (state==3 ? -1 : 1) );  
    93.         Vector2::data_type dy = 10 * (state < 2 ? (state==0 ? 1 : -1) : 0 );  
    94.         g_moveObbCenter += Vector2(dx, dy);  
    95.         g_moveObbPtr->moveTo(g_moveObbCenter);  
    96.     }  
    97.   
    98.     glutPostRedisplay();  
    99. }  
    100.   
    101. int main(int argc, char *argv[])  
    102. {  
    103.     glutInit(&argc, argv);  
    104.     glutInitDisplayMode(GLUT_RGB|GLUT_DOUBLE|GLUT_DEPTH);  
    105.     glutInitWindowSize(g_window_width, g_window_height);  
    106.     glutCreateWindow(g_app_string);  
    107.   
    108.     // set up world space to screen mapping  
    109.     glMatrixMode(GL_PROJECTION);  
    110.     glLoadIdentity();  
    111.     glOrtho(0.0f, (GLfloat)g_window_size, 0.0f, (GLfloat)g_window_size, -1.0f, 1.0f);  
    112.     glMatrixMode(GL_MODELVIEW);  
    113.     glLoadIdentity();  
    114.     glViewport(0, 0, g_window_size, g_window_size);  
    115.   
    116.     glutDisplayFunc(Render);  
    117.     glutReshapeFunc(Reshape);  
    118.     glutKeyboardFunc(keyboard);  
    119.   
    120.     Init();  
    121.   
    122.     glutMainLoop();  
    123.   
    124.     return 0;  
    125. }  
    126.   
    127. GLenum checkForError(char *loc)  
    128. {  
    129.     GLenum errCode;  
    130.     const GLubyte *errString;  
    131.   
    132.     if ((errCode = glGetError()) != GL_NO_ERROR)  
    133.     {  
    134.         errString = gluErrorString(errCode);  
    135.         printf("OpenGL error: %s",errString);  
    136.   
    137.         if (loc != NULL)  
    138.             printf("(%s)",loc);  
    139.   
    140.         printf("\n");  
    141.     }  
    142.   
    143.     return errCode;  
    144. }  
posted @ 2015-02-21 13:17  Unikanade  阅读(314)  评论(0编辑  收藏  举报