bullet学习日记

2023-10-20 00:10
文章标签 学习 日记 bullet

本文主要是介绍bullet学习日记,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

最近需要bullet用物理引擎做一个测量类的项目,因为半途接手,物理部分其实已经实现,但犹于对bullet基本不了解,导致相关部分完全改不动,这两天静下心来把物理引擎用法了解了一翻,顺便做点笔记,以便以后回头看rep,提高类似工作的效率。

因为英文水平并不高,基本上搜索中文资料,然鹅真正专业的都是英文,并且看不太懂。两种学习方式里,感觉自己比较适合从上端往下学习的方式(即直接从demo代码开始了解其功能,一点一点了解。另外一种从下端往上端学习的方式是从文档入手,看完再开始看demo写代码)。直接看文档,太过抽象。

 

一、

一开始我对物理引擎的实现没有认识,在心中构思物理引擎的实现方式,主要有两种方式:一种是他有一个类,我们继承它,便具备了物理特性,一种是它是一个库,我们使用他提供的方法来计算我们的数据。

然而开始了解之后,才发现比较接近第二种想法。真实的是他通过宏选择模块,项目中引用相关文件,然后便可以实例相关类,并使用相关方法。

----->

这一部分是在我对使用Bullet有了应用上的认识之后写的:

bullet像很多仿真系统一样,

先需要你按它的要求但创建一个仿真环境,包括地百等;

然后向其中添加你要仿真的对象以及其属性,如电路仿真是电路元件模型,而这里则是刚体,软体等模型;

然后便可以开始仿真了,即调用它提供的开始接口,参数通常会是一个时间,他返回该时间后对象的状态,即运算结果。

通过对bullet的了解过程,这里做个小总结,基本上从了解如何创建仿真环境开始,稍后是如何向其中添加仿真元素,最后是他的交互过程,即如何仿真并向用户反馈结果。

但是在应用时,我希望能直接使用他内部的检测算法,或者中间产物,而不直接使用的运算结果,因为我并非需要他的结果,而是只需要它帮助计算碰撞是否已习惯以发生,或者发生碰撞的位置等。所以还需要继续了解,以使能灵活的使用大牛的资源。20190814

<-----

 

二、

具体基本流程如下:这是网上找到的一篇较为清晰易懂的适合初学者的文章,希望对同是初识者的你有所帮助。

Bullet教程: Hello World 实例

  

更多信息请关注 物理引擎中文社区http://www.physicsengine.org

这篇文章里我们将尽可能简单的向你展示怎么使用Bullet, 怎样初始化Bullet, 设置一个动力学世界, 还有一个球落向地表 这个对鉴别你的build是否成功非常有用并且也能够让你快速的学习到Bullet的API. 首先,我们假设你的Bullet已经正确安装并且正确设置了Bullet的include路径(例如. /usr/local/include/bullet) 确保能连接到正确的lib. 否则请参阅Installation安装. 如果你用的是gcc来编译,请确保你的静态库反序,就是说. dynamics, collision, math.

 

初始化程序

以一个标准的hello world程序开始:

 

  1. #include <iostream>
  2. int main ()
  3. {
  4. std::cout << "Hello World!" << std::endl;
  5. return 0;
  6. }

 

创建世界

现在我们要添加一个子弹(Bullet)模拟. 首先写入以下语句:

#include <btBulletDynamicsCommon.h>

我们想把btDiscreteDynamicsWorld 实例化但是在做此之前我们还需要解决一些其他事情. 对于一个“hello world”例子来说它太复杂我们并不需要. 但是,为了能更符合我们自己的工程, 他们可以用来微调(fine-tuning)模拟环境.

我们需要指出使用什么样的 Broadphase algorithm(宽相算法). 选择什么样的泛型算法很总要,如果有许多刚体在绘制场景里, since it has to somehow check every pair which when implemented naively(天真) is an O(n^2) problem.

宽相(broadphase)使用 传统的近似物体形状并且被称之为代理.我们需要提前告诉子弹最大的代理数, 所以才能很好的分配内存避免浪费. 下面就是世界里任何时候的最大刚体数.

int maxProxies = 1024;

一些 broadphases 使用特殊的结构要求世界的尺度提前被告知, 就像我们现在遇到的情况一样. 该broadphase可能开始严重故障,如果离开这个物体体积. 因为 the AxisSweep broadphase quantizes 空间基于我们使用的整个空间的大小, 您想这差不多等于你的世界.

