确定使用外力时球体和平面之间的静止接触
Determining Resting contact between sphere and plane when using external forces
这个问题有一个大问题,一个小问题。我相信我的研究中任何一个问题都是对的,但不是两个问题。
对于我的物理循环,我要做的第一件事是对刚体物体的TotalForce
施加引力。然后,我使用TotalForce
和Velocity
检查碰撞。我的TotalForce
在每次物理循环后都会重置为(0, 0, 0)
,尽管我会保持velocity
。
我熟悉在仅使用速度时在移动球体和静态平面之间进行碰撞检查。但是,如果我除了velocity
之外还有其他力,例如重力怎么办?我把其他力放到TotalForces
(现在我只有重力(。为了弥补这一点,当我确定球体当前没有与平面重叠时,我这样做
Vector3 forces = (sphereTotalForces + sphereVelocity);
Vector3 forcesDT = forces * fElapsedTime;
float denom = Vec3Dot(&plane->GetNormal(), &forces);
然而,对于我认为应该是休息接触的方式来说,这可能是有问题的。我以为休息接触是由下式计算的
denom * dist == 0.0f
dist
在哪里
float dist = Vec3Dot(&plane->GetNormal(), &spherePosition) - plane->d;
(作为参考,明显的denom * dist > 0.0f
意味着球体正在远离平面(
然而,这永远不可能是真的。即使似乎有"休息接触"。这是由于我上面的forces
计算总是至少有 -9.8(我的重力(的 .y。当向法线为 (0, 1, 0( 的平面移动时,将产生 -9.8 的 denom
y 值。
我的问题是
1( 我是否正确计算了我提到的前两个代码片段的静止接触?
㞖
2(我的"其他力"(如重力(应该如何使用?我对TotalForces
的使用不正确吗?
作为参考,我的时间步长是
mAcceleration = mTotalForces / mMass;
mVelocity += mAcceleration * fElapsedTime;
Vector3 translation = (mVelocity * fElapsedTime);
编辑
由于一些建议的更改似乎会更改我的碰撞代码,以下是我检测碰撞状态的方法
if(fabs(dist) <= sphereRadius)
{ // There already is a collision }
else
{
Vector3 forces = (sphereTotalForces + sphereVelocity);
float denom = Vec3Dot(&plane->GetNormal(), &forces);
// Resting contact
if(dist == 0) { }
// Sphere is moving away from plane
else if(denom * dist > 0.0f) { }
// There will eventually be a collision
else
{
float fIntersectionTime = (sphereRadius - dist) / denom;
float r;
if(dist > 0.0f)
r = sphereRadius;
else
r = -sphereRadius;
Vector3 collisionPosition = spherePosition + fIntersectionTime * sphereVelocity - r * planeNormal;
}
}
-
您应该使用
if(fabs(dist) < 0.0001f) { /* collided */ }
这是为了计算浮点精度。您肯定不会在大多数角度或接触中获得精确的 0.0f。 -
如果为负值,则
dist
的值实际上是将主体移回平面表面所需的实际量,以防它穿过平面表面。sphere.position = sphere.position - plane.Normal * fabs(dist);
-
一旦你把它移回表面,你可以选择让它在与平面正常方向相反的方向上反弹;或者只是留在飞机上。
parallel_vec = Vec3.dot(plane.normal, -sphere.velocity);
perpendicular_vec = sphere.velocity - parallel_vec;
bounce_velocity = parallel - perpendicular_vec;
-
你不能盲目地做
totalforce = external_force + velocity
除非一切都有单位质量。
编辑:
- 要在 3D 空间中完全定义平面,平面结构应存储平面法线向量和平面上的点。 http://en.wikipedia.org/wiki/Plane_(几何( .
Vector3 planeToSphere = sphere.point - plane.point;
float dist = Vector3.dot(plane.normal, planeToSphere) - plane.radius;
if(dist < 0)
{
// collided.
}
我建议你先学习更多的数学,如果这是你不知道的部分。
注意:对不起,格式搞砸了...我无法将其标记为代码块。
编辑2:根据我对你的代码的理解,要么你命名变量不好,要么像我前面提到的,你需要修改你的数学和物理理论。此行不执行任何有用的操作。
float denom = Vec3Dot(&plane->GetNormal(), &forces);
A 在任何时间实例中,球体上的力可以是与行进方向无关的任何方向。 因此,DENOM基本上计算的是平面方向上的力,但不会告诉您球是否会撞击平面。 例如,重力是向下的,但球可以具有向上的速度并击中上方的平面。有了这个,你需要Vec3Dot(plane.normal, velocity)
。
另外,Mark Phariss 和 Gerhard Powell 已经为您提供了线性运动学的物理方程,您可以使用它们直接计算未来的位置、速度和撞击时间。
例如 s = 0.5 * (u + v) * t;
给出未来时间t之后的位移,将该位移与与平面的距离进行比较,就可以得到球体是否会撞击平面。所以再一次,我建议你先阅读 http://en.wikipedia.org/wiki/Linear_motion 和简单的东西,然后再阅读 http://en.wikipedia.org/wiki/Kinematics。
还有一种方法,如果您期望或假设没有其他力作用在球体上,那么您进行射线/平面碰撞测试以找到它撞击飞机的时间t,在这种情况下,读取 http://en.wikipedia.org/wiki/Line-plane_intersection 。
总会有-9.8y的重力作用在球体上。在悬浮球体的情况下,这将导致向下加速(净力不为零(。在球体停留在平面上的情况下,这将导致平面在球体上施加法向力。如果平面与静止的球体完全水平,则此法向力正好为+9.8y,这将完美地抵消重力。对于在非水平平面上静止的球体,法向力为 9.8y * cos(angle)
(角度介于 -90 和 +90 度之间(。
当移动的球体撞击平面时,事情变得更加复杂,因为法向力将取决于速度和平面/球体材料属性。根据您的应用要求,您可以忽略这一点,也可以尝试使用法向力进行一些操作,看看它是如何工作的。
对于您的具体问题:
- 我相信接触更具体地说只是在
dist == 0.0f
的时候,即球体和平面正在接触。我假设你的碰撞考虑到球体可能会在任何物理时间步长中移过平面。 - 现在,当它们接触时,你似乎没有任何法向力从平面施加在球体上。我会通过检查接触(
dist == 0.0f
(来做到这一点,如果这是真的,则将法向力添加到球体中。在将球体落到近水平面(角度在 -90 到 +90 度之间(的简单情况下,它只是sphereTotalForces += Vector3D(0, 9.8 * cos(angle), 0)
.
编辑:
从这里开始,dist
计算从球体边缘到平面的距离的方程可能不正确,具体取决于问题和代码的细节(未给出(。假设您的飞机穿过原点,正确的方程为:
dist = Vec3Dot(&spherePosition, &plane->GetNormal()) - sphereRadius;
如果plane->d == sphereRadius
,这与你的方程相同。请注意,如果平面不在原点,则使用:
D3DXVECTOR3 vecTemp(spherePosition - pointOnPlane);
dist = Vec3Dot(&vecTemp, &plane->GetNormal()) - sphereRadius;
这个问题的确切解决方案涉及一些非常严肃的数学。如果你想要一个近似的解决方案,我强烈建议你分阶段开发它。
1(确保您的模拟市民在没有重力的情况下工作。球必须穿过空间,并与倾斜的无摩擦表面发生非弹性(或部分弹性(碰撞。
2(引入重力。这将改变弹道轨迹从直线到抛物线,并引入滑动,但它不会对碰撞产生太大影响。
3(引入静态和动力摩擦(独立(。这些将改变滑动的动态。暂时不要担心碰撞中的摩擦。
4(给出球角速度和转动惯量。这是一大步。确保您可以对其施加扭矩并获得逼真的角加速度。请注意,旋转质量的实际行为可能是违反直觉的。
5(尝试在重力下沿水平表面滑动球。如果你做对了一切,它的角速度会逐渐增加,它的线速度会逐渐降低,直到它变成滚动。尝试给球一些初始旋转("平局","跟随"或"英语"(。
6(尝试相同的方法,但在倾斜的表面上。这是一个相对较小的步骤。
如果你走到这一步,你将拥有一个非常逼真的模拟游戏。不要试图跳过任何步骤,你只会让自己头疼。
物理问题的答案:
f = mg + other_f; // m = mass, g = gravity (9.8)
a = f / m; // a = acceleration
v = u + at; // v = new speed, u = old speed, t = delta time
s = 0.5 * (u + v) *t;
发生碰撞时,如果希望速度反弹,请将两个速度都更改为 0(或 v 和 u = -(u * 0.7(。
因为速度 = 0,所以球静止不动。
如果是 2D 或 3D,则只需将曲面法线方向上的速度更改为 0,并保持平行速度不变。这将导致球在表面上滚动。
如果球切开表面,则必须将球移动到表面。您可以将碰撞距离设置为少量(例如 0.001(以确保其保持静止。
http://www.physicsforidiots.com/dynamics.html#vuat
编辑:
NeHe是游戏引擎设计的惊人来源:这是一个关于碰撞检测的页面,有很好的描述:http://nehe.gamedev.net/tutorial/collision_detection/17005/
编辑2:(来自NeHe(
double DotProduct=direction.dot(plane._Normal); // Dot Product Between Plane Normal And Ray Direction
Dsc=(plane._Normal.dot(plane._Position-position))/DotProduct; // Find Distance To Collision Point
Tc= Dsc*T / Dst
Collision point= Start + Velocity*Tc
我建议之后看看erin cato的文章(Box2D的作者(和Glenn fiedler的文章。重力是一种强大的加速度,并产生强大的力。由于浮动不精确、可变时间步长和欧拉积分,很容易很快出现错误的模拟。球体在平面上重新定位,以防它开始通过平面,如果它开始埋入平面,我注意到自己最好只在球体的速度与平面法线相反时才这样做(这可以与 3D 渲染中的面部剔除进行比较:不要考虑背面平面(。
此外,大多数物理引擎会停止对空闲物体的模拟,大多数游戏在移动时从不考虑重力,只有在坠落时才会考虑重力。他们使用"导航网格"和自定义系统,只要他们确定模拟对象粘在"地面"上即可。
我不知道那里有完美的物理模拟器,总会有集成爆炸,错过碰撞(寻找"扫荡碰撞"(......这需要大量的实证微调。
此外,我建议您寻找"脉冲",这是一种避免在遇到碰撞时手动调整速度的方法。
另请查看"每个计算机科学家都应该知道的浮点">
祝你好运,你进入了一个雷区,随机无法理解,数字计算机科学:)咬手指的区域
为了获得更高的保真度(不会解决您的主要问题(,我会将您的时间步改为
mAcceleration = mTotalForces / mMass;
Vector3 translation = (mVelocity * fElapsedTime) + 0.5 * mAcceleration * pow(fElapsedTime, 2);
mVelocity += mAcceleration * fElapsedTime;
你提到球体是一个刚体;你是否也把平面建模为刚体?如果是这样,在接触和完全弹性碰撞的那一刻,你将有一个无限的点力,而没有一些明显的动量消散。
力和速度不能相加(不兼容的单位(;如果你只是想对运动学进行建模,你可以忽略质量,只使用加速度和速度。
假设球体只是简单地放到一个水平面上,有一个完全非弹性的碰撞(没有反弹(,你可以做[注意,我真的不知道C语法,所以这将是Pythonic]
mAcceleration = if isContacting then (0, 0, 0) else (0, -9.8, 0)
如果你在碰撞中增加一些弹性(比如半动量守恒(,它更像是
mAcceleration = (0, -9.8, 0) + if isContacting then (0, 4.9, 0)
- C++中std::resize(n)和std::shrink_to_fit之间的区别
- int(c) 和 c-'0' 之间的区别。C++
- 在cuda线程之间共享大量常量数据
- 在c代码之间共享数据的最佳方式
- Mix_Init和Mix_OpenAudio SDL之间的区别是什么
- C++ 使用 assign 函数的字符串与直接使用 '=' 更改值的字符串之间的区别
- VSOMEIP-2个设备之间的通信(TCP/UDP)不工作
- std::atomic和std::condition_variable wait,notify_*方法之间的区别
- 大小相等但成员数量不同的结构之间的性能差异
- 类与私有变量的其他类之间的线程安全性
- 如何在cpp文件之间切换窗口?在Qt中
- 线程之间的布尔停止信号
- 如何找到两个棋盘平面之间的角度?
- 平面和球体光线追踪之间的交点
- 如何在不复制数据的情况下在平面数组和多维数组之间进行转换?
- 确定使用外力时球体和平面之间的静止接触
- 如何修改svm.cpp来计算点和超平面之间的距离?(LIBSVM)
- 获取单个平面中两个矢量之间角度的有效方法
- 计算三维点与平面之间的距离,c++
- 从.oni文件中获取平面图像和深度图像之间的对应关系