Post Lists

2018년 9월 11일 화요일

10 Laws of Motion For Rigid Bodies

쓰다가 모르고 날려버렸다...

어쨋든 inverse inertia tensor를 월드 좌표계로 기저변환하는 코드이다.


/**
 * Internal function to do an intertia tensor transform by a quaternion.
 * Note that the implementation of this function was created by an
 * automated code-generator and optimizer.
 */
static inline void _transformInertiaTensor(Matrix3 &iitWorld,
                                           const Quaternion &q,
                                           const Matrix3 &iitBody,
                                           const Matrix4 &rotmat)
{
    real t4 = rotmat.data[0]*iitBody.data[0]+
        rotmat.data[1]*iitBody.data[3]+
        rotmat.data[2]*iitBody.data[6];
    real t9 = rotmat.data[0]*iitBody.data[1]+
        rotmat.data[1]*iitBody.data[4]+
        rotmat.data[2]*iitBody.data[7];
    real t14 = rotmat.data[0]*iitBody.data[2]+
        rotmat.data[1]*iitBody.data[5]+
        rotmat.data[2]*iitBody.data[8];
    real t28 = rotmat.data[4]*iitBody.data[0]+
        rotmat.data[5]*iitBody.data[3]+
        rotmat.data[6]*iitBody.data[6];
    real t33 = rotmat.data[4]*iitBody.data[1]+
        rotmat.data[5]*iitBody.data[4]+
        rotmat.data[6]*iitBody.data[7];
    real t38 = rotmat.data[4]*iitBody.data[2]+
        rotmat.data[5]*iitBody.data[5]+
        rotmat.data[6]*iitBody.data[8];
    real t52 = rotmat.data[8]*iitBody.data[0]+
        rotmat.data[9]*iitBody.data[3]+
        rotmat.data[10]*iitBody.data[6];
    real t57 = rotmat.data[8]*iitBody.data[1]+
        rotmat.data[9]*iitBody.data[4]+
        rotmat.data[10]*iitBody.data[7];
    real t62 = rotmat.data[8]*iitBody.data[2]+
        rotmat.data[9]*iitBody.data[5]+
        rotmat.data[10]*iitBody.data[8];

    iitWorld.data[0] = t4*rotmat.data[0]+
        t9*rotmat.data[1]+
        t14*rotmat.data[2];
    iitWorld.data[1] = t4*rotmat.data[4]+
        t9*rotmat.data[5]+
        t14*rotmat.data[6];
    iitWorld.data[2] = t4*rotmat.data[8]+
        t9*rotmat.data[9]+
        t14*rotmat.data[10];
    iitWorld.data[3] = t28*rotmat.data[0]+
        t33*rotmat.data[1]+
        t38*rotmat.data[2];
    iitWorld.data[4] = t28*rotmat.data[4]+
        t33*rotmat.data[5]+
        t38*rotmat.data[6];
    iitWorld.data[5] = t28*rotmat.data[8]+
        t33*rotmat.data[9]+
        t38*rotmat.data[10];
    iitWorld.data[6] = t52*rotmat.data[0]+
        t57*rotmat.data[1]+
        t62*rotmat.data[2];
    iitWorld.data[7] = t52*rotmat.data[4]+
        t57*rotmat.data[5]+
        t62*rotmat.data[6];
    iitWorld.data[8] = t52*rotmat.data[8]+
        t57*rotmat.data[9]+
        t62*rotmat.data[10];
}
이건 글쓴이가 쓴 코드이다. 근데 왜 이렇게 곱하는지 정확한 설명을 하지 않았다.
이건 물리학을 공부해서 알아야할 내용이다. 그리고 쿼터니언을 쓰지도 않는데 왜 이것을 파라미터로 넘기는지 모르겠다.


/**
 * Internal function to do an inertia tensor transform by a quaternion.
 * Note that the implementation of this function was created by an 
 * automated code-generator and optimizer.
 */