使它小于你的世界将导致重大问题, 使它大于你的世界将导致低劣的性能.这是你程序调整的一个简单部分, 所以为了确保数字的正确多花点时间也不防.

在这个例子中,世界从起点开始延伸10公里远。

 

  1. btVector3 worldAabbMin(-10000,-10000,-10000);
  2. btVector3 worldAabbMax(10000,10000,10000);

 

这个broadphase是我们将要使用的, 这个执行的是扫描和裁剪, 这里可以看到更多解释Broadphase .

 

btAxisSweep3* broadphase = new btAxisSweep3(worldAabbMin,worldAabbMax,maxProxies);

 

该broadphase是一个极好的空间以消除不应碰撞的成队物体. 这是为了提高运行效率.

您可以使用碰撞调度注册一个回调,过滤器重置broadphase代理,使碰撞系统不处理系统的其它无用部分

. 更多信息请看 Collision Things.

碰撞配置可以让你微调算法用于全部(而不是不是broadphase )碰撞检测。这个方面现在还属于研究阶段

 

  1. btDefaultCollisionConfiguration* collisionConfiguration =  new btDefaultCollisionConfiguration();
  2. btCollisionDispatcher* dispatcher =  new btCollisionDispatcher(collisionConfiguration);

 

 

我们还需要一个"solver". 这是什么原因导致物体进行互动得当,考虑到重力,游戏逻辑等的影响,碰撞,会被制约。

它工作的很好,只要你不把它推向极端,对于在任何高性能仿真都有瓶颈有一些相似的可以线程模型:

 

btSequentialImpulseConstraintSolver* solver = new btSequentialImpulseConstraintSolver;

 

终于我们可以初始化了世界了:

 

btDiscreteDynamicsWorld* dynamicsWorld = new btDiscreteDynamicsWorld(dispatcher,broadphase,solver,collisionConfiguration);

 

很明显我们把重力方向设置成了Y轴的负方向,即Y轴是像上的

 

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

 

子弹的政策是“谁分配,也删除” 记住,必须符合这样的结果

在main()后记的删除.

我们提供了一个通用的结果. 代码如下:

 

  1. #include <btBulletDynamicsCommon.h>
  2. #include <iostream>
  3. int main () {
  4. std::cout << "Hello World!" << std::endl;
  5. // Build the broadphase
  6. int maxProxies = 1024;
  7. btVector3 worldAabbMin(-10000,-10000,-10000);
  8. btVector3 worldAabbMax(10000,10000,10000);
  9. btAxisSweep3* broadphase =  new btAxisSweep3(worldAabbMin,worldAabbMax,maxProxies);
  10. // 设置好碰撞属性 和调度
  11. btDefaultCollisionConfiguration* collisionConfiguration =  new btDefaultCollisionConfiguration();
  12. btCollisionDispatcher* dispatcher =  new btCollisionDispatcher(collisionConfiguration);
  13. // 实际上的物理模拟器
  14. btSequentialImpulseConstraintSolver* solver =  new btSequentialImpulseConstraintSolver;
  15. // 世界.
  16. btDiscreteDynamicsWorld* dynamicsWorld =  new btDiscreteDynamicsWorld(dispatcher,broadphase,solver,collisionConfiguration);
  17. // 这里做一些你想做的事
  18. // 作为一个好的编程习惯 做好删除工作
  19. delete dynamicsWorld;
  20. delete solver;
  21. delete dispatcher;
  22. delete collisionConfiguration;
  23. delete broadphase;
  24. return 0;
  25. }



 

 

碰撞包围体

我们将创造一个接地平面[静态刚体] ,和一个球体,将属于在地上[动态刚体] 。每个刚体需要参考碰撞包围体. 碰撞包围体只解决碰撞检测问题, 因此没有质量,惯性,恢复原状等概念. 如果您有许多代理,使用相同的碰撞形状[例如每飞船模拟是一个5单元半径范围]。这是个好做法,只有一个子弹形状的碰撞,并分享它在所有这些代理. 但是我们这里的两个刚体形状都不一样,所以他们需要各自的shape.

地面通常是向上的并且里原始点1米的样子. 地面会和远点交叉,但子弹不允许这样做,

因此,我们将抵消它的1米和用来弥补,当我们把刚体设置好以后。

 

btCollisionShape* groundShape = new btStaticPlaneShape(btVector3(0,1,0),1);

 

