Bullet物理引擎在OpenGL中的应用

来源:互联网 发布:vm安装linux虚拟机 编辑:程序博客网 时间:2024/05/16 05:46

Bullet物理引擎在OpenGL中的应用

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


1. OpenGL 环境

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

立方体模型


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);

演示模型

0 0
原创粉丝点击