【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();
}    

运行效果

相关推荐
悠哉悠哉愿意6 分钟前
【物联网学习笔记】TIM
笔记·单片机·嵌入式硬件·物联网·学习
中屹指纹浏览器15 分钟前
2026指纹浏览器技术选型与落地踩坑指南:从需求匹配到风险规避
经验分享·笔记
编程百晓生16 分钟前
《SAP FICO系统配置从入门到精通共40篇》021、CO-PC实战笔记:在制品与差异计算,那些年车间里的“账实不符”
服务器·人工智能·笔记·ar·从入门到精通·sap fico·sap fico系统
tctasia23 分钟前
TCT Awards 2026 获奖名单发布:年度增材制造创新全景一览
3d·制造
Swift社区39 分钟前
鸿蒙游戏中的“智能 NPC”架构设计
游戏·华为·harmonyos
WYiQIU1 小时前
宇树科技Web前端岗(AI方向),这不算泄题吧......
前端·vue.js·人工智能·笔记·科技·面试·职场和发展
王杨游戏养站系统1 小时前
3分钟!玩转游戏下载站系统!蜘蛛池seo功能完善部署!
游戏·游戏下载站养站系统·游戏养站系统
炽烈小老头1 小时前
【每天学习一点算法 2026/04/17】多数元素
数据结构·学习·算法
2501_916007471 小时前
从零开始学习iOS开发:Xcode环境配置与项目创建完整指南
ide·vscode·学习·ios·个人开发·xcode·敏捷流程
m0_743106461 小时前
【浙大&南洋理工最新综述】Feed-Forward 3D Scene Modeling(一)
论文阅读·人工智能·计算机视觉·3d·几何学