有关求Unity的求解器与迭代器
在PhysX3.2中,刚体模拟相对于2.8.x时代,多了两个概念:contactOffset和restOffset。这两个变量加入的初衷是为了让SDK能够更好的控制相互靠的很近的物体,尽快将他们稳定下来。我们来看看文档对这两个变量的描述:
-
/**
-
\brief Sets the contact offset.
-
-
Shapes whose distance is less than the sum of their contactOffset values will generate contacts. The contact offset must be positive and
-
greater than the rest offset. Having a contactOffset greater than than the restOffset allows the collision detection system to
-
predictively enforce the contact constraint even when the objects are slightly separated. This prevents jitter that would occur
-
if the constraint were enforced only when shapes were within the rest distance.
-
-
<b>Default:</b> 0.02f * PxTolerancesScale::length
-
-
<b>Sleeping:</b> Does <b>NOT</b> wake the associated actor up automatically.
-
-
\param[in] contactOffset <b>Range:</b> (maximum(0,restOffset),inf)
-
-
@see getContactOffset PxTolerancesScale setRestOffset
-
*/
-
virtual void setContactOffset(PxReal contactOffset) = 0;
-
-
/**
-
\brief Sets the rest offset.
-
-
Two shapes will come to rest at a distance equal to the sum of their restOffset values. If the restOffset is 0, they should converge to touching
-
exactly. Having a restOffset greater than zero is useful to have objects slide smoothly, so that they do not get hung up on irregularities of
-
each others' surfaces.
-
-
<b>Default:</b> 0.0f
-
-
<b>Sleeping:</b> Does <b>NOT</b> wake the associated actor up automatically.
-
-
\param[in] restOffset <b>Range:</b> (-inf,contactOffset)
-
-
@see getRestOffset setContactOffset
-
*/
-
virtual void setRestOffset(PxReal restOffset) = 0;
从上面的说明可能比较委婉,但是我们可以粗俗一点的理解,contactOffset的作用相当于是把物体的碰撞形体变得肥了那么一点点,这样SDK可以更早以及更持续的获取两个物体之间的碰撞信息;而restOffset的作用则是允许两个物体的嵌入深度,只要嵌入的不够深,那么SDK不会用力把物体分开。
并且从值域我们可以看到,restOffset是可以为负数的,这时意味着允许嵌入的范围是从物体的边缘往里允许嵌入;那么,如果设置为正数的话,就是往外了 -- 如果把两个restOffset都是正的Box摞起来,他们之间会有空隙存在的,呵呵。
如果有PhysX 2.8的使用经验的话,那么你一定知道PhysX 2.8中的skinWidth,也就是Shape允许被穿透的厚度。这个参数的配置,在PhysX3.2中,就相当于是contactOffset=0,同时restOffset=-skinWidth。
从这两者的用途看,也许restOffset能够让两个物体更快的进入稳定状态的作用更加直观一些,因为他明确的定义了不超出某个范围,SDK就不会加力来把两个物体分开。相对来讲,contactOffset就不是那么容易理解了:为什么扩大了碰撞范围、更早更持续的感知到物体之间的碰撞,反而能够帮助物体更快的稳定下来呢?这一点跟PhysX SDK对物体的碰撞状态管理有关。想想这种状态:两个物体靠得非常近,这一帧稍微嵌入一点点,下一帧就刚刚分开,接下来又是持续的这样分分合合,这在一堆物体堆积在一起的时候是常见的情形。这样的情况下,如果没有contactOffset做一点提前预判的话,那么碰撞点的生成就是不连续的,SDK就需要生成很多TOUCH_FOUND、TOUCH_LOST这样的事件,不仅开发者处理起来麻烦,SDK管理起来也觉得不轻松。于是,就有了把碰撞形体变得肥胖一点,预判一下,这样可以更持续的获得碰撞的事件,并且由于只是在contactOffset的范围内,因此SDK虽然会生成碰撞信息,但是不会实际加力把这两个物体分开,于是,当他们满足Sleeping的条件后,就会分别进入Sleeping状态,碰撞点也就不会再生成了。
也许这么说,还是觉得比较空洞,不是那么好理解,那么,我们就做一个实验来看着两个到底是怎么工作的吧。
请把下面的代码加入到PhysX SDK的某个Sample中,让他们可以被执行到就可以:
-
const PxVec3 pos = getCamera().getPos();
-
PxVec3 dir = getCamera().getViewDir();
-
PxVec3 oriPos = pos + dir*20;
-
PxVec3 initVel (0.0f);
-
PxVec3 halfExts (1.0f);
-
PxReal contactOffset = 0.1f;
-
PxReal restOffset = -0.01f;
-
PxRigidDynamic* pActor0 = createSphere(PxVec3(oriPos.x - halfExts.x-contactOffset, oriPos.y, oriPos.z), halfExts.x, &initVel, mManagedMaterials[MATERIAL_RED], mDefaultDensity);
-
pActor0->setActorFlag(PxActorFlag::eDISABLE_GRAVITY, true);
-
-
PxShape* shape0[1];
-
pActor0->getShapes(shape0, 1);
-
shape0[0]->setContactOffset(contactOffset);
-
shape0[0]->setRestOffset(restOffset);
-
-
PxRigidDynamic* pActor1 = createSphere(PxVec3(oriPos.x+halfExts.x, oriPos.y, oriPos.z), halfExts.x, &initVel, mManagedMaterials[MATERIAL_RED], mDefaultDensity);
-
pActor1->setActorFlag(PxActorFlag::eDISABLE_GRAVITY, true);
-
pActor1->getShapes(shape0, 1);
-
shape0[0]->setContactOffset(contactOffset);
-
shape0[0]->setRestOffset(restOffset);
执行前,请打开PVD,这样,可以连接到PVD来观察碰撞点的生成情况。
同时,在这里,我也附加了两张图来说明这段代码的运行情况:
上面的代码,我们创建两个球,他们之间的最近距离刚好是一个contactOffset,这样他们看起来是不相交的,但是根据contactOffset和restOffset的工作方式,他们之间会产生碰撞点,并且不会移动,因为SDK不会尝试把他们分开,正如下面的图中我们看到的:
由于取消了重力,他们俩就会呆在那里,等待睡眠,然后碰撞事件就可以结束了,如这张图中我们看到的状态:
通过这个例程,对这两个概念的理解应该容易多了吧
当然,如果还觉得不够深刻,那么,就请修改上面的代码吧,多尝试几种情况,理解就会更深,也能够更合适的根据自己的场景,选择合适的参数来设置给contactOffset和restOffset。