基于Bullet引擎和OpenGL ES绘制碰撞物体

简介

各大手机厂商在自己的OS中都逐步合入可交互的桌面游戏或动效。比如小米miui14中的桌面花宠摆件、动态桌面图标;vivo的OriginOS3中的动态壁纸和基于物理引擎算法的情景天气;OPPO的ColorOS13中的游戏追光效果、动态壁纸和应用使用主题动效等。因此在平板端,借助Bullet引擎和OpenGL ES,可以尝试实现类似的动态壁纸、动效交互等等。

Bullet引擎

Bullet是开源的物理模拟计算引擎,能够检测、计算和模拟物理世界物体碰撞,常用于游戏和影视制作。我们所熟知的电影《大侦探福尔摩斯》和《侠盗猎车手4、5》、《荒野大嫖客》等游戏都是基于Bullet物理游戏引擎。

OpenGL ES

OpenGL ES(OpenGL for Embedded Systems)是OpenGL三维图形API的子集。相较于OpenGL,OpenGL ES删除了一些较少使用、不够高性能的接口,所以存在OpenGL可以使用但OpenGL ES不可使用的接口。

OpenGL ES与硬件平台无关,因此在移动端NDK开发能够做到多平台的兼容。

Bullet3基础使用

创建世界

首先需要初始化相关配置,并获取到指向btDiscreteDynamicsWorld的指针dynamicsWorld,它是我们创建用来表示三维碰撞的世界。

    ///-----initialization_start-----

    //collision configuration contains default setup for memory, collision setup. Advanced users can create their own configuration.
    btDefaultCollisionConfiguration* collisionConfiguration = new btDefaultCollisionConfiguration();

    ///use the default collision dispatcher. For parallel processing you can use a diffent dispatcher (see Extras/BulletMultiThreaded)
    btCollisionDispatcher* dispatcher = new btCollisionDispatcher(collisionConfiguration);

    ///btDbvtBroadphase is a good general purpose broadphase. You can also try out btAxis3Sweep.
    btBroadphaseInterface* overlappingPairCache = new btDbvtBroadphase();

    ///the default constraint solver. For parallel processing you can use a different solver (see Extras/BulletMultiThreaded)
    btSequentialImpulseConstraintSolver* solver = new btSequentialImpulseConstraintSolver;

    dynamicsWorld = new btDiscreteDynamicsWorld(dispatcher, overlappingPairCache, solver, collisionConfiguration);

    dynamicsWorld->setGravity(btVector3(0, -10, 0));

    ///-----initialization_end-----

世界的组成一定不是一个简单的三维空间,还需要各种各样的物体构成。在上述Bullet初始化即一个三维世界创建后,可以通过主动的添加刚体来构成这个世界中的各个物体。刚体是指在运动中和受到力的作用后,形状和大小不变,内部各点的相对位置不变的物体。现实中不存在运动或受到力后还保持形体大小等不变的物体,所以刚体可以理解为一种理想化的物体。

创建刚体

在物理世界中,首先创建一个静态刚体来表示“地面”。刚体的创建和合入世界需要完成四个步骤:

  • 创建物体形状(btCollisionShape)
  • 初始化物体位置和旋转状态(btTransform)
  • 将物体封装成Body(btRigidBody/btSoftBody)
  • 将对象加入到世界中
    btCollisionShape* groundShape = new btBoxShape(btVector3(btScalar(200.), btScalar(200.), btScalar(50.)));

    collisionShapes.push_back(groundShape);

    btTransform groundTransform;
    groundTransform.setIdentity();
    groundTransform.setOrigin(btVector3(0, 200, 0));

    btScalar mass(0.);

    //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)
        groundShape->calculateLocalInertia(mass, localInertia);

    //using motionstate is optional, it provides interpolation capabilities, and only synchronizes 'active' objects
    btDefaultMotionState* myMotionState = new btDefaultMotionState(groundTransform);
    btRigidBody::btRigidBodyConstructionInfo rbInfo(mass, myMotionState, groundShape, localInertia);
    btRigidBody* body = new btRigidBody(rbInfo);

    body->setRestitution(btScalar(1.2));
    //add the body to the dynamics world
    dynamicsWorld->addRigidBody(body);

Bullet中有许多形状,包括btBoxShape、btSphereShape、btCapsuleShape、btStaticPlaneShape、TaperedCapsule、btConvexHullShape等,他们分别代表四面体、球体、胶囊体等。通过对Body对象的操作可以但不局限于摩檫力设置void btCollisionObject::setFriction(btScalar frict)、碰撞反弹系数设置void btCollisionObject::setRestitution(btScalar rest)。

上述过程创建的是静态刚体,同样的步骤通过加入动态刚体对象来模拟相关物理碰撞现象。静态刚体的质量为0,且不受重力影响。

获取刚体实时位置

