Post Lists

2018년 10월 9일 화요일

GPED 물리엔진 수정 (1)

GPED 책을 끝냈으니, 이해하지 못했던 것들, 그리고 아직 구현하지 않은 것들을 공부하고 이해하며 구현하면서, 그 과정들을 여기에 남겨놔야 겠다.

오늘은 알고리즘 공부하느라 많이 못했지만, 아주 작은 진전이 있었기에 남긴다.

일정 길이가 넘어가면 다음 게시글에 이어서 쓰도록 할 생각이다.

18-10-10
* 추가사항 : 카메라 위치와 방향에 따라 Rigidbody Projectile 쏘기
* 결과 GIF


* 추가한 목적 : Rigid Body Physics가 제대로 되고 있는지 쉽게 확인하기 위해
* 수정/추가한 코드

void AmmoRound::setState(ShotType shotType, const chanQuatCamera& camera)
{
 type = shotType;

 // Set the properties of the particle
 switch (type)
 {
 case SHOT_PISTOL:
  body->setMass(1.5f);
  body->setVelocity(0.f, 0.f, -20.f);
  body->setAcceleration(0.f, -0.5f, 0.f);
  body->setDamping(0.99f, 0.8f);
  radius = 0.2f;
  break;
 case SHOT_ARTILLERY:
  body->setMass(200.f); // 200.0kg
  body->setVelocity(0.f, 30.f, -40.f); // 50m/s
  body->setAcceleration(0.f, -21.0f, 0.f);
  body->setDamping(0.99f, 0.8f);
  radius = 0.4f;
  break;
 case SHOT_FIREBALL:
  body->setMass(4.f); // 4.0kg - mostly blast damage
  body->setVelocity(0.f, -0.5f, -10.f); // 10m/s
  body->setAcceleration(0.f, 0.3f, 0.f); // Floats up
  body->setDamping(0.9f, 0.8f);
  radius = 0.6f;
  break;
 case SHOT_LASER:
  // Note that this is the kind of laser bolt seen in films,
  // not a realisti laser beam!
  body->setMass(0.1f); // 0.1kg - almost no weight
  body->setVelocity(0.f, 0.f, -100.f); // 100m/s
  body->setAcceleration(0.f, 0.f, 0.f); // No gravity
  body->setDamping(0.99f, 0.8f);
  radius = 0.2f;
  break;
 }

 body->setCanSleep(false);
 body->setAwake();

 glm::mat3 tensor;
 GPED::real coeff = GPED::real(0.4) * body->getMass() * radius * radius;
 GPED::getInertiaTensorCoeffs(tensor, coeff, coeff, coeff);
 body->setInertiaTensor(tensor);

 // Set the data common to all particle types
 body->setPosition(camera.Position);
 startTime = glfwGetTime();
 body->setOrientation(1,0,0,0);

 // Camera Direction Shooting
 glm::vec3 OriginVel = body->getVelocity();
 glm::quat q = camera.Orientation *  glm::quat(0, OriginVel)  * glm::conjugate(camera.Orientation);
 body->setVelocity(q.x, q.y, q.z);

 body->setRotation(0, 0, 0);
 body->clearAccumulators();

 // Clear the force accumulators
 body->calculateDerivedData();
 calculateInternals();
}

