2015년 5월 2일 토요일

[일기] 기분나쁜 일기장 Springs and Springlike Things (6.1 ~ 6.2.4)


번역 실력 없음. 내가 이해한 바, 계속 고쳐나간다.

 우리가 엔진 제작시 생성 가능하고 가장 유용하게 쓸 수 있는 하나의 힘은 스프링력이다. 명확하게 용수철은 운전 게임에서 주로 쓰인다. 아마 부드럽고 변형 가능한 많은 물체들을 나타내는데 사용될 것이다. 스프링들과 입자들만으로도 거의 모든 부류의 인상적인 효과를 연출할 수 있다. 즉 물의 잔물결이나 천옷, 깃발 그리고 밧줄 등의 시연을 포함한다. 다음 챕터에서 다룰 경중 제약과 더불어, 스프링과 입자들은 거의 대부분의 물체들을 시연할 수 있다.

 우리의 엔진에서 스프링을 지원하기 위해, 이번 챕터에서는 스프링의 이론을 다루고나서 우리의 엔진에서 어떻게 그들이 구현될 수 있는지를 살펴보게 된다. 마지막으로는 스프링을 시연하는데 발생하는 무시못할 문제를 살펴보게 된다.

6.1 Hook's Law (훅의 법칙)

 훅의 법칙은 우리에게 스프링을 다룰 수식을 제공한다. 훅이란 사람은 스프링에서 발휘되는 힘이 그 스프링이 그 평형위치(rest position; 맞나?)으로부터 확장되거나 축소된 거리에 의존한다는 사실을 발견했다. 두배로 늘어난 스프링은 두 배의 힘을 발휘한다. 공식은 따라서 다음과 같다.

 여기서 ∆l 은 스프링의 변동 길이이다. 그리고 k는 소위 '스프링 상수'라 하는, 스프링의 경직 정도를 나타내는 값이다. 수식에서 계산되는 힘은 스프링의 양단에서 적용된다. 즉 다시 말해서 만일 두 물체가 스프링에 연결되어 있다면, 그들 각각은 서로 위 수식에 의해 계산되는 크기로, 같은 힘을 받으며 이끌릴 것이다.

 우리는 수식에서 ∆l 을 이용했다. 이로 알 수 있는 점은 평형점에서 스프링을 누르거나 당기는 힘이 없는 경우, 스프링은 본연의 길이를 가진다는 점이다. 이곳의 길이를 'rest length(휴지 길이;평형 길이(?)'라 부른다. 그리고 l0로 표기한다. 만약 스프링이 현재 길이 l을 갖고 있다면, 그 용수철에 의해 생성되는 힘은

f = -k ( l - l0) [6.1]
이 된다.

  지금까지, 훅의 법칙이 일차원 스프링에서만 적용되는 용어로 간주했었다. 하지만, 3차원으로 고려하자면 우리는 스칼라 대신 벡터로 힘을 표현해야 하며 이에 상응하는 수식은 다음과 같게 된다.

[6.1]
 여기서 d 는 우리가 힘을 적용시킬 물체에 속한 스프링의 한 끝으로부터 스프링의 다른 끝을 향하는 벡터이고 다음과 같이 주어진다.