通过遍历世界dynamicsWorl中的collisionObjects可以获取相应的刚体的实时位置。

    5dynamicsWorld->stepSimulation(1.f / 30.f, 10);

    //print positions of all objects
    for (int j = dynamicsWorld->getNumCollisionObjects() - 1; j >= 0; j--) {
        btCollisionObject *obj = dynamicsWorld->getCollisionObjectArray()[j];
        btRigidBody *body = btRigidBody::upcast(obj);
        btTransform trans;
        if (body && body->getMotionState()) {
            body->getMotionState()->getWorldTransform(trans);

            btCollisionShape *shape = body->getCollisionShape();
            glm::mat4 model = glm::mat4(1.0f);
            trans.getOpenGLMatrix(glm::value_ptr(model));
            btBoxShape *tempShape = static_cast<btBoxShape * >(shape);
            btVector3 tempVector = tempShape->getImplicitShapeDimensions();

            void *attachedNode = body->getUserPointer();
            if (attachedNode) {//update cubes only

                int index = body->getUserIndex();

                //z轴坐标取负数可以和屏幕对应
                //x1,y1,z1,x2,y2,z2
                float collsionPos[6] = {trans.getOrigin().getX() - tempVector.getX(),
                                        trans.getOrigin().getY() - tempVector.getY(),
                                        -trans.getOrigin().getZ() + tempVector.getZ(),
                                        trans.getOrigin().getX() + tempVector.getX(),
                                        trans.getOrigin().getY() + tempVector.getY(),
                                        -trans.getOrigin().getZ() - tempVector.getZ()};

            }
        } else {
            trans = obj->getWorldTransform();
        }
    }

OpenGL ES渲染结合

将刚体坐标根据对应的物体转换为顶点坐标进行图形渲染,这样就可以看到具有碰撞效果的图形在界面上移动。

    if (m_ProgramObj == 0)
        return;

    int size = map.size();

    GLfloat vVertices[6*6*3*(map.size())];
    drawBox(vVertices,map);

    // Set the viewport

    glViewport(0,0,m_ScreenW,m_ScreenH);
    UpdateMVPMatrix(m_MVPMatrix, 0, 0, m_ScreenW / m_ScreenH);
    glUniformMatrix4fv(m_MVPMatrixLoc, 1, GL_FALSE, &m_MVPMatrix[0][0]);

    //Clear the color buffer
    glClear(GL_COLOR_BUFFER_BIT);

    //Use the program object
    glUseProgram(m_ProgramObj);
    //Load the vertex data
    glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,0,vVertices);
    glEnableVertexAttribArray(0);

    glDrawArrays(GL_TRIANGLES,0, 6*6*map.size());

在此提供正方体顶点计算相关算法

    float leftX = collsionPos[0] /(float)m_ScreenW;
    float leftY = collsionPos[1] / (float)m_ScreenH;
    float leftZ = collsionPos[2] /(float)m_ScreenH;
    float rightX = collsionPos[3] /(float)m_ScreenW;
    float rightY = collsionPos[4] /(float)m_ScreenH;
    float rightZ = collsionPos[5] /(float)m_ScreenH;

    float vVerticesTemp[108] = {
            //正面
            leftX,leftY,leftZ,
            rightX,leftY,leftZ,
            rightX,rightY,leftZ,
            rightX,rightY,leftZ,
            leftX,rightY,leftZ,
            leftX,leftY,leftZ,

            //背面
            leftX,leftY,rightZ,
            rightX,leftY,rightZ,
            rightX,rightY,rightZ,
            rightX,rightY,rightZ,
            leftX,rightY,rightZ,
            leftX,leftY,rightZ,

            //左面
            leftX,rightY,rightZ,
            leftX,rightY,leftZ,
            leftX,leftY,leftZ,
            leftX,leftY,leftZ,
            leftX,leftY,rightZ,
            leftX,rightY,rightZ,

            //右面
            rightX,rightY,rightZ,
            rightX,rightY,leftZ,
            rightX,leftY,leftZ,
            rightX,leftY,leftZ,
            rightX,leftY,rightZ,
            rightX,rightY,rightZ,

            //下面
            leftX,leftY,leftZ,
            rightX,leftY,leftZ,
            rightX,leftY,rightZ,
            rightX,leftY,rightZ,
            leftX,leftY,rightZ,
            leftX,leftY,leftZ,

            //上面
            leftX,rightY,leftZ,
            rightX,rightY,leftZ,
            rightX,rightY,rightZ,
            rightX,rightY,rightZ,
            leftX,rightY,rightZ,
            leftX,rightY,leftZ

    };

    for(float i : vVerticesTemp){
        vVertices[pos++] = i;
    }

总结

在后续的实现过程中,不仅仅局限于桌面交互、动效图标,还能通过构建整机系统的物理服务来管理移动端整机的注册窗口动效、应用内的窗口动效等。

参考文献

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,406评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,732评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,711评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,380评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,432评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,301评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,145评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,008评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,443评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,649评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,795评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,501评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,119评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,731评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,865评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,899评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,724评论 2 354

推荐阅读更多精彩内容