【OSG学习笔记】Day 18: 碰撞检测与物理交互

物理引擎(Physics Engine)

物理引擎 是一种通过计算机模拟物理规律(如力学、碰撞、重力、流体动力学等)的软件工具或库。

它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互,广泛应用于 游戏开发、动画制作、虚拟现实(VR)、机器人仿真 等领域。

物理引擎的核心功能
  1. 碰撞检测:判断物体是否发生碰撞或接触。
  2. 物理模拟:根据物理定律(如牛顿力学)计算物体的运动轨迹、速度、加速度等。
  3. 交互反馈:处理物体碰撞后的力学反应(如反弹、摩擦、破碎)。
  4. 约束系统:模拟物体间的连接关系(如铰链、弹簧、绳索等)。
常见物理引擎
  • Bullet:开源、跨平台,适合刚体和软体模拟,常用于游戏和仿真(OSG开发)。
  • PhysX:NVIDIA开发的商业引擎,广泛用于3A游戏(如《绝地求生》)。
  • Havok:商业引擎,支持复杂物理效果,应用于影视和游戏。
  • Box2D:2D物理引擎,适合2D游戏(如《愤怒的小鸟》)。
物理引擎的工作流程
  1. 更新物理世界:根据时间步长计算物体运动。
  2. 碰撞检测:识别所有碰撞对。
  3. 物理响应:计算碰撞后的速度、力和动量变化。
  4. 同步渲染:将物理模拟结果反映到图形界面。

刚体(Rigid Body)

刚体 是物理学中的一个理想模型,指在运动和受力过程中 形状和大小完全不变 的物体。换句话说,刚体的任意两点间的距离始终保持不变,忽略形变(如压缩、弯曲、旋转时的体积变化)。

刚体的特点
  • 无弹性形变:碰撞时能量守恒(完全弹性碰撞)或部分损失(非弹性碰撞),但形状不变。
  • 自由度 :刚体在三维空间中有 6个自由度(3个平移自由度 + 3个旋转自由度)。
  • 应用场景 :适用于模拟 硬物体 的运动,如石块、车辆、机械零件等。
与软体的区别
  • 软体:允许形变(如布料、液体、果冻),模拟更复杂(需考虑弹性、粘性等)。
  • 刚体:简化模型,计算效率高,适合大规模物理模拟。
刚体在物理引擎中的应用
  • 通过 质量、惯性矩、摩擦力、 restitution(恢复系数) 等参数定义物理属性。
  • 结合碰撞形状(如球体、立方体、胶囊体)进行碰撞检测,减少计算量。

物理引擎与刚体的关系

物理引擎通过算法模拟刚体的运动和交互,例如:

  • 当两个刚体碰撞时,物理引擎根据 动量守恒定律 计算碰撞后的速度和方向。
  • 利用 约束(Constraint) 限制刚体的自由度(如用铰链连接两个刚体,使其只能绕轴旋转)。

在您的学习场景中(OSG + Bullet),Bullet作为物理引擎,负责处理刚体的碰撞检测和物理模拟,而OSG负责图形渲染,两者结合实现逼真的物理交互效果(如物体掉落、碰撞反弹等)。

实战

cpp 复制代码
#include <osgViewer/Viewer>
#include <osg/Geode>
#include <osg/ShapeDrawable>
#include <osg/MatrixTransform>
#include <osgGA/TrackballManipulator>
#include <osg/PositionAttitudeTransform>

// Bullet物理引擎头文件
#include <btBulletDynamicsCommon.h>

// OSG与Bullet转换工具
osg::Matrix bulletToOsg(const btTransform& transform) {
    osg::Matrix matrix;
    btScalar elements[16];
    transform.getOpenGLMatrix(elements);
    matrix.set(elements);
    return matrix;
}

class PhysicsUpdateCallback : public osg::NodeCallback {
public:
    PhysicsUpdateCallback(btDynamicsWorld* world, 
                          std::map<btRigidBody*, osg::ref_ptr<osg::MatrixTransform>>& bodyNodeMap)
        : _world(world), _bodyNodeMap(bodyNodeMap) {}

    virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) {
        // 更新物理世界
        _world->stepSimulation(1.0f/60.0f, 10);
        
        // 更新OSG场景图中的物体位置
        for (auto& pair : _bodyNodeMap) {
            btRigidBody* body = pair.first;
            osg::MatrixTransform* transformNode = pair.second.get();
            
            btTransform trans;
            body->getMotionState()->getWorldTransform(trans);
            transformNode->setMatrix(bulletToOsg(trans));
        }
        
        traverse(node, nv);
    }

private:
    btDynamicsWorld* _world;
    std::map<btRigidBody*, osg::ref_ptr<osg::MatrixTransform>>& _bodyNodeMap;
};

int main() {
    // 初始化OSG查看器
    osgViewer::Viewer viewer;
    viewer.setCameraManipulator(new osgGA::TrackballManipulator);
    
    // 创建根节点
    osg::ref_ptr<osg::Group> root = new osg::Group;
    
    // ===== Bullet物理引擎初始化 =====
    // 创建碰撞检测配置和调度器
    btDefaultCollisionConfiguration* collisionConfiguration = new btDefaultCollisionConfiguration();
    btCollisionDispatcher* dispatcher = new btCollisionDispatcher(collisionConfiguration);
    
    // 创建叠代约束求解器
    btSequentialImpulseConstraintSolver* solver = new btSequentialImpulseConstraintSolver;
    
    // 创建宽广阶段碰撞检测算法
    btDbvtBroadphase* overlappingPairCache = new btDbvtBroadphase();
    
    // 创建动力学世界
    btDiscreteDynamicsWorld* dynamicsWorld = new btDiscreteDynamicsWorld(
        dispatcher, overlappingPairCache, solver, collisionConfiguration);
    
    // 设置重力
    dynamicsWorld->setGravity(btVector3(0, 0, -9.81));
    
    // ===== 创建物理对象和OSG场景 =====
    std::map<btRigidBody*, osg::ref_ptr<osg::MatrixTransform>> bodyNodeMap;
    
    // 1. 创建地面
    // OSG部分
    osg::ref_ptr<osg::Box> groundShape = new osg::Box(osg::Vec3(0, 0, -1), 20, 20, 1);
    osg::ref_ptr<osg::ShapeDrawable> groundDrawable = new osg::ShapeDrawable(groundShape);
    osg::ref_ptr<osg::Geode> groundGeode = new osg::Geode;
    groundGeode->addDrawable(groundDrawable);
    
    osg::ref_ptr<osg::MatrixTransform> groundTransform = new osg::MatrixTransform;
    groundTransform->addChild(groundGeode);
    root->addChild(groundTransform);
    
    // Bullet部分
    btCollisionShape* groundShapeBullet = new btBoxShape(btVector3(10, 10, 0.5));
    btDefaultMotionState* groundMotionState = new btDefaultMotionState(
        btTransform(btQuaternion(0, 0, 0, 1), btVector3(0, 0, -1)));
    
    btRigidBody::btRigidBodyConstructionInfo groundRigidBodyCI(
        0, groundMotionState, groundShapeBullet, btVector3(0, 0, 0));
    
    btRigidBody* groundRigidBody = new btRigidBody(groundRigidBodyCI);
    dynamicsWorld->addRigidBody(groundRigidBody);
    
    // 记录地面刚体与OSG节点的映射
    bodyNodeMap[groundRigidBody] = groundTransform;
    
    // 2. 创建球体
    // OSG部分
    osg::ref_ptr<osg::Sphere> sphereShape = new osg::Sphere(osg::Vec3(0, 0, 5), 1.0);
    osg::ref_ptr<osg::ShapeDrawable> sphereDrawable = new osg::ShapeDrawable(sphereShape);
    osg::ref_ptr<osg::Geode> sphereGeode = new osg::Geode;
    sphereGeode->addDrawable(sphereDrawable);
    
    osg::ref_ptr<osg::MatrixTransform> sphereTransform = new osg::MatrixTransform;
    sphereTransform->addChild(sphereGeode);
    root->addChild(sphereTransform);
    
    // Bullet部分
    btCollisionShape* sphereShapeBullet = new btSphereShape(1.0);
    btDefaultMotionState* sphereMotionState = new btDefaultMotionState(
        btTransform(btQuaternion(0, 0, 0, 1), btVector3(0, 0, 5)));
    
    btScalar mass = 1.0;
    btVector3 sphereInertia(0, 0, 0);
    sphereShapeBullet->calculateLocalInertia(mass, sphereInertia);
    
    btRigidBody::btRigidBodyConstructionInfo sphereRigidBodyCI(
        mass, sphereMotionState, sphereShapeBullet, sphereInertia);
    
    btRigidBody* sphereRigidBody = new btRigidBody(sphereRigidBodyCI);
    dynamicsWorld->addRigidBody(sphereRigidBody);
    
    // 记录球体刚体与OSG节点的映射
    bodyNodeMap[sphereRigidBody] = sphereTransform;
    
    // 3. 创建立方体
    // OSG部分
    osg::ref_ptr<osg::Box> boxShape = new osg::Box(osg::Vec3(3, 0, 3), 1.0);
    osg::ref_ptr<osg::ShapeDrawable> boxDrawable = new osg::ShapeDrawable(boxShape);
    osg::ref_ptr<osg::Geode> boxGeode = new osg::Geode;
    boxGeode->addDrawable(boxDrawable);
    
    osg::ref_ptr<osg::MatrixTransform> boxTransform = new osg::MatrixTransform;
    boxTransform->addChild(boxGeode);
    root->addChild(boxTransform);
    
    // Bullet部分
    btCollisionShape* boxShapeBullet = new btBoxShape(btVector3(1.0, 1.0, 1.0));
    btDefaultMotionState* boxMotionState = new btDefaultMotionState(
        btTransform(btQuaternion(0, 0, 0, 1), btVector3(3, 0, 3)));
    
    btRigidBody::btRigidBodyConstructionInfo boxRigidBodyCI(
        2.0, boxMotionState, boxShapeBullet, sphereInertia);
    
    btRigidBody* boxRigidBody = new btRigidBody(boxRigidBodyCI);
    dynamicsWorld->addRigidBody(boxRigidBody);
    
    // 记录立方体刚体与OSG节点的映射
    bodyNodeMap[boxRigidBody] = boxTransform;
    
    // 添加物理更新回调
    osg::ref_ptr<PhysicsUpdateCallback> physicsCallback = 
        new PhysicsUpdateCallback(dynamicsWorld, bodyNodeMap);
    root->setUpdateCallback(physicsCallback);
    
    // 设置场景数据并运行查看器
    viewer.setSceneData(root);
    return viewer.run();
}    

运行效果

相关推荐
重庆小透明2 小时前
【从零开始学习JVM | 第六篇】运行时数据区
java·jvm·后端·学习
晨曦backend3 小时前
Vim 替换命令完整学习笔记
笔记·学习·vim
liuyang___4 小时前
日期的数据格式转换
前端·后端·学习·node.js·node
cnbestec4 小时前
Minktec 柔性弯曲传感器应用:3D 脊柱姿势追踪与人体活动识别
3d·minktec·minktec柔性传感器·minktec弯曲传感器·柔性传感器
蒙奇D索大4 小时前
【11408学习记录】[特殊字符] 速解命题核心!考研数学线性代数:4类行列式满分技巧(含秒杀公式)
笔记·学习·线性代数·考研·改行学it
江池俊5 小时前
通过Docker和内网穿透技术在Linux上搭建远程Logseq笔记系统
linux·笔记·docker
葡萄城技术团队5 小时前
Wyn 商业智能与 3D 大屏的深度融合应用
3d·信息可视化
心 爱心 爱5 小时前
CVPR 2025 所有名字里带3D的论文:1-374 (存档,后续整理)
3d
哆啦A梦的口袋呀5 小时前
基于Python学习《Head First设计模式》第十章 状态模式
学习·设计模式