我们将让它从天上掉下来,它是一个球体,半径为1米.

 

btCollisionShape* fallShape = new btSphereShape(1);

 

 

这里需要做碰撞形状的清理工作.

 

刚体

在,我们可以添加形状的碰撞到我们的现场,并将它们定位.

让我们先初始化地面. 它的方向是特定的, 子弹的四元数形式 x,y,z,w . 位置在地面下一米, 将要补充一米我们不得不做的. 运动状态在这里可以得到详细的说明:MotionStates

 

  1. btDefaultMotionState* groundMotionState =
  2. new btDefaultMotionState(btTransform(btQuaternion(0,0,0,1),btVector3(0,-1,0)));

 

 

在第一个和最后一个参数,在下面的构造函数中是质量和地表的惯性. 由于地面是静止的所以我们把它设置成0. 固定不动的物体,质量为0 -他是固定的.

 

  1. btRigidBody:: btRigidBodyConstructionInfo
  2. groundRigidBodyCI(0,groundMotionState,groundShape,btVector3(0,0,0));
  3. btRigidBody* groundRigidBody =  new btRigidBody(groundRigidBodyCI);



 

最后我们把地面加到世界中:

 

dynamicsWorld->addRigidBody(groundRigidBody);

 

新增下跌领域非常相似。我们将其置于50米以上的地面.

 

btDefaultMotionState* fallMotionState = new btDefaultMotionState(btTransform(btQuaternion(0,0,0,1),btVector3(0,50,0)));

 

由于它是动态刚体,我们将给予质量1公斤。我不记得如何计算一个球体的惯性,但是,这并不重要,因为子弹提供它的实现

 

  1. btScalar mass =  1;
  2. btVector3 fallInertia(0,0,0);
  3. fallShape->calculateLocalInertia(mass,fallInertia);

 

 

现在,我们可以建造刚体只是像以前一样,并把它加到世界中:

 

  1. btRigidBody::btRigidBodyConstructionInfo fallRigidBodyCI(mass,fallMotionState,fallShape,fallInertia);
  2. btRigidBody* fallRigidBody =  new btRigidBody(fallRigidBodyCI);
  3. dynamicsWorld->addRigidBody(fallRigidBody);

 

 

一个快速的解释btRigidBody::btRigidBodyConstructionInfo是为了; 物体的构建是通过某些参数的. 这是通过一个特殊的结构实现的。 该部分的btRigidBodyConstructionInfo被复制到物体当你建造的时候,并只用于在初始化的时候. 如果你想创建几千个属性一样的物体, 你只需要建立一个btRigidBodyConstructionInfo, 并通过它创建所有的.

 

开始模拟

这就是有趣的开始。我们会加强模拟200倍,间隔60赫兹. 这使它有足够的时间降落的地面上. 每一步, 我们都会打印出它离地面的高度.

这stepSimulation 在做你所期待, 不过他的接口确实很复杂. 读Stepping The World 以获得更多消息.

进后,我们审查的状态下降领域.位置和方向都封装在btTranform对象,我们摘录下降领域的运动状态. 我们只关心位置,我们退出变换getOrigin ( ) 。然后,我们打印y组成部分的立场载体.

 

 

  1. for (int i=0 ; i<300 ; i++) {
  2. dynamicsWorld->stepSimulation( 1/60.f,10);
  3. btTransform trans;
  4. fallRigidBody->getMotionState()->getWorldTransform(trans);
  5. std::cout << "sphere height: " << trans.getOrigin().getY() << std::endl;
  6. }

 

 

这应该产生一个输出看起来像这样的东西:

sphere height: 49.9917

        sphere height: 49.9833

        sphere height: 49.9722

        sphere height: 49.9583

        sphere height: 49.9417

        sphere height: 49.9222

        sphere height: 49.9

        ...

        sphere height: 1

        sphere height: 1

        sphere height: 1

        sphere height: 1

        sphere height: 1

看起来不错迄今。如果你图这对输出迭代次数,你就会得到这个:

 

这个球体开始于地表的一米处. 这是因为取的是几何中心并且它的半径为1米. 这个球刚开始会有一个大的反弹然后渐渐的减缓弹起高度.

这是可以预料的实时物理引擎,但它可以尽量减少,增加频率的模拟步骤

. 试试再说!

现在你可以把这个动态世界代入你的程序 实时绘制出这个球体. 也可以看看其他的 Collision Shapes . 试试一堆盒子 或者圆柱体然后用一个球去扔向他们.

 