[6.2]

 여기서 Xa는 고려 대상의 물체에 부착되어 있는 스프링의 한 끝의 위치이다. 그리고 Xb는 스프링의 다른 한 끝의 위치이다.

 식 [6.1]은 힘이 반드시 스프링의 다른 한 끝을 향하고 있어야 함을 의미하고 있다. 그리고 그 크기는 스프링 상수에 스프링이 확장된 정도를 곱하여 구해진다. 위의 |d| 부분이 스프링의 양끝 사이의 거리를 의미한다. 그리고 이는 단순히 스프링의 길이이다.

 식 [6.1]이  스프링의 한 끝에 관해서만 정의되었기 때문에(우리가 고려하는 물체가 붙어있는 부분), 스프링의 다른 끝에 붙어있는 물체를 처리하게 되었을 때 수식을 수정하지 않고도 그대로 적용이 가능하다. 또한 우리는 스프링이 양 끝의 물체 각각을 안쪽으로 당김을 알고 있기 때문에, 만일 한 끝에 있는 물체가 받는 힘이 f라 한다면 다른 끝의 물체가 받는 힘은 -f란 사실을 쉽게 알 수 있다.

 이번 챕터에서 다루게 될 Force Generator(이하 힘 발생기)에서는 각각의 힘을 따로따로 계산할 것이다. 즉, 위  사실을 고려하지 않을 것이다. 효율적으로 구현을 해 본다면 서로 연관된 두 물체들에 같은 힘 발생기를 사용하되, 먼저 한 물체를 다룰 때에 계산된 힘을 캐싱해 두었다가 다른 부분을 다룰 때 이를 사용하여 계산시간을 줄이는 방법으로 구현할 수도 있다. 이러한 형태의 힘 구동기는 CD에서 제공된다.

 6.1.1  The Limit OF Elasticity (탄성 한계)

  실제 스프링은 탄성 한계라 불리는 길이 내에서만 정상적으로 동작한다. 만일 당신이 금속 스프링을 계속해서 잡아 늘인다면, 결국 그 스프링은 자체의 탄성을 넘어서게 되어 변형될 것이다. 유사하게 계속 압축을 시킨다면 코일을 구성하는 금속이 서로 맞다아 더 이상은 축소되는 것이 불가능하게 된다.

 우리는 우리가 구현할 힘 발생기에서 이러한 실제적인 스프링의 구현을 지원하기 위해 몇 가지의 제한을 코드로 작성할 수 있다. 그러나 대부분의 경우, 이러한 복잡한 과정은 필요가 없다. 사용자들은 스프링이 '스프링 같이' 움직임이는 것을 주로 보게 될 것이며 스프링이 그 자체의 탄성 한계에서 정확하게 움직이고 있는지 아닌지는 관찰하기는 극히 어렵다. 한 예외사항이 있다면 그것은 특정 한계를 넘어서 압축될 수 없는 용수철의 경우가 있다. 가령 자동차의 현가장치를 예를 들 수 있는데, 이들은 완충을 위해 축소된다. 그러나 그들이 한계까지 축소되면 이 이후로는 더 이상은 스프링처럼 동작하지 않고 완충 없이 두 물체가 충돌하는 개념으로 다루어져야 할 것이다. 이와 같은 경중 제약은 다음 챕터에서 다룰 것인데 위에서 설명한 상황은 스프링을 사용해서는 쉽게 설계되지 않는다.

 6.1.2  Springlike Things (용수철같은 물체)

  식 [6.1]을 이용해서 시연될 수 있는 것은 코일로 감겨진 금속 용수철만이 아니다. 훅의 법칙은 실제 자연현상의 거의 모든 부분에 적용될 수 있다. 어떤 물체든 그것이 탄성을 갖는다면 그 탄성 한계 내에서 훅의 법칙을 적용할 수 있다.

  응용에는 제한이 없다. 스프링을 번지 점프에도 적용할 수 있다. 가장 까가운 물체 표면 위의 한 점과 물에 가라앉은 물체를 가상의 스프링으로 연결하는 방식으로 물의 부력을 시연하는데 사용할 수도 있다. 몇몇 개발자들은 게임 캐릭터 뒤에 용수철을 달고 그 뒤에 카메라를 달아서, 게임 캐릭터를 따라다니는 카메라를 제어하기 위해서도 스프링을 사용한다.

6.2 Springlike Force Generators (용수철 유사물 힘 발생기)

  이제 용수철 힘에 기반하여 4 가지의 힘 발생기를 구현해 볼 것이다. 비록 각각은 서로 약간씩 스프링의 현재 길이를 계산하는 방식에 차이가 존재한다. 그러나 그들은 모두 용수철힘을 계산하는데 훅의 법칙을 사용한다.
  이번 절에서는 많은 물리 시스템의 많은 특징들이 설명될 것이다. Core processing engine은 보통 일반성을 갖는다 - 즉, (인터페이스가?) 포괄적이다(추상적이다) - 그러나 이를 지원하는(알맞게 구현하는) 많은 도우미 클래스나 함수가 존재한다(이 경우에서는 4개의 힘 발생기가 그 역할을 수행한다). 이때 도우미 클래스나 함수들의 구현은 서로간 매우 유사하다. 단지 몇 가지의 차이가 존재하며 이를 별도로 처리하기 위해 구현이 분리되었을 뿐이다. 이 책의 남은 부분에서 이러한 유사한 부분들을 살피는 일은 피할 것이다. 비슷한 유형의 기능을 담는 구현은 소스 코드에서 찾아보길 바란다. 하지만 처음인 만큼, 이러한 서로의 미묘한 다른 구현들을 상세히 살펴보는 것은 가치가 있다.

 6.2.1  A Basic Spring Generator (기본 스프링 생성기)

  기본 스프링 생성기는 단순히 용수철의 길이를 식 [6.2]에 따라 계산을 하고 훅의 법칙을 이용하여 힘을 계산해낸다. 단순히 다음과 같이 구현될 수 있다.

