Bullet有關六自由度彈性約束研究,bullet自由度
Bullet有關六自由度彈性約束研究
這段時間打算將物理引擎整合進來,於是對Bullet這款開源的物理引擎進行了一些研究。Bullet的研究也有了一段時間了,Bullet這個引擎其實很久之前就接觸了,只是一直以來,只是跑跑它的例子,也沒有研究例子以及原始碼,想要整合進入渲染引擎中也就井中月水中花了。今年3月,我曾經製作了一個整合Bullet最簡單的一個例子。即類比物體墜落的,並且寫了部落格《QtQuick + OpenGL + Bullet初次測試》。這個例子開了一個好頭。這段時間開始研究Bullet的一些其它有趣兒的特性了。
Bullet的約束(也稱關節),是一個非常有意思的部分,因為它表示了兩個碰撞物體之間的物理關係。比如說如果兩個珠子是由一根繩子串起來的,那麼繩子就代表了兩個珠子之間的約束,當然你可以將繩子換成一根橡皮筋或者一根彈簧,它們都代表了不同的約束。這些約束,都有它們的名稱,我最近在研究的就是六自由度彈性約束。
研究一個引擎最好的方法就是研究它的例子。對於Bullet也是如此。在Bullet內建的ConstraintDemo中,我看到了由一些非常有意思的約束組織而成的情境。因此從這個地方入手還不錯。例子的如下:
你可以使用滑鼠右鍵發射方塊,來測試一下約束對於這些物體有什麼作用。
這個情境包含了多種約束,每一種約束的處理方法都包含了相應的力學公式以及它的加成。我這回只對六自由度彈性約束進行研究,因此將其單獨拉了出來。
六自由度彈性約束是一種約束,顧名思義,它可以作六個維度旋轉,並且在平移方面可以保持一定的彈性。中的右上方中黃色和藍色方塊形成的就是一個六自由度彈性約束。這個情境將Bullet的約束都顯示出來了,讓大家都可以瞭解,約束究竟能夠怎樣地影響物體。
六自由度彈性約束它具有一下的屬性,它們分別如下:
英文名 |
中文名 |
RigidBodyA |
此約束作用的剛體A |
RigidBodyB |
此約束作用的剛體B |
frameInA |
從剛體A到此約束的變換 |
frameInB |
從剛體B到此約束的變換 |
LinearUpperLimit |
平移(線性)最高的限制 |
LinearLowerLimit |
平移(線性)最低的限制 |
AngularUpperLimit |
旋轉(角)最高的限制 |
AngularLowerLimit |
旋轉(角)最低的限制 |
Spring |
彈簧效果 |
Stiffness |
彈簧的剛性(勁度) |
Damping |
彈簧的衰減 |
我們將預設的例子效果修改一下,設計成我們想要的帶有彈簧的效果:
frameInA.setOrigin(btVector3(btScalar(10), btScalar(0), btScalar(0.))); frameInB.setOrigin(btVector3(btScalar(0), btScalar(0), btScalar(0.))); btGeneric6DofSpringConstraint* pGen6DOFSpring = new btGeneric6DofSpringConstraint(*pBodyA, *pBodyB, frameInA, frameInB, true); pGen6DOFSpring->setLinearUpperLimit(btVector3(0, 0, 0)); pGen6DOFSpring->setLinearLowerLimit(btVector3(0, 0, 0)); pGen6DOFSpring->setAngularLowerLimit(btVector3(-0.17f, -0.17f, -0.17f) ); pGen6DOFSpring->setAngularUpperLimit(btVector3(0.17f, 0.17f, 0.17f) ); pGen6DOFSpring->enableSpring(3, true); pGen6DOFSpring->setStiffness(3, 100.0f); pGen6DOFSpring->setDamping(3, 0.25f); pGen6DOFSpring->enableSpring(4, true); pGen6DOFSpring->setStiffness(4, 100.0f); pGen6DOFSpring->setDamping(4, 0.25f); pGen6DOFSpring->enableSpring(5, true); pGen6DOFSpring->setStiffness(5, 100.0f); pGen6DOFSpring->setDamping(5, 0.25f); pBodyA->addConstraintRef( pGen6DOFSpring ); pBodyB->addConstraintRef( pGen6DOFSpring );
將這些代碼替換掉ConstraintDemo.cpp的559到576行,然後重新查看效果。發現約束改變了,如:
黃色的方塊變成一個在六個自由度中都只有-10°到10°的約束。嘗試用方塊碰撞它,發現會繞著約束進行旋轉。
接下來就是模仿的過程了,話說我要親自嘗試一下在Qt Quick + OpenGL下跑Bullet中約束的例子,因此在前期做了很多的工作,初始化相關的類就花費了很多的精力,由於代碼篇幅較長,這裡只貼出重要的部分:
void initialize( void ) { qDebug( "--- initialize compoundCube physics ---" ); btTransform centerOfMassTransform( btTransform::getIdentity( ) ); centerOfMassTransform.setOrigin( btVector3( 0.2, 0.0, 0.2 ) ); btTransform tr; tr.setIdentity(); tr.setOrigin(btVector3(btScalar(-20.), btScalar(16.), btScalar(0.))); tr.getBasis().setEulerZYX(0,0,0); btRigidBody* pBodyA = createRigidBody( Cube, btVector3( 1.0, 1.0, 1.0 ), 0.0, tr ); pBodyA->setActivationState(DISABLE_DEACTIVATION); tr.setIdentity(); tr.setOrigin(btVector3(btScalar(-10.), btScalar(16.), btScalar(0.))); tr.getBasis().setEulerZYX(0,0,0); btRigidBody* pBodyB = createRigidBody( Cube, btVector3( 1.0, 1.0, 1.0 ), 1.0, tr, centerOfMassTransform ); pBodyB->setActivationState(DISABLE_DEACTIVATION); btTransform frameInA, frameInB; frameInA = btTransform::getIdentity(); frameInB = btTransform::getIdentity(); frameInA.setOrigin(btVector3(btScalar(10), btScalar(0), btScalar(0.))); frameInB.setOrigin(btVector3(btScalar(0), btScalar(0), btScalar(0.))); btGeneric6DofSpringConstraint* pGen6DOFSpring = new btGeneric6DofSpringConstraint( *pBodyA, *pBodyB, frameInA, frameInB, false ); pGen6DOFSpring->setLinearUpperLimit(btVector3(0, 0, 0)); pGen6DOFSpring->setLinearLowerLimit(btVector3(0, 0, 0)); pGen6DOFSpring->setAngularLowerLimit(btVector3(-0.17f, -0.17f, -0.17f) ); pGen6DOFSpring->setAngularUpperLimit(btVector3(0.17f, 0.17f, 0.17f) ); pGen6DOFSpring->enableSpring(3, true); pGen6DOFSpring->setStiffness(3, 100.0f); pGen6DOFSpring->setDamping(3, 0.25f); pGen6DOFSpring->enableSpring(4, true); pGen6DOFSpring->setStiffness(4, 100.0f); pGen6DOFSpring->setDamping(4, 0.25f); pGen6DOFSpring->enableSpring(5, true); pGen6DOFSpring->setStiffness(5, 100.0f); pGen6DOFSpring->setDamping(5, 0.25f); pBodyA->addConstraintRef( pGen6DOFSpring ); pBodyB->addConstraintRef( pGen6DOFSpring ); // End 我們新添加的 m_scene->physics( )->world( ).addConstraint(pGen6DOFSpring, true); pGen6DOFSpring->setDbgDrawSize(btScalar(5.f)); m_constraint = pGen6DOFSpring; // 新添加一個自由落體的 btTransform newTrans; newTrans.setIdentity( ); newTrans.setOrigin( btVector3( -12.0, 60, 0.9 ) );// btQuaternion q;// q.setEuler( 0.7, 0.6, 0.7 );// newTrans.setRotation( q ); btRigidBody* newBody = createRigidBody( Cube, btVector3( 2.0, 6.0, 3.0 ), 300.0, newTrans ); newBody->setActivationState( DISABLE_DEACTIVATION ); } void simulate( void ) { //m_constraint->setEquilibriumPoint( ); //qDebug( "--- simulate compoundCube physics ---" ); } btRigidBody* createRigidBody( ShapeType shapeType, const btVector3& half, btScalar mass, const btTransform& startTransform, const btTransform& centerOfMassTransform = btTransform::getIdentity( ) ) { btCollisionShape* shape = Q_NULLPTR; // 建立形狀 switch ( shapeType ) { case Sphere:// 球狀 shape = new btSphereShape( half.x( ) ); break; case Cube:// 立方體 shape = new btBoxShape( half ); break; case Capsule:// 膠囊 shape = new btCapsuleShape( half.x( ), half.y( ) ); break; default: qDebug( "This piece is code will never be reached unless bug." ); break; } btMotionState* motionState = new btDefaultMotionState( startTransform, centerOfMassTransform ); btVector3 inertia( 0, 0, 0 ); shape->calculateLocalInertia( mass, inertia ); btRigidBody* body = new btRigidBody( mass, motionState, shape, inertia ); m_scene->physics( )->world( ).addRigidBody( body ); return body; }
這裡建立的是一個簡單的情境:只有一個六自由度彈性約束,兩個剛體,還有一個品質非常大的物體,通過點擊按鈕來啟用物理引擎,這樣物體會通過做自由落體運動來將重力勢能轉化為動能,衝擊這個剛體。由於設定了旋轉彈性因子,因此處於約束另一方的剛體很自然地通過旋轉避開了品質大的物體,最終該物體被彈出……
翻開源碼,究竟彈性是怎麼實現的呢?原來這用到了一個經典的公式:胡克定律。胡克定律的公式是F= k·x,其中k是物體的勁度係數,這裡對應的也就是stiffness。看來要瞭解Bullet這個庫的原理,還要我們複習很多物理知識呢。