Bullet物理引擎在OpenGL中的应用

Bullet物理引擎在OpenGL中的应用

在开发OpenGL的应用之时, 难免要遇到使用物理来模拟OpenGL中的场景内容. 由于OpenGL仅仅是一个关于图形的开发接口, 因此需要通过第三方库来实现场景的物理模拟. 目前我选择 Bullet 物理引擎, 其官方网站为 Bullet, 开发库的下载地址则在 github 上.


1. OpenGL 环境

首先我们需要搭建框架, OpenGL 的基本框架这里不详述, 我个人是在几何着色器内实现光照, 这是由于我实现的是面法线. 另外用到的其他三方库有 GLFW 和 GLM库, 前者有助于管理OpenGL窗口, 后者省却了自己写数学公式代码的过程. 另外实现了立方体模型和球体的创建, 满足学习 Bullet 的需要即可.

Bullet物理引擎在OpenGL中的应用


2. 物理环境的初始化

对于 Bullet 物理库而言, 它的搭建也很简单, 在初始化 OpenGL 的上下文的时候, 也可以初始化我们的物理环境, 参考物理库自带的 HelloWorld 即可, 相关代码为:

    ///collision configuration contains default setup for memory, collision setup
m_collisionConfiguration = new btDefaultCollisionConfiguration();
//m_collisionConfiguration->setConvexConvexMultipointIterations(); ///use the default collision dispatcher. For parallel processing you can use a diffent dispatcher (see Extras/BulletMultiThreaded)
m_dispatcher = new btCollisionDispatcher(m_collisionConfiguration); m_broadphase = new btDbvtBroadphase(); ///the default constraint solver. For parallel processing you can use a different solver (see Extras/BulletMultiThreaded)
btSequentialImpulseConstraintSolver* sol = new btSequentialImpulseConstraintSolver;
m_solver = sol; m_dynamicsWorld = new btDiscreteDynamicsWorld(m_dispatcher, m_broadphase, m_solver, m_collisionConfiguration); m_dynamicsWorld->setGravity(btVector3(0, -10, 0));

3. 物理环境的销毁

相应的, 还有清理代码

    std::vector<ModelInfo>::iterator it = m_models.begin();
while(it != m_models.end())
{
ModelInfo info = *it;
delete info.model;
m_dynamicsWorld->removeRigidBody(info.obj);
it++;
}
m_models.clear(); for (int j=0;j<m_collisionShapes.size();j++)
{
btCollisionShape* shape = m_collisionShapes[j];
m_collisionShapes[j] = 0;
delete shape;
}
//delete dynamics world
delete m_dynamicsWorld; //delete solver
delete m_solver; delete m_broadphase; delete m_dispatcher; delete m_collisionConfiguration; //next line is optional: it will be cleared by the destructor when the array goes out of scope
m_collisionShapes.clear();

在我的实现内, 结构体 ModelInfo 是一个自定义的结构体(struct), 我通过该结构体的容器保存了所有物体的物理模型以及其对应OpenGL模型的关系, 这样在物理库更新一个物体的位置和方向时, 我们就可以在 OpenGL 内更新物体的位置和方向.

4. 物理世界的渲染

下面是我渲染物理世界中所有模型的代码, 计算出所有模型的变换矩阵, 而后通知其相关代码进行渲染.

void PhysicsBaseWorld::render()
{
std::vector<ModelInfo>::iterator it = m_models.begin();
while(it != m_models.end())
{
ModelInfo info = *it;
btRigidBody* obj = info.obj;
btRigidBody* body = btRigidBody::upcast(obj); btTransform trans;
if (body && body->getMotionState())
{
body->getMotionState()->getWorldTransform(trans); } else
{
trans = obj->getWorldTransform();
}
glm::vec3 position = glm::vec3(float(trans.getOrigin().getX()),float(trans.getOrigin().getY()),float(trans.getOrigin().getZ()));
btQuaternion rot = trans.getRotation();
glm::quat q = glm::quat(rot.getW(), rot.getX(), rot.getY(), rot.getZ());
glm::mat4 rot4 = glm::toMat4(q);
glm::mat4 m = glm::translate(glm::mat4(1.0), position) * rot4; Model* model = info.model;
model->setModelMat(m);
model->render();
it++;
}
}

5. 绘制静态的物体

在创建物理世界的过程中, 物理库中主要使用函数 createRigidBody() 来创建刚体模型, 其主要有三个参数, 分别表示质量, 变换, 形状. 其中质量为 0 的物体为静止物体, 可以用来创建地面或者路边的石头之类的物体模型. 创建静止立方体模型的代码

    ///create a few basic rigid bodies
btCollisionShape* shape = new btBoxShape(btVector3(halfsize[0],halfsize[1],halfsize[2])); m_collisionShapes.push_back(shape); btTransform transform;
transform.setIdentity();
transform.setOrigin(btVector3(pos[0], pos[1], pos[2])); {
btScalar mass(0.);
btRigidBody* body = createRigidBody(mass,transform,shape); Cube* cube = new Cube(halfsize[0],halfsize[1],halfsize[2]);
cube->setColor(col);
ModelInfo info = {body, cube};
m_models.push_back(info);
}

上面的代码创建的过程中, 我同时创建了一个对应的 OpenGL 立方体.

6. 绘制可活动的物体

创建可活动模型时需要设置相关的运动状态信息

    btAssert((!shape || shape->getShapeType() != INVALID_SHAPE_PROXYTYPE));

    //rigidbody is dynamic if and only if mass is non zero, otherwise static
bool isDynamic = (mass != 0.f); btVector3 localInertia(0, 0, 0);
if (isDynamic)
shape->calculateLocalInertia(mass, localInertia); btDefaultMotionState* myMotionState = new btDefaultMotionState(startTransform); btRigidBody::btRigidBodyConstructionInfo cInfo(mass, myMotionState, shape, localInertia); btRigidBody* body = new btRigidBody(cInfo); body->setUserIndex(-1);
m_dynamicsWorld->addRigidBody(body); btBoxShape* s = dynamic_cast<btBoxShape*>(shape);
if(s != 0)
{
btVector3 size = s->getHalfExtentsWithMargin();
Cube* cube = new Cube(size.getX(), size.getY(), size.getZ());
cube->setColor(col);
ModelInfo info = {body, cube};
m_models.push_back(info);
} return body;

7. 更新物理世界

最后我们需要时刻更新物理世界中的模型位置和方位

m_dynamicsWorld->stepSimulation(elpasedTime, 0);

Bullet物理引擎在OpenGL中的应用

上一篇:LightOJ 1259 Goldbach`s Conjecture (哥德巴赫猜想 + 素数筛选法)


下一篇:查找 管道 exec