opengl中的有界框碰撞检测
Posted
技术标签:
【中文标题】opengl中的有界框碰撞检测【英文标题】:bounded box collision detection in opengl 【发布时间】:2012-11-25 15:37:19 【问题描述】:这是程序的叠加层。我在 Visual Studio 10 中工作,使用 opengl/glut 的 c++。我正在尝试创建一个迷宫(我从输入文件生成)。然后我使用 obj loader(glm) 从 3dstudio max 导入两个模型。其中一个模型(mo)将在迷宫中移动。所以我在他周围和迷宫的墙壁周围添加了一个边界框。我还在 mo 周围画了一个盒子,它似乎随着角色移动/旋转。但是由于某种原因(也许我没有在正确的地方做)碰撞测试从来没有检测到任何东西。我会让代码来说话,如果您有任何问题,我很乐意为您解答。立即准备代码。
//bounding boxes
struct BoundingBox
Vector3D max;
Vector3D min;
;
BoundingBox *box_mo;
BoundingBox *static_box[400];
void initbox(BoundingBox *b)
b->min.x=100000;
b->min.y=100000;
b->min.z=100000;
b->max.x=-100000;
b->max.y=-100000;
b->max.z=-100000;
BoundingBox *CreateCollisionBox(GLMmodel *model,GLMgroup *object)
/* GLM doesn't store each vertex together with the object that owns it. It doesn't have that notion. In GLM object don't have vertex, they have triangles. And each triangle is actually an index in the triangle list of the object.*/
BoundingBox *boxx=(BoundingBox*)malloc(sizeof(BoundingBox));
initbox(boxx);
for(int i=0;i<object->numtriangles;i++)
// for each vertex of the triangle pmodel1->triangles[object->triangles[i]]
// calculate min and max
for(int j=0;j<3;j++)
GLuint index=model->triangles[object->triangles[i]].vindices[j];
GLfloat x = model->vertices[index*3 +0];
GLfloat y = model->vertices[index*3 +1];
GLfloat z = model->vertices[index*3 +2];
if(boxx->min.x>x) boxx->min.x =x;
if(boxx->min.y>y) boxx->min.y =y;
if(boxx->min.z>z) boxx->min.z =z;
if(boxx->max.x<x) boxx->max.x =x;
if(boxx->max.y<y) boxx->max.y =y;
if(boxx->max.z<z) boxx->max.z =z;
return boxx;
void AddCollisionBox(GLMmodel *model,GLMgroup *object)
//box[boxindex]=CreateCollisionBox(model,object);
box_mo=CreateCollisionBox(model,object);
//boxindex++;
// A GLMmodel has a chained list of groups, each group representing an object.
// Each object has a name (the name you gave it in 3D Studio Max or Gmax).
// Let's you have 10 walls in your scene a few other objects as well and you want to
// create collision boxes just for the walls and you do not want to make a collision box
// for one of your objects. You could name all your walls
// like this: Wall1, Wall2, ..., Wall10. If you wanted to add collision boxes just to them
// you could go through all objects in the scene and if their name contains "Wall" add them.
// with this one: strstr
// Basicly this function does just that: if you want to add boxes for the walls you would call it like this: DefineCollisionBoxes(model,"Wall");
void DefineCollisionBoxes(GLMmodel *model,char *name)
GLMgroup *group = model->groups;
while(group)
if(strstr(group->name,name))
AddCollisionBox(model,group);
group=group->next;
bool Collision(BoundingBox *b,GLfloat x,GLfloat y,GLfloat z)
return x <= b->max.x && x>= b->min.x && y<= b->max.y && y>= b->min.y && z<= b->max.z && z >= b->min.z;
bool CollisionTest(BoundingBox *a,BoundingBox *b)
/*bool collision=false;
for(int i=0;i<static_boxes;i++)
for(float x=static_box[i]->min.x, y=static_box[i]->min.y,z=static_box[i]->min.z ;x<=static_box[i]->max.x && y<=static_box[i]->max.y && z<=static_box[i]->max.z;x+=0.1,y+=0.1,z+=0.1)
if(Collision(a,x,y,z) == true)
collision=true;
return collision;*/
if(a->min.x <= b->max.x && a->max.x >= b->min.x && a->min.z <= b->max.z && a->max.z >= b->min.z && a->min.y <= b->max.y && a->max.y >= b->min.y)
return true;
return false;
void drawBox(BoundingBox *b)
glColor3f(1,1,1);
glBegin(GL_LINE_LOOP);
glVertex3f(b->max.x,b->max.y,b->min.z);
glVertex3f(b->min.x,b->max.y,b->min.z);
glVertex3f(b->min.x,b->min.y,b->min.z);
glVertex3f(b->max.x,b->min.y,b->min.z);
glEnd();
glBegin(GL_LINE_LOOP);
glVertex3f(b->max.x,b->min.y,b->max.z);
glVertex3f(b->max.x,b->max.y,b->max.z);
glVertex3f(b->min.x,b->max.y,b->max.z);
glVertex3f(b->min.x,b->min.y,b->max.z);
glEnd();
glBegin(GL_LINE_LOOP);
glVertex3f(b->max.x,b->max.y,b->min.z);
glVertex3f(b->max.x,b->max.y,b->max.z);
glVertex3f(b->min.x,b->max.y,b->max.z);
glVertex3f(b->min.x,b->max.y,b->min.z);
glEnd();
glBegin(GL_LINE_LOOP);
glVertex3f(b->max.x,b->min.y,b->max.z);
glVertex3f(b->min.x,b->min.y,b->max.z);
glVertex3f(b->min.x,b->min.y,b->min.z);
glVertex3f(b->max.x,b->min.y,b->min.z);
glEnd();
//display function
void display()
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//setup view
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
camera.render
//read from file, create walls and characters
//place model mo in front of the third person camera always
Vector3D positionn;
positionn = camera.position + camera.forward*5;
glPushMatrix();
glColor3f(0.88f,0.75f,0.49f);
//here i move my character and calculate bounding box at each frame for him
glTranslatef(positionn.x,-0.42f,positionn.z);
glScalef(0.7f,0.7f,0.7f);
glRotatef(angle,0,1,0);
drawMo();
DefineCollisionBoxes(pmodel1,"body"); // actual function call
drawBox(box_mo); //draw bounding box
glPopMatrix();
//test if the character collides with any of the walls - not working
for(int i=0;i<static_boxes;i++)
if(CollisionTest(box_mo,static_box[i]) == true)
printf("collision");
//swap buffers
glutSwapBuffers();
【问题讨论】:
I will let the code do the talking
Stack Overflow 不是这样工作的,您遇到的实际问题是什么?
好吧,我已经说过,问题是 CollisionTest 永远不会返回 true,这意味着检测不起作用。我为每个对象绘制了方框,它们似乎都工作得很好,所以我真的不知道出了什么问题。如果我画了一个碰撞框并且它随着角色旋转和移动,这是否意味着结构(它的原理)工作正常?那么为什么碰撞测试不起作用呢?
您能否缩短您的代码,这样我们就不必费力费力地弄清楚哪些部分是重要的?
好的,我重新编辑了我的帖子,如果您想查看它们,我保留了创建边界框的功能。主要关注的是显示功能,我尝试查看对象是否碰撞,即使我飞过所有墙壁,printf 消息也不会出现
看起来你正在测试一个盒子是否在一个盒子里面(你只有 && 没有 ||)所以在我看来,除非整个盒子都被包含,否则你总是会得到错误
【参考方案1】:
我将尝试概述如何解决碰撞问题。为简单起见,我将做出以下假设:
-
我们有一个迷宫所在的坐标系。
相机和模型都转换到这个坐标系中。
为使墙壁到达该坐标系而对墙壁执行的唯一转换是纯平移,无需任何旋转或缩放。
字符将被平移、旋转和缩放。
您不想检查是否会发生碰撞,而是要检查是否发生了碰撞。
您拥有或将要编写或获取向量和矩阵库
如果我们使用这些假设,我们可以使用 3d 中的矩形来表示每面墙。而且由于我们假设迷宫既不旋转也不缩放,我们可以只做一次,然后将其存储在数组或 B-tree 或其他任何东西中。 (请注意,选择正确的数据结构来存储您的世界进行计算可以大大提高性能)
角色在迷宫中移动,我们假设它可以旋转和缩放。确定角色是否与任何物体发生碰撞的最简单方法是计算对象的边界球。一旦你有了碰撞检测,你可以只存储两点。球体的中心点和球体上的任意点。
一旦角色被带到迷宫中的正确位置,您就可以对提到的两个点应用完全相同的变换。您可以使用矩阵库执行此操作,也可以在转换字符后查询 MODELVIEW 矩阵。您可以像这样调用“glGetFloatv”来做到这一点:
GLfloat model[16];
glGetFloatv(GL_MODELVIEW_MATRIX, model);
一旦你变换了这两个点,你就可以找到它们之间的距离 - 这将是一个物体与变换后的中心点之间的距离,以避免碰撞。
现在要做的最后一件事是遍历相关平面并使用转换后的中心点和计算的距离检查点到平面的距离。然后,您需要确保投影到平面的中心在矩形内。
这是程序的概要(这里没有真正的代码,只有 cmets 和函数抽象)
void drawCharacter(Matrix4f const & charMatrix);
void drawMaze(Matrix4f const & mazeMatrix);
//Will return MAX_FLT to indicate out of rectangle condition
float distanceToRectangle(Vector3f const & point, Rectangle3df const & plane);
vector<Rectangle3df> wallPlanes;
Vector3f centerOfCharSphere;
Vector3f pointOnCharSphere;
//Initialization
/*
1. Go Over maze create planes for all walls
2. Calculate character sphere and extract two points: The center of the sphere and a point on the sphere
*/
//Render
/*
Calculate the charMatrix - The matrix that takes the character from object CRS to maze CRS
Calculate the translation matrix for the maze (mazeMatrix) - the matrix that translates the maze into place
*/
Vector3f transformedSphereCenter = charMatrix.transform(centerOfCharSphere);
Vector3f transformedPointOnSphere = charMatrix.transform(pointOnCharSphere);
Vector3f R = transformedPointOnSphere - transformedSphereCenter;
float safeDistance = R.length();
for(vector<Plane3f>::const_iterator it = wallPlanes.begin(); it != wallPlanes.end(); it++)
if(distanceToRectangle(transformedSphereCenter, *it) <= safeDistance)
printf("collided\n");
/*
Setup projection matrix
Setup the modelview matrix for the camera
*/
drawMaze(mazeMatrix);
drawCharacter(charMatrix);
我希望这能让你走上正轨
【讨论】:
谢谢塞巴斯蒂安,非常有帮助。所有假设都是正确的,我只需要一个矩阵库(或使用你所说的模型视图)。我会试试这个,因为目前我只有一个球体碰撞测试,这对我现在需要的很好,但我需要稍后加强它,所以谢谢你的回答。我会再保留这个问题几天,如果没有,我会选择你的答案。以上是关于opengl中的有界框碰撞检测的主要内容,如果未能解决你的问题,请参考以下文章