完整代码

 

    1. #include <iostream>
    2. #include <btBulletDynamicsCommon.h>
    3. int main (void)
    4. {
    5. btVector3 worldAabbMin( -10000,-10000,-10000);
    6. btVector3 worldAabbMax( 10000,10000,10000);
    7. int maxProxies =  1024;
    8. btAxisSweep3* broadphase =  new btAxisSweep3(worldAabbMin,worldAabbMax,maxProxies);
    9. btDefaultCollisionConfiguration* collisionConfiguration =  new btDefaultCollisionConfiguration();
    10. btCollisionDispatcher* dispatcher =  new btCollisionDispatcher(collisionConfiguration);
    11. btSequentialImpulseConstraintSolver* solver =  new btSequentialImpulseConstraintSolver;
    12. btDiscreteDynamicsWorld* dynamicsWorld =  new btDiscreteDynamicsWorld(dispatcher,broadphase,solver,collisionConfiguration);
    13. dynamicsWorld->setGravity(btVector3( 0,-10,0));
    14. btCollisionShape* groundShape =  new btStaticPlaneShape(btVector3(0,1,0),1);
    15. btCollisionShape* fallShape =  new btSphereShape(1);
    16. btDefaultMotionState* groundMotionState =  new btDefaultMotionState(btTransform(btQuaternion(0,0,0,1),btVector3(0,-1,0)));
    17. btRigidBody::btRigidBodyConstructionInfo
    18. groundRigidBodyCI( 0,groundMotionState,groundShape,btVector3(0,0,0));
    19. btRigidBody* groundRigidBody =  new btRigidBody(groundRigidBodyCI);
    20. dynamicsWorld->addRigidBody(groundRigidBody);
    21. btDefaultMotionState* fallMotionState =
    22. new btDefaultMotionState(btTransform(btQuaternion(0,0,0,1),btVector3(0,50,0)));
    23. btScalar mass =  1;
    24. btVector3 fallInertia( 0,0,0);
    25. fallShape->calculateLocalInertia(mass,fallInertia);
    26. btRigidBody::btRigidBodyConstructionInfo fallRigidBodyCI(mass,fallMotionState,fallShape,fallInertia);
    27. btRigidBody* fallRigidBody =  new btRigidBody(fallRigidBodyCI);
    28. dynamicsWorld->addRigidBody(fallRigidBody);
    29. for (int i=0 ; i<300 ; i++) {
    30. dynamicsWorld->stepSimulation( 1/60.f,10);
    31. btTransform trans;
    32. fallRigidBody->getMotionState()->getWorldTransform(trans);
    33. std::cout <<  "sphere height: " << trans.getOrigin().getY() << std::endl;
    34. }
    35. dynamicsWorld->removeRigidBody(fallRigidBody);
    36. delete fallRigidBody->getMotionState();
    37. delete fallRigidBody;
    38. dynamicsWorld->removeRigidBody(groundRigidBody);
    39. delete groundRigidBody->getMotionState();
    40. delete groundRigidBody;
    41. delete fallShape;
    42. delete groundShape;
    43. delete dynamicsWorld;
    44. delete solver;
    45. delete collisionConfiguration;
    46. delete dispatcher;
    47. delete broadphase;
    48. return 0;
    49. }

转载于:https://www.cnblogs.com/ice-arrow/p/11345643.html

这篇关于bullet学习日记的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/243272

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识

线性代数|机器学习-P36在图中找聚类

文章目录 1. 常见图结构2. 谱聚类 感觉后面几节课的内容跨越太大,需要补充太多的知识点,教授讲得内容跨越较大,一般一节课的内容是书本上的一章节内容,所以看视频比较吃力,需要先预习课本内容后才能够很好的理解教授讲解的知识点。 1. 常见图结构 假设我们有如下图结构: Adjacency Matrix:行和列表示的是节点的位置,A[i,j]表示的第 i 个节点和第 j 个

Node.js学习记录(二)

目录 一、express 1、初识express 2、安装express 3、创建并启动web服务器 4、监听 GET&POST 请求、响应内容给客户端 5、获取URL中携带的查询参数 6、获取URL中动态参数 7、静态资源托管 二、工具nodemon 三、express路由 1、express中路由 2、路由的匹配 3、路由模块化 4、路由模块添加前缀 四、中间件