static inline void _transformInertiaTensor(glm::mat3& iitWorld,
 const glm::quat& q, const glm::mat3& iitBody, const glm::mat4& rotmat)
{
 glm::mat3 rot = glm::mat3(rotmat);
 glm::mat3 rotated = rot * iitBody;
 
 for (unsigned i = 0; i < 3; ++i)
  for (unsigned j = 0; j < 3; ++j)
   for (unsigned k = 0; k < 3; ++k)
    iitWorld[j][i] += rotated[k][i] * rotmat[k][j];
}

이건 저 위의 식을 내가 바꾼내용이다.

처음에 rotmat을 3x3 행렬로 바꾸어, rot * ittBody를 하면 처음 행렬을 만드는 식과 똑같이 된다.

그리고 두 번째에서 iitWorld에 넣는데, 코드에서 보면 행 끼리 곱하고 있다.

위에서 만들어진 행렬이

t4   t9   t14
t28 t33  t38
t52 t57  t62

의 형식으로 들어가 있다고 생각하자. 그러면 행끼리의 곱셈이 된다.
사실 왜 이렇게 곱셈을하는지 모르겠기 때문에 일단은 저거와 똑같이 구현했다고 치고,
계속 넘어가자.

===============================================

10.3 D'Alembert For Rotation
우리가 뉴턴의 운동 제 2법칙과 동일한 것을 가졌듯이, 우리는 또한 D'Alembert의 원리의 회전 버전을 찾을 수 있다. D'Alembert의 원칙이 우리가 여러 힘들을 단일의 힘으로 축적하게 하는 것을 허용한 것을 기억해라. 그러고나서 이 한 힘을 적용할 수 있다. 그 하나의 축적된 힘의 효과는 모든 그것의 component forces와 동이라핟. 우리는 한 오브젝트에 적용되는 모든 힘을 간단히 더하여 이것을 이용한다. 그러고나서, 그것의 가속력을 한 번만 계산한다. 최종 결과게 기반으로 하여.

그 같은 원칙이 torques에 적용된다: 전체 torques의 효과는 그것들을 모두 합친 single torque의 효과와 같다.

우리는 다음을 갖는다



여기에서 tau_i는 i번째 torque이다.

그러나 복잡한 것이 있다. 우리는 이 챕터에서 일찍, off-center force가 torques로 변환될 수 있다는 것을 보았었다. 올바른 forces와 torques의 집합을 얻기위해서, 우리는 이 계산을 고려할 필요가 있다.

또 다른 D'Alembert의 원칙의 결과는 우리가어떤 다른 torques를 축저하는 방식과 똑같이 힘에 의해 야기된 torques를 축적할 수 있다는 것이다. 우리가 단순히 forces를 축적하고 그러고나서 최종 힘과 같은 torque를 취할 수 없다는 것을 주목해라. 우리는 두 개의 forces (손가락과 volume dial 위의 엄지같은)을 갖고, 이것은 linear forces로서 서로를 연쇄하지만, 큰 torque를 생성하기위해 합쳐진다.

그래서 우리는 두 개의 accumulators를 갖는다: forces를 위한 것과, torques를 위한 또 다른 것. 적용되는 각 force는 force와 torque accumulator 둘 다에 적용된다, 그리고 거기에서 그것의 torque는 다음으로 계산된다



(즉, 우리가 이전에 보았던 방정식 10.1). 각 적용된 torque에 대해, 우리는 그냥 torque를 축적한다 (torques는 대응되는 force component가 없다).

중력과 같은 어떤 forces들은 항상 한 오브젝트의 CM에 적용될 것이다. 이 경우에, 그것들은 회전을 이끌어내지 않기 때문에, 그것들의 torque component를 처리하려고 노력할 지점이 없다. 우리는 우리의 엔진에서 third route를 제공하여 이것을 허용한다 : 적용 위치가 없는 force를 더하는 것. 이 경우에, 우리는 force accumulator에 단순히 force를 더하고, torque accumulation를 넘어간다. 코드에서, 이것은 다음처럼 보인다

