• Bullet物理引擎在OpenGL中的应用


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

    演示模型

  • 相关阅读:
    Linux常用命令大全(非常全!!!)
    TCP连接的建立与释放(三次握手与四次挥手)
    TCP/IP Http 和Https socket之间的区别
    redis持久化方法对比分析
    关于HTTP协议,一篇就够了
    远程桌面不能拷贝文件的问题
    URLDecoder: Incomplete trailing escape (%) pattern
    利用pdf2swf将PDF转换成SWF
    Oracle删除当前用户下所有的表的方法
    JS简单验证密码强度
  • 原文地址:https://www.cnblogs.com/summericeyl/p/5267791.html
Copyright © 2020-2023  润新知