원래의 Projectile에 Quaternion Camera를 받아서, 수정했다.
밑에서 주석 (// Camera Direction Shooting)에서부터 3줄을 추가했다.
그리고 body->setPosition()을 카메라의 위치로 바꾸었다.

발사체의 위치는 그냥 카메라 위치로 설정하면 되니 쉬웠지만,
방향을 설정하는데 조금 걸렸다.

처음에 setOrientation에다가 카메라의 Quaternion orientation 변수를 받아서 설정했는데,
RigidBody의 integrate 방식에서 orientation이 velocity의 방향을 바꾸지 않는다. 그러니까 발사체가 linear하게 velocity로 날아가야 하는데 안되는 것이였다. 그래서 여러가지 테스트를하고 생각을 해본 결과, body의 velocity를 카메라 쿼터니언으로 회전시킨다면, 된다고 생각했다.

http://chanhaeng.blogspot.com/2018/09/quaternions-and-rotations.html

내가 번역했던 쿼터니언 회전 자료에 따르면,

 


단위 쿼터니언 q를 사용하여 v(v또한 실수부가 0인 쿼터니언이다)를 회전시키는데, 이 때, 쿼터니언을 통한 회전은 v의 길이를 바꾸지 않는다. 또한 v의 방향은 보존된채 회전된다.

따라서, 


// Camera Direction Shooting
glm::vec3 OriginVel = body->getVelocity();
glm::quat q = camera.Orientation *  glm::quat(0, OriginVel)  * glm::conjugate(camera.Orientation);
body->setVelocity(q.x, q.y, q.z);

위의 코드를 통해서 발사체의 속도와 방향을 유지한 채 카메라의 방향으로 회전시킨 후,
그 결과를 rigidbody의 속도로 다시 설정한다.

여기에서 한 가지 더 고려해야 할 것은, GPED의 책은


switch (type)
{
case SHOT_PISTOL:
 body->setMass(1.5f);
 body->setVelocity(0.f, 0.f, 20.f);
 body->setAcceleration(0.f, -0.5f, 0.f);
 body->setDamping(0.99f, 0.8f);
 radius = 0.2f;
 break;
case SHOT_ARTILLERY:
 body->setMass(200.f); // 200.0kg
 body->setVelocity(0.f, 30.f, 40.f); // 50m/s
 body->setAcceleration(0.f, -21.0f, 0.f);
 body->setDamping(0.99f, 0.8f);
 radius = 0.4f;
 break;
case SHOT_FIREBALL:
 body->setMass(4.f); // 4.0kg - mostly blast damage
 body->setVelocity(0.f, -0.5f, 10.f); // 10m/s
 body->setAcceleration(0.f, 0.3f, 0.f); // Floats up
 body->setDamping(0.9f, 0.8f);
 radius = 0.6f;
 break;
case SHOT_LASER:
 // Note that this is the kind of laser bolt seen in films,
 // not a realisti laser beam!
 body->setMass(0.1f); // 0.1kg - almost no weight
 body->setVelocity(0.f, 0.f, 100.f); // 100m/s
 body->setAcceleration(0.f, 0.f, 0.f); // No gravity
 body->setDamping(0.99f, 0.8f);
 radius = 0.2f;
 break;
}

월드좌표계 기준으로 +Z축으로 이동하게끔 했는데(linear velocity를), 카메라가 보는 방향은 -Z축 이므로, -Z축으로 이동하게끔, 속도의 z component를 음수화 시켜주면, 제대로 동작하게 된다.

* 앞으로 공부해야 할 사항
1. 물리엔진 전반 다시
1) Rigid body의 뒷면으로 가서 발사체를 날리면, 충돌탐지가 안되고 그냥 지나가기도 한다.
2) 충돌 탐지가 제대로 되고 있는지 확인해야 한다.
3) 충돌 반응에서, Rigid Body가 한 바퀴 넘어가서 이전과 다르게 작동한다. 그러니까, 한 바퀴 넘기면, 다시 넘기기 힘들다. 내 생각에는 이 부분은 Tensor와 관련이 있는 것 같다.
4) 회전하고나서 Tensor가 제대로 변환되고 있는지, 그 후에 충돌 탐지와 반응이 되고 있는지 봐야된다.
5) 현재 Collision Resolution에서 adjustVelcoities 메소드에서 iterative 방식으로 하는데, 이것을 횟수를 많이하면 속도가 축적이 되어버려서, 제대로된 물리 엔진이 안된다. 그래서 임시방편으로 iteration을 1번으로 줄여놓았다. 이것이 왜 이런 일이 일어나는지 보고, 해결하도록 해야한다.

2018-10-10 오전
1.
5) 5번 먼저 해결 함. applyVelocityChange 메소드에서,


if (body[1])
{
 // Work out body one's linear and angular changes
 glm::vec3 impulsiveTorque = glm::cross(impulse, relativeContactPosition[1]);
 rotationChange[1] = inverseInertiaTensor[1] * impulsiveTorque;
 velocityChange[1] = -impulse * body[1]->getInverseMass();

 // And apply them.
 body[1]->addVelocity(velocityChange[1]);
 body[1]->addRotation(rotationChange[1]);
}

여기에서 velocityChane[1]을 할당할 때, '-' 부호를 안해줘서, 적당한 velocity change가 일어나지 않았고, 그래서 계속해서 원하는 대로 velocity가 변경이 안되니, 속도가 축적되어서, 그 해당 max index만 계속해서 velocity를 축적해버렸다. 그래서 위의 코드로 바꿔주었고, 속도 축적은 되지 않아서 velocityIteration을 많이 돌려도 괜찮아서 이전 보다 훨씬 더 나은 물리를 보여준다.













댓글 없음:

댓글 쓰기