게다가, 우리가 몸체의 우리의 per-frame setup 우리는 torque를 0으로 만든다:

여기에서 중요한 경고는 한 힘의 적용 위치와 관련이 있다. 그것은 월드 좌표에서 표현되어야 한다. 만약 너가 한 스프링이 한 오브젝트의 고정된 점에 붙어있게 한다면, 너는 각 프레임에서 그 부착점의 위치를 재 계산할 필요가 있다. 너는 이것을 그 오브젝트의 좌표 포지션에 transform matrix로 변환하여 간단히 할 수 있다. 월드 좌표의 포지션을 얻기위해. 왜냐하면 이것은 할 수 있는 유용한 것이기 때문에, 나는 그것을 지원할 부가적인 force-accumulation method를 제공한다.

Be careful: force의 방향은 world 좌표계에서 예상되어지고, 반면에, 그 적용점은 object coordinates에서 예상된다. 이것은 이러한 계산이 보통 수행되는 방식과 부합하지만, 너는 그 힘과 position을 둘 다 world coordinates로 바꾸는 다르버전을 만들 수 있다. 이 경우에, force의 transformation에 신경써라: 그것은 오직 회전만되어야 한다; 그것은 full 4x3 matrix로 transformed 되어선 안된다 (vector에 offset position을 더하는).

10.3.1 Force Generators
우리는 파티클을 위해만들었던 force generators가 rigid bodies와 작동하도록 업데이트할 필요가 있다. 특히, 그것들은(force generators) rigid body의 특정한 점에서 한 force를 적용해야만 할지도 모른다. 만약 이것이 CM에 있지 않다면, 우리는 우리의 rigid body에 대한 force와 torque 둘 다를 생성할 것이다. 이전의 섹션에서 보았듯이.

이것은 force generator가 단일의 force vector를 리턴하지 않도록 하는 로직이다: 우리는 그 힘이 어디에서 적용되는지를 모를 것이다. 대신에 우리는 그 force generator가 그것이 원하는 무슨 방법이든 force를 적용하도록 허용할 것이다. 우리는 body의 CM보다는 한 점에 한 force를 적용하는 메소드를 호출하는 force generators를 만들 수 있거나, 그것들은 CM에 force를 적용할지도 모른다.

이것은 gravity force generator가 거의 같다는 것을 의미한다. particle보다는 rigid-body type을 받도록하는 것만이 변경된다:


GPED::Gravity::Gravity(const glm::vec3 & gravity)
 : gravity(gravity)
{ }

void GPED::Gravity::updateForce(RigidBody * body, real duration)
{
 // Check that we do not have infinite mass
 if (!body->hasFiniteMass()) return;

 // Apply the mass-scaled force to the body
 body->addForce(gravity * body->getMass());
}

그 spring force generator는 이제 그 스프링이 각 오브젝트에서 어디에 부착되어있는지를 알 필요가 있다. 그리고 그것은 그것의 적용점과 함께 적절한 force를 생성해야만 한다.


GPED::Spring::Spring(const glm::vec3 & localConnectionPt, RigidBody * other, const glm::vec3 & otherConnectionPt, real springConstant, real restLength)
 : connectionPoint(localConnectionPt), 
 other(other), otherConnectionPoint(otherConnectionPt),
 springConstant(springConstant), restLength(restLength)
{ }

void GPED::Spring::updateForce(RigidBody * body, real duration)
{
 // Calculate the two ends in world space
 glm::vec3 lws = body->getPointInWorldSpace(connectionPoint);
 glm::vec3 ows = other->getPointInWorldSpace(otherConnectionPoint);

 // Calculate the vector of the spring
 glm::vec3 force = lws - ows;

 // Calculate the magnitude of the force
 real magnitude = glm::length(force);
 magnitude = real_abs(magnitude - restLength);
 magnitude *= springConstant;

 // Calculate the final force and apply it
 force = glm::normalize(force);
 force *= -magnitude;
 body->addForceAtPoint(force, lws);
}