- Excerpt from precision.h -
--------------------------------------------------------------
/** 절대값을 구하는 함수 재정의 */
#define real_abs fabsf
--------------------------------------------------------------

- Excerpt from pfgen.h -
--------------------------------------------------------------
/**
* @details A force generator that applies a spring force.
* 스프링힘을 적용시키는 힘 발생기
*/
class ParticleSpring : public ParticleForceGenerator {
/** 스프링의 다른 끝에 있는 입자*/
Particle *other;

/** 스프링 상수*/
real springConstant;

/** 평형 길이*/
real restLength;

public:
/** 주어진 파라미터를 통해 용수철 생성 */
ParticleSpring(Particle* other, real springConstant, real restLength);

/** 주어진 임자에 스프링힘을 적용*/
virtual void updateForce(Particle* particle, real duration);
};
--------------------------------------------------------------

- pfgen.cpp 발췌 -
--------------------------------------------------------------
void ParticleSpring::updateForce(Particle* particle, real duration) {
// 스프링 벡터를 계산한다.
Vector3 force;

particle->getPosition(&force);
force -= other->getPosition();

// 힘의 크기 계산
real magnitude = force.magnitude();
magnitude = real_abs(magnitude - restLength);
magnitude *= springConstant;

// 최종 힘 계산후 적용
force.normalize();
force *= -magnitude;
particle->addForce(force);
}
--------------------------------------------------------------

  이 발생기는 3개의 파라미터를 통해 생성된다. 첫째는 스프링의 다른 끝에 붙어 있는 물체에 대한 포인터이며, 둘째는 스프링 상수, 그리고 셋째는 스프링의 평형 길이이다. 우리는 다음과 같이 생성기를 만들고 적용할 수 있다.
--------------------------------------------------------------
Particle a, b;
ParticleForceRegistry registry;

ParticleSpring ps(&b, 1.0f, 2.0f);
registry.add(&a, ps);
--------------------------------------------------------------

 위 생성기는 스프링에 의존하는 데이터를 포함하고 있기 때문에 5장에서 생성한 발생기의 경우와는 다르게, 한 발생기 인스턴스가 다른 복수의 물체들에 적용될 수 없다. 우리는 각 물체에 대해 새로운 생성기를 생성할 필요가 있다. (엄격히 말해 사용을 못하는 것은 아니다. 여러 물체가 완전히 똑같은 평형 길이를 갖고 용수철 상수 값을 갖는 여러 스프링에 의해 연결되어 있는 물체들을 시연하고자 한다면 가능할 것이다)

  또한 이 발생기가 물체 하나에만 힘을 제공하고 있다는 사실을 알 필요가 있다. 만약 우리가 두 물체를 스프링으로 연결하고자 한다면, 우리는 각각에 대해 생성기를 등록해야 한다.
--------------------------------------------------------------
Particle a, b;
ParticleForceRegistry registry;

ParticleSpring psA(&b, 1.0f, 2.0f);
registry.add(&a, psA);
ParticleSpring psB(&a, 1.0f, 2.0f);
regisrty.add(&b, psB);
--------------------------------------------------------------

 6.2.2  An Anchored Spring Generator

 많은 경우에 있어서, 우리는 한 스프링에 두 물체를 함께 연결시키기를 원하지 않는다. 대신에 우리는 스프링의 한 끝을 공간 어딘가에 고정해 두기를 원한다. 이와 같은 경우는 탄성이 있는 밧줄로 된 다리 위의 지지 케이블을 위해 사용될 수도 있다. 예를 들어, 스프링의 한 끝은 다리에 붙어 있고, 다른 한 끝은 공간에 고정되어 있는 것이다. 그림 [6.2]를 보면 된다.