Torque Generators
우리는 force generators를 따를 수 있고, torque generators의 한 집합을 만들 수 있다. 그것들은 우리가 이제 까지 사용한 같은 force generator structure와 들어맞는다 : rigid body의 addTorque method를 호출하여.

너는 회전하는 오브젝트를 끊임없이 이끌기 위해 이것을 사용할 수 있다, 선풍기 날의 모음 또는 자동차의 바퀴들 같은 것들.

10.4 The Rigid-Body Integration
=================================
또 쓰다 조금 날아갔다. angular damping을 적용한 부분이였다.
그리고 그 공식에 대해서도 했다.
=================================

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
void RigidBody::integrate(real duration)
{
    // Calculate linear acceleration from force inputs
    lastFrameAcceleration = acceleration;
    lastFrameAcceleration += forceAccum * inverseMass;
    // Calculate angular acceleration from torque inputs.
    glm::vec3 angularAcceleration = inverseInertiaTensorWorld * torqueAccum;
    // Adjust velocities
    // Update linear velocity from both acceleration and impulse
    velocity += lastFrameAcceleration * duration;
    // Update angular velocity from both acceleration and impulse.
    rotation += angularAcceleration * duration;
    // Impose drag.
    velocity *= real_pow(linearDamping, duration);
    rotation *= real_pow(angularDamping, duration);
    // Adjust positions
    // Update linear position
    position += velocity * duration;
    // Update angular position
    glm::quat deltaOrien(0, rotation * duration);
    deltaOrien *= orientation;
    orientation.w += deltaOrien.w * ((real)0.5);
    orientation.x += deltaOrien.x * ((real)0.5);
    orientation.y += deltaOrien.y * ((real)0.5);
    orientation.z += deltaOrien.z * ((real)0.5);
    // Normalise the orientation, and update the matrices with the new
    // position and orientation
    calculateDerivedData();
    // Clear accumulators.
    clearAccumulators();
}
cs
{
Update angular position 부분은 업데이트 시키기 위해 위에 처럼 했다.

이 공식을 기반으로 코드가구현된 것이다.
Delta quat theta는 rotation * duration을하고, 실수항이 0인 쿼터니언을 만들고
orientation과 quaternion multiplication을하고, 그것의 절반씩을 원래의 쿼터니언에 더한다.
}

10.5 Summary
angular motion의 물리학은 우리가 chapter 3에서 만났던 linear motion의 물리학과 매우 유사하다. force가 mass를 통해 가속도와 연관지어지는 것과 같은 방식으로, 우리는 torque가 moment of inertia를 통해 angular acceleration과 연관지어지는 것을 보았다. 그 물리학은 비슷하지만, 각 경우에, 그 수학은 좀 더 복잡하고, 그 구현은 더 길어진다. vector position은 그것의 angular correspondence를 orientation에 대한 쿼터니언에서 발견했고, 그리고 scalar-valued mass는 inertia tensor가 되었었다.

그러므로 지난 두 챕터들은 책의 시작에 있는 것들 보다 상당히 더 어려웠다. 만약 너가 rigid-body physics engine을 얻기위해 따라온다면, 그러면 너는 너 자신을 자랑스러워 해도 된다. 우리가 이제까지 구성한 것에 중요한 제한이 있다 (특히, 우리는 충돌을 새로운 엔진에 넣지 않았다). 그래서 우리는 책의 나머지를 resolving에 쓸것이지만, 너가 우리가 가진 것으로 할 수 있는 많은 훌륭한 것들이 있다. Chapter 11은 우리의 현재 엔진에 대해 몇 가지 적용을 소개한다.







댓글 없음:

댓글 쓰기