[그림 6.2]

 이러한 경우에, 이전에 우리가 생성한 힘 발생기의 구조는 적합하지 않다. 따라서 이를 상황에 맞게 변조하여 힘 발생기 자체가 연결될 물체보다는 고정될 위치를 받도록 하는 것이 좋을 것이다. 힘 발생기 코드는 힘을 계산할 때, 물체의 위치를 알아내서 위치를 계산하는 방법에서, 직접적으로 고정된 위치를 찾아 계산할 수 있도록 수정되야 한다. 이 Anchored force generator(이하 고정된 힘 발생기(?))의 구현은 다음과 같다.

- pfgen.h 발췌 -
--------------------------------------------------------------
/**
* @details 스프링힘을 제공하는 힘 발생기. 그러나 스프링의 한쪽 끝은 공간의 한 
* 점에 고정되어 있다.
*/
class ParticleAnchoredSpring : public ParticleForceGenerator {
/** 스프링의 고정된 한 끝의 위치*/
Vector3* anchor;

/** 스프링 상수*/
real springConstant;

/** 스프링의 평형 길이*/
real restLength;

pubilc:
/** 주어진 파라미터로 용수철 생성*/
ParticleAnchoredSpring(Vector3* anchor, real springConstant, real restLength);

/** 힘 생성후 대상 입자에 힘 적용*/
virtual void updateForce(Particle* particle, real duration);
};
--------------------------------------------------------------
- pfgen.cpp 발췌 -
--------------------------------------------------------------
oid ParticleAnchoredSpring::updateForce(Particle* particle, real duration) {
Vector3 force;
particle->getPosition(&force);
force -= *anchor;

real magnitude = force.magnitude();
magnitude = real_abs(magnitude - restLength);
magnitude *= springConstant;

// 최종 힘 계산 후 적용
force.normalize();
force *= magnitude;
particle->addForce(force);
}
--------------------------------------------------------------

  만일 게임 카메라를 사용자의 캐릭터에 연결시키기를 원한다면, 위 고정된 힘 생성기가 사용될 수 있을 것이다. 그러나 매번 절대 움직이지 않는 고정된 점을 사용하는 대신에, 캐릭터의 위치에 따라, 각 프레임마다 고정점을 재설정하고 재계산하는 과정이 필요할 것이다. 이전의 구현은 변경이 필요 없다. 단지 setAnchor()와 같은 메서드만 추가하면 될 것이다. 또한 이러한 고정점을 변경하는 기능은 추후에 필요할 수도 있을 것이다.

 6.2.3  An Elastic Bungee Generator

  탄성의 번지 점프 밧줄은 당기는 힘만을 제공한다. 이러한 구현은 절대 축소되지 않지만 확장은 되는 용수철을 구현하는 방법으로 가능할 수 있다. 이 상황은 두 물체의 쌍을 일정하게 유지하는데 있어서 유용하다. 만일 그들이 너무 많이 벗어나게 되면 서로 당길 것이며 분리됨 없이 그들이 원하는(?) 만큼 가까운 거리를 유지하게 된다. 이러한 힘 발생기는 다음과 같이 구현된다.

- pfgen.h 발췌 -
--------------------------------------------------------------
/**
* @details 확장될 경우에만 힘을 발휘하는 용수철
*/
class ParticleBungee : public ParticleForceGenerator {
/** 스프링의 고정된 한 끝에 붙은 입자*/
Particle* other;

/** 스프링 상수*/
real springConstant;

/** 이 용수철이 힘을 생성하기 시작하는 때에 번지 밧줄의 길이*/
real restLength;
public:
/** 주어진 파라미터로 번지 밧줄을 만든다 */
ParticleBungee(Particle* other, real springConstant, real restLength);

/**힘 생성 후 대상 입자에 힘 적용 */
void updateForce(Particle* particle, real duration);
};
--------------------------------------------------------------

- pfgen.cpp
--------------------------------------------------------------
void ParticleBungee::updateForce(Particle* particle, real duration) {
Vector3 force;
particle->getPosition(&force);
force -= other->getPosition();

//번짓줄이 축소된 것인지를 확인한다.
real magnitude = force.magnitude();
if (magnitude <= restLength) return;

magnitude = springConstant * (restLength - magnitude);

force.normalize();
force *= -magnitude;
particle->addForce(force);
}
--------------------------------------------------------------

 이곳에 공장 메서드를 추가했다(Factory Method). 이 클래스에서 번짓줄에 좀더 손쉽게 두 물체를 연결하는 것을 돕기 위함이다.

//// 시간이 너무 오래 걸린다... 하지 말아야





댓글 없음:

댓글 쓰기