우리의 엔진에 대해 우리가 만들 수 있는 가장 유용한 힘들 중의 하나는 spring force이다. 비록 스프링들이 운전 게임 (car의 suspension을 재현하기 위한)에서 명백한 사용을 가질지라도, 그것들은 부드럽거나 또는 변형될 수 있는 많은 종류의 오브젝트들에게도 사용된다. 스프링들과 파티클들은 홀로 ropes, flags, cloth garments, water ripples와 같은 인상적인 효과들의 다양한 범위를 만들 수 있다. 우리가 다음 챕터에서 다룰 hard constraints와 함께, 그것들은 거의 어떤 종류의 오브젝트든 나타낼 수 있다.
스프링을 지원하도록 우리의 엔진을 확장하기 위해서, 이 챕터는 처음에 스프링의 이론을 다루고, 그러고나서 그것들이 어떻게 우리의 엔진을 위해서 만들어질 수 있는지를 볼 것이다. 마지막으로, 우리는 스프링 재현의 가장 중요한 문제를 볼 것이다.
6.1 Hook's Law
Hook's의 법칙은 스프링의 수학을 우리에게 준다. Hook은 한 끈에 의해 발휘된 힘이 오직 그것의 rest position으로부터 그 스프링이 연장되거나 또는 응축된 거리만을 의존한다는 것을 발견했다. 두 배 더 확장된 스프링은 두 배 더 힘을 발휘할 것이다. 그 공식은 그러므로
여기에서 Δl 은 스프링이 확장되거나 응축된 거리이고, k는 "spring constant"인데, 스프링의 stiffness(뻣뻣함)을 주는 값이다. 이 방정식에 의해 주어지는 힘은 스프링의 양 끝에서 느껴진다. 다시 말해서, 두 오브젝트들이 한 스프링에 의해 연결되어있다면, 그러면 그것들은 각각 서로 같은 힘으로 끌어와질 것이다. 이전의 방정식에 의해서.
우리가 방정식에서 Δl 을 사용한 것에 유의해라. 이것은 왜냐하면 스프링을 확장하거나 응축시키는데 작용할 어떠한 힘도 없는 휴식기에서, 그것이 어떤 natural length를 가지기 때문이다. 이것은 또한 "rest length"라고 불려지고, l_0라는 기호를 갖는다. 만약 그 스프링이 현재 길이 l에 있다면, 그러면 그 스프링에 의해 생성되는 힘은
지금까지, 우리는 Hook's law를 오직 1차원 스프링의 관점에서만 고려했다. 3차원에 관해서, 우리는 스칼라라기 보다는 force vector를 만들 필요가 있다. 힘에 대응되는 공식은
여기에서 d는 우리가 힘을 생성할 그 오브젝트에 부착된 스프링의 끝에서 그 스프링의 다른 끝까지에서 나온 벡터이다. 그것은 다음에 의해 주어진다
여기에서 x_A는 고려중에 있는 그 오브젝트에 부착된 스프링의 끝의 위치이고, x_B는 그 스프링의 다른 쪽 끝의 위치이다.
방정식 6.1은 그 힘이 그 스프링의 다른 쪽 끝 방향 (\widhat{d} component) 이 되어야 한다고 말한다. 스프링의 확장된 양이 곱해진 스프링 계수가 주어진 크기와 함께 - -k(|d| - l_0) 부분이다. 그 |d| 요소는 스프링의 끝 사이의 거리의 크기이다. 그리고 이것은 간단히 스프링의 길이가 된다. 이것은 -k(|d| - l_0) 를 -k(l - l_0)를 쓰는 다른 방식일 뿐이다.
방정식 6.1이 오직 그 스프링의 한 쪽 끝의 관점으로 정의되었기 때문에 (우리가 현재 고려하고 있는 오브젝트에 부착된 끝), 우리는 그것은 그 스프링의 다른 쪽 끝에 수정하지 않고 사용할 수 있다. 우리가 거기에 부착된 오브젝트를 처리하려 할 때. 대안적으로, 그 스프링의 두개의 끝들은 서로를 향해 같은 크기의 힘으로 끌어당기기 때문에, 우리는 만약 한 쪽 끝에서의 힘이 f라면, 그러면 그 반대쪽은 -f라는 것을 안다.
이 챕터에서 묘사되는 force generator에서, 우리는 각 오브젝트에 대한 force를 개별적으로 계산할 것이고, 이 사실을 이용하지 않는다. 좀 더 최적화된 접근법은 관련된 두 오브젝트들에 대해 같은 force generator를 사용하고, 다른 것에 대해 힘을 다시 계산하는 시간을 아끼기 위해 하나로부터 나온 force calculation을 cache할 것이다. 이 종류의 force generator는 source code에서 제공된다.
6.1.1 The Limit of Elasticity
진짜 스프링은 오직 길이의 어떤 범위 내에서만 이렇게 행동한다: 이 범위는 그것들이ㅡ "limit of elasticity(탄성 한도)"라고 불려진다. 만약 너가 금속 스프링을 계속해서 연장하려 한다면, 결국에 너는 그것의 탄성을 초과할 것이고, 그것은 변형될 것이다. 유사하게, 만약 너가 스프링을 너무 많이 응축시킨다면, 그것의 코일들은 닿을 것이고, 더 이상 응축이 불가능하다.
우리는 현실적인 스프링 모델을 만들기 위해서 이러한 한계들을 우리의 force generator에 코딩할 수 있다. 그러나, 대부분의 경우에, 우리는 이러한 정교함을 필요하지 않는다. 플레이어들은 그것의 가장 스프링 같은 것을 하는 스프링을 볼 것이다: 그들은 그것이 그것의 탄성 한도 내에서 정확히 행동하는지를 눈치채지 못할 가능성이 높다. 이것에 대한 한 예외는 어떤 한계를 넘어서 응축할 수 없는 스프링의 경우이다. 이것은 car suspension에 대해서는 사실이다: 그것들은 그것들의 "stop"을 한다. 이 지점에 대해 응축된 후에, 그것들은 더 이상 스프링처럼 행동하지 않고, 오히려 두 오브젝트 사이에 충돌처럼 행동한다. 우리는 다음 챕터에서 이러한 종류의 hard constraint를 다룰 것이다: 그것은 쉽게 스프링을 사용하여 모델링 되어질 수 없다.
6.1.2 Springlike Things
방정식 6.1을 사용하여 재현될 수 있는 것은 coiled metal spinrg이다 : Hook's law는 자연 현상의 큰 범위에 적용된다. 탄성의 특성을 갖는 어떤 것이든 보통 Hook's law가 적용되는 탄성 한도를 가질 것이다.
적용은 한계가 없다. 우리는 스프링으로 탄성이있는 번지를 구현할 수 있다. 우리는 같은 방식으로 물의 부력을 재현할 수 있다. 잠수된 오브젝트를 보이지 않는 스프링으로 표면의 가장 가까운 점과 연결하여. 몇몇 개발자들은 심지어 카메라를 조종하기 위해 스프링을 사용한다. 그것이 게임 캐릭터를 따라다니기 때문이다. 카메라에서 캐릭터 뒤에 있는 점의 스프링을 적용하여 (그림 6.1을 보아라).
6.2 Springlike Force Generators
우리는 스프링 힘에 기반으로 네 개의 force generators를 구현할 것이다. 각각이 스프링의 현재 길이를 계산하는 것의 다소 다른 방식을 가질지라도, 그것들은 모두 결과 힘을 계산하기 위해 Hook's law를 사용한다.
이 섹션은 많은 물리 시스템의 한 특징을 보여준다. 코어 프로세싱 엔진은 generic하게 남지만, 그것은 꽤 종종 서로가 비슷한 helper classes와 functions에 의해 둘러싸인다 (이 경우에는 스프링 force generators의 다른 유형들로). 이 책의 나머지에서, 나는 세부적으로 비슷한 변형을 하는 것을 피할 것이다; 너는 소스코드에서 비슷한 클래스들의 몇 가지들을 볼 수 있다.
6.2.1 A Basic Spring Generator
기본 스프링 generator는 간단히 방정식 6.2를 사용하여 그 스프링의 길이를 계산하고, 힘을 계산하기 위해 Hook's law를 사용한다. 그것은 이것처럼 구현되어질 수 있다:
/** * A force generator that applies a spring force */ class ParticleSpring : public ParticleForceGenerator { /** The Particle at the other end of the spring. */ GPEDParticle* other; /** Holds the spring constant. */ real springConstant; /** Holds the rest length of the spring. */ real restLength; public: /** Creates a new spring with the given parameters. */ ParticleSpring(GPEDParticle* other, real springConstant, real restLength); /** Applies the spring force to the given particle.*/ virtual void updateForce(GPEDParticle* particle, real duration); }; void GPED::ParticleSpring::updateForce(GPEDParticle* particle, real duration) { // Calculate the vector of the spring glm::vec3 force = particle->getPosition(); force -= other->getPosition(); // 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; particle->addForce(force); }
그 generator는 세 개의 파라미터들로 만들어진다. 그 첫 번째는 그 스프링의 다른 끝에 있는 오브젝트이고, 그 두 번째는 스프링 상수, 그리고 세 번째는 그 스프링의 rest length이다. 우리는 이 코드를 사용하여 generator를 만들 수 있다:
Particle a, b; ParticleForceRegistry registry; ParticleSpring ps(&b, 1.0f, 2.0f); registry.add(&a, &ps);
스프링에 의존하는 데이터를 포함하고 있기 때문에, 한 인스턴스는 다양한 오브젝트들에 대해 사용할 수 없다 chapter 5에서 force generators가 했던 방식으로. 대신에 뤼는 각 오브젝트에 대한 새로운 generator를 만들 수 있다.
또한 force generator (우리가 만났던 다른 것들과 같이) 오직 한 오브젝트에 대해 힘을 만든다는 것을 유의해라. 만약 우리가 두 오브젝트를 스프링으로 연결하려면, 그러면 우리는 각각에 대해 generator를 만들고 등록할 필요가 있을 것이다.
GPED::ParticleForceRegistry forcemanager; GPED::GPEDParticle a, b; a.setPosition(glm::vec3(20.f, 5.f, 0.f)); // a.setAcceleration(glm::vec3(0.f, -10.f, 0.f)); b.setPosition(glm::vec3(17.f, 5.f, 0.f)); // b.setAcceleration(glm::vec3(0.f, -10.f, 0.f)); GPED::ParticleSpring fspringA(&b, 1.0f, 2.0f); GPED::ParticleSpring fspringB(&a, 1.0f, 2.0f); forcemanager.add(&a, &fspringA); forcemanager.add(&b, &fspringB);
6.2.2 An Anchored Spring Generator
많은 경우에, 우리는 두 오브젝트를 스프링으로 함께 연결하고 싶지 않다; 오히려 우리는 그 스프링의 한 쪽 끝이 공간에서 고정된 점이 되길 원한다. 예를들어, 이것은 탄력있는 rope-bridge에서 지지하는 케이블에 대해 사실일지도 모른다. 그 스프링의 한 쪽 끝은 다리에 부착되어 있고, 그 다른 것은 공간에 고정되어 있다; 그림 6.2를 이 것의 예시로 보아라.
이 경우에, 우리가 만든 spring generator의 형태는 작동하지 않을 것이다. 우리는 그것을 수정하여, generator가 연결될 한 오브젝트 보다는 고정된 위치를 기대한다. force generator code는 또한 한 오브젝트에 대해 보기 보다는 오히려 직접 위치를 사용하여 수정된다. 그 anchored force generator 구현은 이것처럼 보인다:
/** * A force generator that applies a spring force, where * one end is attached to a fixed point in space. */ class ParticleAnchoredSpring : public ParticleForceGenerator { /** The location of the anchored end of the spring*/ glm::vec3* anchor; /** Holds the spring constant. */ real springConstant; /** Holds the rest length of the spring. */ real restLength; public: /** Creates a new spring with the given parameters. */ ParticleAnchoredSpring(glm::vec3* anchor, real springConstant, real restLength); /** Applies the spring force to the given particle*/ virtual void updateForce(GPEDParticle* particle, real duration); }; void GPED::ParticleAnchoredSpring::updateForce(GPEDParticle * particle, real duration) { // Calculate the vector of the spring. glm::vec3 force = particle->getPosition(); force -= *anchor; // 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; particle->addForce(force); }
만약 우리가 플레이어의 캐릭터에 게임 카메라를 연결하려 한다면, 이것은 우리가 사용하는 한 접근법이다. 결코 움직이는 anchor point 대신에, 그러나, 우리는 캐릭터의 위치를 기반으로 매 프레임마다 anchor point를 재계산하고, 그 anchor를 재설정한다. 이전 구현은 어떠한 수정도 필요없다 (새로운 값을 주는 setAnchor method말고); 우리는 anchor point를 game loop어디간에서 update를 수행할 필요가 있다.
6.2.3 An Elastic Bungee Generator
elastic bungee는 오직 당기는 힘만을 만들어낸다. 너는 그것을 구겨서 tight ball로 만들 수 있고, 그것은 밖으로 펴지지 않을 것이지만, 그것은 연장되었을 때 다른 스프링처럼 행동한다. 이것은 오브젝트들의 한 쌍을 함께 유지하는데 유용하다: 만약 그것들이 너무 멀리 자기 위치를 벗어나면 그것들이 함께 당겨진다. 그러나 그것들은 분리되는 것없이 가까워질 수 있다.
그 generator는 이것처럼 구현된다:
/** * A force generator that applies a spring force only * when extended. */ class ParticleBungee : public ParticleForceGenerator { /** The particle at the other end of the spring.*/ GPEDParticle* other; /** Holds the spring constant.*/ real springConstant; /** * Holds the length of the bungee at the point it begins to * generate a force */ real restLength; public: /** Creates a new bungee with the given parameters */ ParticleBungee(GPEDParticle* other, real springConstant, real restLength); /** Applies the spring force to the given particle*/ virtual void updateForce(GPEDParticle* particle, real duration); }; void GPED::ParticleBungee::updateForce(GPEDParticle* particle, real duration) { // Calculate the vector of the spring glm::vec3 force = particle->getPosition(); force -= other->getPosition(); // Check if the bungee is compressed real magnitude = glm::length(force); if (magnitude <= restLength) return; // Calculate the magnitude of the force. magnitude = springConstant * (magnitude - restLength); // Calculate the final force and apply it force = glm::normalize(force); force *= -magnitude; particle->addForce(force); }
{
spring을 이용해 다리 같은 것을 구현해보려고 했다. 일단 고정점은 red quad로 작게 그렸고, 그 밑에 box가 다리의 panel과 같은 것이 된다. restLength를 설정하고, 그것보다 더 긴 위치에서 시뮬레이션을 시작하게 했다. 처음에 damping을 1.0으로 하고 시뮬레이션 했는데, panel이 고정점을 위아래로 왔다갔다 했다. 아마 힘이 가해진게 damping이 많이 되지 않아 그대로, 속도를 계속 바꾸니까 그렇다. 그래서 damping을 0.3으로 바꾸어 속도가 많이 줄어들게 했는데, 그렇게 하니 restLength로 돌아와서 애들이 멈추었다.
2018-10-12
지금 드는 생각은, spring generator에 toleration 값을 넣어서, 오차가 얼마 안날 때는 force를 넣지 않도록 해도되겠다. 또는 내가 찾은 다른 link를 보고 구현을 좀 봐바야겠다.
}
나는 우리가 쉽게 두 오브젝트를bungee에연결하도록 하기 위해 이 클래스에 또한 factory function을 추가했다.
이 구현은 elastic이 두 오브젝트에 연결된 것을 가정한다. simple spring generator와 같은 방식으로, 우리는 한 오브젝트를 공간에서 고정된 anchor point에 연결하는 코드 버전을 만들 수 있다. 우리가 필요한 수정은 정확히 우리가 이전에 본 것과 같다. 그래서 나는 그것들을 길게 다시 쓰지 않을 것이다. 소스 코드에 anchored bungee genrator에 대한 샘플 구현이 있다.
{
더 진행하기 전에, 나의 생각을 정리할 겸, 각 force generator에 대해 정리해볼 필요가 있다.
- ParticleGravity, ParticleDrag : 이것은 이전 챕터에서 이야기 되었듯이, particle에직접 acceleration을 설정하고 damping을 설정하여 처리한다. 그래서 내가 따로 무언가 특별한 것을하고 싶다면 쓸 수 있다.
- ParticleSpring, ParticleAnchoredSpring : 두 개의 큰 차이점은 다른 한 쪽이 particle이냐 또는 고정된 anchor이냐이다. 사실, anchor는 바뀔 수 있는 fixed particle이지만.
여기에서 하나 드는 의문은, 내가 ParticleSpring, ParticleAnchoredSpring 둘 다를 이용해서 시뮬레이션 했을 때, 이것들은 정말 스프링처럼, restLength에 도달 했을 때 가만히 고정되지 않는다. 그래서 우리는 아직 마찰력이라던지 그러한 것들이 force에 넣어지지 않았다는 것을 알 필요가 있다. 그래서 이것을 제대로 spring처럼 사용하기 위해서는 이 책을 더 읽어보고 이 책의 글쓴이가 나중에 이 클래스를 정확히 어떻게 사용하는지 알 필요가 있다.
updateForce를 보면 알겠지만, 이것은 계속해서 spring force를 적용하기 때문에, restLength에 도달하더라도, 약간의 오차로 인해서 스프링 압축과 확장이 계속해서이루어지게 된다.
- ParticleBungee, ParticleAnchoredBungee : 두 개의 큰 차이점은 updateForce에 있는데, 먼저 공통점부터 말할 것이다. 둘 다의 공통점은 연결된 두 개의 것이 restLength보다 작으면 거리 계산을 하지 않는다는 것이다. 즉, 코드로는
if(magnitude <= restLength) return;
이다. 그런데 차이점은
ParticleBungee는,
magnitude = springConstant * (restLength - magnitude);
ParticleAnchoredBungee는,
magnitude = springConstant * (magnitude - restLength);
즉 빼는 방식이 다르다. 위 코드는 둘 다, magnitude가 restLength보다 클 때, 그러니까 두 오브젝트가 스프링 길이보다 확장되어 있을때 이다. 그런데 그냥 bungee는 음의 값을 가지게되고, ParticleAnchoredBungee는 양의 값을 가진다.
사실 나는 여기에서 코드가 잘못되었다는 의심이 든다. elastic bungee이기 때문에, 일정 거리 이상 가면 당겨야 하는데, bungee가 음의 값을 가지고 방향을 반대로 하기 때문에, 당겨져야 할 것이 반대로 멀리 나가게 한다. 실제로 시뮬레이션 하면은 두 오브젝트는 끝없이 멀리 날아간다.
ParticleAnchoredBungee는 내가 예상하듯이 그 restLength에 도착하면 멈추려고한다.
하지만, 우리는 코드 구현이 어떻게 되었는지 이해를 한다면, damping을 좀 걸어줘야
particle이 제대로 멈춘다는것을 알것이다.
결론은, 아직 이번 파트에서 계속 소개되는 force generator들이 아직 불안정하다는 것이다. 따라서, 그냥 이런 force generator들은 이렇게 만드는 것이구나 라고 대강의 이해를 하고, 계속 공부하고. 나중에 demo를 설명할 때, 직접 소스코드를 보고 이러한 force generator들을 어떻게 활용되는 지를 끝까지 파고든다면, 이 코드들이 왜 이렇게 되었는지, 어떤 코드가 문제 인지를 우리는 알 수 있을 것이고. 이것들을 사용할 수 있는 힘을 얻을 수 있을 것이다.
}
6.2.4 A Buoyancy Force Generator
buoyancy force(부력)은 한 오브젝트가 뜨게 유지하는 것이다. 아르키메데스는 처음에 부력이 한 오브젝트가 옮겨놓는 물의 무게와 동일하다는 것을 작업했었다.
그림 6.3의 첫 번째 부분은 물에서 잠수된 한 블럭을 보여준다. 그 블럭은 0.5kg의 질량을 가진다. 순수한 물은 1000kg/m^3 의 농도를 가진다; 다시 말해서, 한 물의 세제곱 미터는 약 one metric ton(1000kg, 1톤)을 갖는다. 그림에 있는 블럭은 0.001m^3의 부피를 갖는다. 그래서 그것은 같은 양의 물을 옮기고 있다. 이 물의 질량은 그러므로 1kg이 될 것이다 (0.001m^3 * 1000kg = 1kg).
무게는 물리에서 질량과 같지 않다. 질량은 오브젝트가 가속도를 저항하게 만드는 한 오브젝트의 특성이다. 한 오브젝트의 질량은 항상 같은 것이다. 무게는 중력이 한 오브젝트에 발휘하는 힘이다. 우리가 이미 보았듯이, 힘은 다음의 방정식에 의해 주어진다.
f = mg
여기에서 f는 weight이고, m은 질량, g는 중력가속도이다. 이것은 다른 행성에서, 같은 오브젝트가 다른 무게를 갖는 것을 의미한다. 왜냐하면 g가 바뀌기 때문이다. (그러나 질량은 같다).
지구에서, 우리는 g = 10m/s^2이라고 가정한다. 그래서 1kg의 무게를 가진 오브젝트는 1 x 10 = 10kN의 무게를 가질 것이다. 그 kN 단위는 무게의 단위이다: kilograms, kg, 는 무게의 단위가 아니다. 너의 bathroom scales이 그렇게 말할지라도. 이것은 공간에 대해 작업하는 과학자들이 다양한 문제에 반영시키도록 야기시킨다: g가 다르기 때문에, 그들은 과학 reference book에서 발견되는 변환 factors를 이용하여 더 이상 파운드와 같은 English units(영국 단위)들을 kilograms으로 바꿀 수 없었다. 파운드는 무게의 기준이고, kilogram은 질량의 기준이다.
그래서, 부력으로 돌아가자. 그림 6.3의 첫 번째 부분에서 우리의 블럭은 10kN의 부력을 가진다. 그림의 두 번째 부분에서, 오직 절반만이 잠겨있다. 그래서 같은 계산을 사용해서, 그것은 5kN의 부력을 가진다.
비록 우리가 우리의 force generator를 위해 그것을 사용할 필요가 없을지라도, 그 오브젝트의 무게를 보는 것은 또한 교육적이다. 두 경우에, 그 블럭의 무게는 같다: 5kN (0.5kg의 질량에 같은 g의 값을 곱하면). 그래서 그 그림의 첫 번째 부분에서, 그 부력은 그 블럭을 위로 올릴 것이다; 그림의 두 번째 부분에서, 그 무게는 부력과 정확히 같다. 그래서 그 오브젝트는 같은 위치를 유지할 것이다 - 즉 떠다닌다.
한 오브젝트에 대해 정확한 부력을 계산하는 것은 도형이 어떻게 생겼는지를 정확히 아는 것을 포함한다. 왜냐하면 그 도형은 옮겨지는 물의 부피에 영향을 미치기 때문이다. 그리고 그것은 힘을 계산하기 위해 사용된다. 만약 너가 특정하게 다르게 모양이 생긴 boat hulls의 행동을 모델링 하는 물리엔진을 설계하지 않는다면, 너는 이러한 수준의 세부사항을 필요하지 않을 가능성이 높다.
대신에, 우리는 근사로서 springlike calculation을 사용할 수 있다. 그 오브젝트가 표면에 가까워 졌을 때, 우리는 그것에 부력을 주기위해 spring force를 사용한다. 그 힘은 그 오브젝트의 깊이에 비례한다. spring force가 그 스프링의 확장(extension)또는 압축(compression)에 비례하듯이. 우리가 그림 6.3에서 보듯이, 이것은 완전히 가라앉지 않은 사각형 블럭에 대해서 정확할 것이다. 어떤 다른 오브젝트에 대해, 그것은 다소 부정확할 것이다. 그러나 눈에 띌만큼 충분하지 않다.
그 블럭이 완전히 가라앉았을 때, 그것은 다소 다른 방식으로 행동한다. 그것을 물 속으로 더 깊게 밀어 넣는 것은 어떠한 물도 더 이상 옮기지 않을 것이다. 그래서 우리가 물이 같은 밀도를 갖는다고 가정하는한, 그 힘은 같을 것이다. 우리가 책의 이 부분에서 다루고 있는 그 point-masses는 어떠한 크기도 가지고 있지 않다. 그래서 우리는 그것들이 완전히 잠겼는지를 결정하기 위해 그것들이 얼마나 큰지를 구분할 수 없다. 우리는 대신에 간단히 고정 깊이를 사용할 수 있다: 우리가 부력을 만들 때, 우리는 한 깊이를 주는데, 거기에서 그 오브젝트는 완전히 잠긴것으로 고려된다. 이 지점에서, 부력은 더욱 깊은 잠수에 대해서 증가하지 않을 것이다.
대조적으로, 그 오브젝트가 물에서 떠오를 때, 그것이 수면 위의 그것의 최대 잠수 깊이에 도달할 때 까지 그것의 일부가 여전히 잠수되어 있을 것이다. 이 지점에서, 그 오브젝트의 마지막 부분은 물에서 떠나 있다. 이 경우에, 더 이상 부력은 없다. 우리가 그 오브젝트를 얼마나 높이 들어올릴 지라도: 그것은 간단히 어떠한 물도 옮기고 있지 않다.
그 힘을 계산하는 공식은 그러므로 다음과 같다:
여기에서 s는 잠수 깊이이고 (그 오브젝트가 완전히 잠겨있는 깊이), p는 액체의 밀도, v는 그 오브젝트의 부피이고, d는 그 오브젝트가 잠수된 양이다. 그 양은 그것의 최대 잠수 깊이에 비례하여 주어진다 (즉, 그것이 완전히 잠겼을 때, d >= 1이고, 그것이 완전히 물 밖일 때 d <= 0이다):
여기에서 y_o는 그 오브젝트의 y좌표이고, y_w는 수면의 y좌표이다 (그것이 XZ 면에 평행하다고 가정한다). 이것은 이것처럼 구현되어질 수 있다:
/** * A force generator that applies a buoyancy force for a plane of * liquid parallel to XZ plane. */ class ParticleBuoyancy : public ParticleForceGenerator { /** * The maximum submersion depth of the object before * it generates it maximum buoyancy force */ real maxDepth; /** * The volume of the object */ real volume; /** * The height of the water plane above y = 0. The plane will be * parallel to the XZ plane. */ real waterHeight; /** * The density of the liquid.Pure water has a density of * 1000 kg per cubic meter. */ real liquidDensity; public: /** Creates a new buoyancy force with the given parameters */ ParticleBuoyancy(real maxDepth, real volume, real waterHeight, real liquidDensity = 1000.0f); /** Applies the buoyancy force to the given particle.*/ virtual void updateForce(GPEDParticle* particle, real duration); }; void GPED::ParticleBuoyancy::updateForce(GPEDParticle * particle, real duration) { // Calculate the submersion depth. real depth = particle->getPosition().y; // Check if we're out of the water. if (depth >= waterHeight + maxDepth) return; glm::vec3 force(0.0); // Check if we're at maximum depth. if (depth <= waterHeight - maxDepth) { force.y = liquidDensity * volume; particle->addForce(force); return; } // Otherwise we are partly submerged. force.y = liquidDensity * volume * (depth - maxDepth - waterHeight) / 2 * maxDepth; particle->addForce(force); }
이 코드에서, 나는 부력이 위 방향으로 작용한다고 가정한다. 나는 Hook's law에 대해 스프링 길이를 계산하기 위해 오직 오브젝트의 위치의 y component만을 사용했다. 그 generator는 4개의 파라미터를받는다: maximum depth paraemeter, 오브젝트의 부피, 수면의 높이, 그리고 뜨고있는 액체의 농도. 만약 어떠한 농도 파라미터가 주어지지 않는다면, 그러면 1000kg/m^3의 농도를 가진 물이 가정된다 (바닷물은 약 1020 ~ 1030 kg/m^3의 농도를 가졌고, 사해(Dead Sea)는 1250kg/m^3의 농도를 가진다).
그 generator는 오직 한 오브젝트에게만 적용된다. 왜냐하면 그것은 object의 사이즈와 volume에 대한 데이터를 포함하기 때문이다. 한 instance는 같은 사이즈와 부피를 가진 많은 오브젝트들에 주어질수 있다. 같은 액체로 뜨는, 그러나 혼란을 피하기 위해서 오브젝트마다 새로운 인스턴스를 만드는 것이 가장 최상이다.
6.3 Stiff Springs
실제 세계에서,거의 모든 것은 스프링으로서 작동한다. 만약 한 바위가 땅에 떨어진다면,그 땅은 매우 뻣뻣한 스프링처럼 한다. 스프링 행동의 한 모델로, 우리는 어떤 것을 재현할 수 있다. 오브젝트들 사이의 충돌들은 부력과 유사한 방식으로 모델링 될 수 있다: 그 오브젝트들은 또 다른 것에 넘겨지는 것이 허용된다 ("interpenetration"), 그리고 spring force는 그것들을 다시 밀어낼것이다.
각 오브젝트에 대한, 정확한 spring parameters와 함께, 이 방법은 우리에게 완벽한 충돌을 준다. 그것은 "penalty" method 라고 불려지고, 많은 물리 simulators에 사용된다. 게임에서 사용된 몇 가지를 포함해서.
만약 삶이 너무 간단하다면, 이 책이 이 백 페이지보다 더 짧았을 것이다. 사실, 물건이 질척거리는 스프링에서 튕길 때 게임에 있는 모든 것이 스펀지처럼 보이는 것을 피하기 위해서 우리는 spring constant를 증가시켜야한다. 이것으로 그것이 매우 높게 되기 위해서이다. 만약 너가 그것을 시도하고, 엔진을 작동시킨다면, 너는 모든 것이 뒤섞이는 것을 볼 것이다: 오브젝트들이 거의 즉시 무한대로 사라질 것이고, 너의 프로그램은 numerical errors로 충돌할것이다. 이것은 stiff springs의 문제이고, penalty methods를 거의 우리의목적에 쓸모없게 만든다.
6.3.1 The Problem of Stiff Springs
stiff springs이 문제들을 일으키는 이유를 이해하기 위해, 우리는 스프링의 행동을 짧은 time steps로 쪼갤 필요가 있다. 그림 6.4는 몇 가지 시간 단계에 대한 스프링의 행동을 보여준다. 첫 번째 단계에서, 그 스프링은 확장되고, 그리고 우리는 그 시점에서 그 힘을 계산한다.
그 힘은 chapter 3의 다음의 업데이트 함수를 사용하여 스프링의 끝에 적용된다:
다시 말해서, 그 힘은 가속도로 변환된다: 그 instant of time에서의 스프링의 끝의 가속도이다. 이 가속도는 그러고나서 그 오브젝트에 전체 시간 interval에 대해 적용된다. 이것은 만약 그 오브젝트가 움직이지 않고 있었다면 정확할 것이다 - 즉, 만약 그 스프링이 전체 시간 주기동안 constant extension에서 유지된다면.
실제 세계에서, 그 스프링이 조금 움직이자마자, 시간 interval의 아주 작은 부분 이후에, 그 힘은 다소 감소할 것이다. 그래서 전체 time interval에 대해 같은 힘을 적용하는 것은 너무 많은 힘을 적용하는 것이다. 그림 6.4에서 우리는 이것이 많이 중요하지 않다고 본다. 비록 그 힘이 너무 클지라도, 그 끝ㅇ,ㄴ 그 다음 시간 프레임 전에 너무 멀리 가지 않는다. 그러고나서 더 작은 힘이 다음 타임 프레임에 적용된다. 등등. 그 전반적인 표과는 스프링이 정상적으로 행동하지만, 그것은 우리가 명시했던 spring constant보다 더 stiffer하다.
그림 6.5는 같은 문제를 보여주지만, 더욱 뻣뻣한 스프링의 문제를 보여준다. 이제 첫 번째 프레임에서의 그 힘은 스프링의 끝이 rest length를 지나고 옮길만큼 충분하고, 스프링을 압축시킬만큼 충분하다. 현실에서, 그 스프링의 움직임은 이렇지 않다: 그것은 안쪽으로 움직이기 시작하고, 적용되는 큰 즉각적인 힘을 갖게되고, 하지만 이 힘은 그 끝이 안으로 들어올 때 급격히 떨어진다.
그 그림은 스프링이 그것이 원래 연장되는 것 보다 더 압축된 것을 보여준다. 다음 프레임에서, 그것은 반대 방향으로 움직이지만, 심지어 적용될 더욱 큰 힘을 갖는다. 그래서 그것은 너무 멀리 쏘게되고, 더 멀리 연장된다. 매 시간 프레임에서, 그 스프링은 더 큰 힘을 가지고 oscillate한다. 그 스프링의 끝이 infinity에 도달할 때 까지, 명백히 이것은 정확하지 않다.
우리가 사용하는 시간 프레임이 더 길어질수록, 이것이 발생할 확률이 더 높다. 만약 너의 게임이 스프링을 사용하고 변할 수 있는 프레임율을 사용한다면, 너는 너의 스프링 상수가 매우 느린 기계에서 사용될 때 너무 크지 않게 신경쓸 필요가 있다. 만약 한 플레이어가 모든 그래픽스 옵션을 바꾸고, 그녀의 머신을 초당 10 프레임으로 낮춘다면 (또는 더 늦게), 너는 모든 너의 물리들이 폭발하길 원하지 않을 것이다!
우리는 작은 시간 주기를 업데이트를 위해 가용함으로써 이 문제를 어느정도 해결할 수 있다. 또는 우리는 우리가 렌더링하는 각 프레임에 대해 몇 가지 더 작은 업데이트를 사용할 수 있다. 그러나, 두 접근법 모두 우리에게 많은 것을 주지 않는다. 현실적인 충돌들을 재현하는데 필요한 spring stiffness의 종류들은 우리가 지금까지 구성한 프레임워크에서는 가능하지 않다.
대신에 우리는 충돌과 다른 hard constraints들을 재현할 대안의 방식들을 사용할 것이다.
6.3.2 Faking Stiff Springs
이 섹션은 stiff springs에 도움을 주기 위해, spring forces를 계산하는 것의 다른 방법을 사용하는 좀 더 고급 spring force generator를 구현할 것이다. 그것은 stiff springs을 작동시키는것 에 대한 "cheat"를 제공한다. 이 책의 나머지 챕터에서, 우리는 constraints, collisions, 그리고 contacts를 재현하는데 좀 더 튼튼한 기법들을 볼 것이다.
너는 안전하게 이 섹션을 건너띌 수 있다: 그 수학들은 세부적으로 폭발하지 않는다; 우리가 fake stiff springs를 사용할 수 있는 곳이 제한이 있다. 그리고 그 공식화는 항상 작동한다고 보장되지 않는다. 특히, 그것들이 효과를 합리적으로 fake할 수 있는동안, 한 개 이상이 결합되거나 또는 일련의 오브젝트들이 그것들에 연결될 때, 그 계산에서 물리적 부정황석이 몹시 더럽게 상호작용하고 심각한 문제를 일으킨다. 그러나, 올바른 상황에서, 그것들은 force generator의 너의 라이브러리에 유용한 addition이 될 수 있다.
우리는 그 힘이 시간 interval에 대해 어떻게 변할지를 예측하여 어느정도로 이 문제를 해결할 수 있다. 그리고 평균 힘을 생성하기 위해 그것을 사용할 수 있다. 이것은 가끔씩 "implicit spring"이라고 불려지고, 이 방식으로 다양한 힘을 다룰 수 있는 물리엔진은 implicit or semi-implicit이다. 우리가 그 챕터의 마지막에 볼 이유 때문에, 우리는 그 correct behavior를 fake하는 것 이상을 할 수 없다. 그래서 나는 이 접근법을 "fake implicit force generation"이라고 부른다.
그 힘 방정식을 처리하기 위해, 우리는 만약 그것 자신의 장치에 남는게 있다면 한 스프링이 어떻게 작동하는 지를 이해할 필요가 있다.
Harmonic Motion
마찰 또는 저항이 없는 스프링은 영원히 oscillate한다. 만약 우리가 그러한 스프링을 어떠한 extension으로 펼치고, 그러고나서 그것을 놓아준다면, 그것의 끝은 함께 가속할 것이다. 그것은 그것의 natural length를 넘기고, 압축하기 시작한다. 그것의 끝이 그것들이 초기에 확장된 것과 정확히 같은 정도로 압축되었을 때, 그것은 따로 가속하기 시작한다. 이것은 영원히 계속된다. 이러한 종류의 움직임은 물리학자에게 잘 알려져있다; 그것은 "simple harmonic motion(단진동)"라고 불려진다. 그 스프링의 한 끝의 위치는 다음의 방정식을 따른다
여기에서 k는 spring constant이고, m은 그 물체의 질량이고, x는 다음의 방정식에서 편의성을 위해 정의된다.
이러한 종류의 방정식은 "differential equation(미분 방정식)"이라고 불려진다. 그것은 다른 격차들을 연결한다 가끔씩은 원래의 양으로: 이 경우에 이차 미분 \ddot{p}과 원래 p이다. 미분 방정식은 가끔씩 그냥 원래의 양에 대한 표현을 주기위해 해결될 수 있다. 우리의 경우에, 그 방정식은 우리에게 위치와 현재 시간을 연결시키는 표현을 주기위해 해결될 수 있다. 그 표현은 다음을 주기위해 해결된다
여기에서 p_0은 그 예측을 시작하는 곳에서 natural length를 기준으로 한 스프링의 끝 위치이다. \dot{p}_0은 같은 시간에 속도이다.
우리는 우리가 관심이 있는 시간 간격을 방정식 6.4로 대체할 수 있다(즉, 현재 프레임의 duration), 그리고 만약 스프링이 그것의 할 일을 하도록 냅둔다면 스프링이 어디서 끝날지를 처리할 수 있다. 우리는 그러고나서 그것이 frame의 duration동안 올바른 위치에 도착하게할만큼 충분한 큰 힘을 만들어낼 수 있다. 만약 그 최종 위치가 p_t일 필요가 있다면, 그러면 그것에 도달하기 위한 힘은 이렇게 된다.
그리고 그 가속도 \ddot{p}은 다음으로 주어진다
[6.5]
비록 이것이 파티클을 올바른 장소로 가게 할지라도, 그것은 올바른 스피드로 거기에 가지 못한다는 것을 주목해라. 우리는 섹션의 끝에서 이것이 실패함으로써 야기되는 문제들에 돌아갈 것이다.
Damped Harmonic Motion
진짜 스프링은 스프링 힘뿐만 아니라 drag도 경험한다. 그 스프링은 같은 지점으로 영원히 속 진동하지 않는다. 그것의 최대 확장은 매번 덜 될 것이다. 결국 그것이 rest length에 고정될 때 까지. 이 점진적인 감소는 스프링이 경험하는 drag에 의해 야기된다.
우리가 우리의 물리 엔진을 보통 작동시킬 때, 그 drag는 damping parameter에 통합될 것이다. 우리가 이전의 공식을 사용하여 스프링의 행동을 예측할 때, 이것은 발생하지 않는다.
우리는 damped harmonic oscillator를 주기위해 방정식에서 damping을 포함할 수 있다. 그 미분방정식 6.3은 다음이 된다
여기에서 k는 스프링 상수이고 (이 경우에 x가 필요없다) 그리고 d는 drag 계수이다 (그것은 이전 챕터에서 방정식 .1의 k_1계수와 부합된다). 이 방정식은 제곱된 속도에 비례하는 drag를 허용하지 않는다 - 즉, 방정식 5.1의 k_2 값을 말한다. 만약 우리가 이것을 추가했다면, 그 수학은 상당히 거의 보이지 않는 개선을 위해서 복잡해질 것이다 (끼억해라, 우리는 이것을 어떤 경우에 faking하고 있다는 것을) 그래서 우리는 가장 간단한 종류의 drag를 고수한다.
미분 방정식을 푸는 것은 미래에 어떤 시간에 대한 위치의 표현을 준다:
여기에서 gamma는 다음에 의해 주어진다
c는 다음에 의해 주어진다
이 방정식에서 이전처럼 시간 간격(time interval)을 t로 대체하여, 우리는 p_t에 대한 값을 얻을 수 있고, 우리가 regular harmonic motion에서도 했듯이 방정식 6.5를 사용하여 가속도를 계산할 수 있다.
Implementation
fake implicit spring force generator를 구현하는 코드는 이렇게 보인다:
/** * A force generator that fakes a stiff spring force, and where * one end is attached to a fixed point in space. */ class ParticleFakeSpring : public ParticleForceGenerator { /** The location of the anchored end of the spring */ glm::vec3* anchor; /** Holds the spring constant */ real springConstant; /** Holds the damping on the oscillation of the spring. */ real damping; public: /** Creates a new spring with the given parameters */ ParticleFakeSpring(glm::vec3* anchor, real springConstant, real damping); /** Applies the spring force to the given particle */ virtual void updateForce(GPEDParticle* particle, real duration); }; void GPED::ParticleFakeSpring::updateForce(GPEDParticle* particle, real duration) { // Check that we do not have infinite mass if (!particle->hasFiniteMass()) return; // Calculate the relative position of the particle to the anchor glm::vec3 position = particle->getPosition(); position -= *anchor; // Calculate the constants and check whether they are in bounds. real gamma = real(0.5) * real_sqrt(4 * springConstant - damping * damping); if (gamma = real(0.0)) return; glm::vec3 c = position * (damping / (real(2.0) * gamma)) + particle->getVelocity() * (1.0f / gamma); // Calculate the target position glm::vec3 target = position * real_cos(gamma * duration) + c * real_sin(gamma * duration); target *= real_exp(-0.5f * duration * damping); // Calculate the resulting acceleration and therefore the force glm::vec3 accel = (target - position) * (real(1.0) / duration * duration) - particle->getVelocity() * duration; particle->addForce(accel * particle->getMass()); }
그 force generator는 우리가 이 챕터에서 이전에 만든 anchored regular spring genrator와 비슷하게 보인다. 한 가지 중요한 차이점이 있다 : 그것은 더 이상 natural spring length를 갖지 않는다. 이것, 우리가 두 오브젝트를 붙일 수 있는 한 스프링 보다 anchored genrator를 사용하는 사실은 여기에서 사용되는 수학의 어떤 결과이다. 그 결과는 우리가 항상 zero인 rest length를 가져야 하는 것이다.
Zero Rest Lengths
만약 한 스프링이 zero rest length를 갖는다면, 그 스프링의 한 끝의 어떤 변위는 그 스프링의 extension을 만들어낸다. 만약 우리가 그 스프링의 한 쪽 끝을 고친다면, 그러면 항상 anchored된 쪽 끝 방향으로 힘이 있을 것이다.
스프링의 양쪽 끝이 움직이는 것이 허용되는 스프링에 대해, 힘의 방향은 결정하기에 더 어렵다. 이전의 공식은 그 힘이 그 오브젝트의 위치만을 관점으로 표현될 수 있다고 가정했다. 만약 우리가 그 스프링을 고정시키지 않았다면, 그러면 우리는 방정식에서 스프링의 다른 쪽 끝의 움직임을 포함시켜야만 한다. 그리고 그것을 해를 구할 수 없는 것이다.
만약 우리가 한 쪽 끝을 고정시키지만, non-zero rest length를 사용한다면 유사한 문제가 발생한다. 1차원에서, non-zero rest length는 평형점을 조금씩 움직이는 것과 같다. 그림 6.6에서 보여지듯이. 그 같은 것이 3차원에도 해당하지만, 스프링은 자유롭게 swivel하도록 허용되기 때문에, 평형점은 이제 같은 문제를 가지고 움직인다. non-anchored spring에 대해.
그래서 그 방정식은 한 오브젝트가 미리 결정되고 고정된 위치를 유지하는데 잘 작동한다.
이전의 anchored springs에 대해, 우리는 이 위치를 수동으로 프레임마다 움직일 수 있었다. 우리가 그 force generator가 그것의 예측한 움직임을 처리하는 것을 기대하지 않는한.
Velocity Mismatches
지금까지 우리는 오직 위치에 대해서만 이야기 했다. 방정식 6.5는 오브젝트가 그것의 예상된 위치로 도달하는데 필요한 힘을 계산한다. 불행하게도, 그것은 정확한 속도로 도달하지 못한다 (비록 그것이 종종 가까울지라도). 이 방정식은 결국에 매번 그 오브젝트의 속도를 증가시키고, 더 빨라져서 무한으로 폭발해버리는가?
damped harmonic motion에 대해, 고정점이 움직이지 않을 때, 이러한 종류의 예측을 수행하는 그 속도는 결코 이것을 달성하는데 쌓아지지 않을 것이다. 이것을 보여주는데 관련된 수학은 복잡하다. 그래서 나는 그것을 비관주의자를 위한 exercise로 남겨둘 것이다.
비록 우리가 폭발하는 속도를 얻지 않을지라도, 최종 속도와 정확한 속도 사이의 mismatch는 스프링이 일관되지 않은 스프링 상수로 행동하도록 야기시킨다. 가끔씩, 그것은 우리가 명시했던 것보다 더 뻣뻣하다. 가끔씩 그것은 더 헐렁해질 것이다. 대부분의 경우에 그것이 눈치챌만하지 않지만, 우리가 했던 방식에서 힘을 faking하는 것의 피할 수 없는 결과이다.
Interacting with Other Forces
faked spring generator의 또 다른 중대한 한계는 그것이 다른 힘들과 작용하는 방식이다.
이전의 방정식은 그 오브젝트가 자유롭게 움직이는 것을 가정한다. 어떠한 힘의 영향하에서가 아닌. 그 스프링 힘은 시간 간격의 과정에서 줄어들것이다. 왜냐하면 그 스프링이 그것의 rest length를 향해서 움직이기 때문이다. 만약 우리가 스프링이 constant length로 확장되거나 압축되도록 유지하는 또 다른 힘을 가진다면, 그러면 그 힘은 상수가 될 것이고, 원래 force generator는 완벽한 결과를 줄 것이다. 그 스프링 constant가 무엇이드 ㄴ간에.
우리는 이론적으로 모든 다른 힘을 스프링 generator에 관한 예측에 통합시킬 수 있고, 그러고나서 그것은 정확히 정확한 힘을 반환할 것이다. 불행하게도 그 힘을 정확히 처리하기 위해서, 우리는 재현될 모든 오브젝트의 행동을 알 필요가 있다. 모든 오브젝트의 행동을 재현하는 것은 물론 물리 엔진의 전체 목적이다. 그래서 우리가 이것을 작동시키는 유일한 방식은 force calculations에서 full physics engine을 넣는 것이다. 이것은 실용적이지 않다 (사실, 엄격히 말해서, 불가능하다. 왜냐하면 그 엔진에서, 우리는 다른 것이 필요하다, ad infinitum).
확장되도록 유지하려고 의도되는 스프링에 대해 (그 챕터에서 이전에 우리의 rope-brige
유지하는 스프링 같은 것), faked spring forces들은 너무작을 것이고, 종종 상당이 너무 작을 것이다. 실제로, 너가 원하는 효과를 얻기위해서 기법들을 섞는 것을 차즌ㄴ게 가장 좋다. 게임에서 다른 오브젝트들에 대해 다른 스프링 force generators를 사용하여
나는 이 faked force generator를 성공적으로 캐릭터의 머리카락을 스프링으로 모델링하는데 사용했다 (그리고 다른 흔들리는 몸의 부분들). 그 rest position은 3D model에서 hair-vertex의 원래 위치에 의해 주어지고, 그 spring force는 이 rest position에 대한 실제 drawn vertex를 attract한다. 캐릭터가 움직임에 따라, 그녀의 머리카락은 자연스럽게 까딱거린다. 이 방식은 이상적으로 그 문제에 적합하다. 왜냐하면 그 정점들은 그것들에 어떤 다름 힘도 가지지 않기 때문이다 (중력에의한 자연스러운 "flop"은 model design에 의해 통합되었다), 그리고 그것들은 너무 튕겨보이는 것처럼 보이는 것을 피가히 위해 매우 높은 스프링 계수를 가질 필요가 있다. hairbounce demo는 이것의 간단한 예제를 준다.
6.4 Summary
놀랍게도 많은 물리효과들은 Hook's law를 사용하여 모델링 되어질 수 있다. 부력과 같은 몇 가지 효과들은 같은 코드를 사용하여 간단하게 지원되는 스프링에 대해 그러한 간단한 특징을 갖는다.
우리는 탄성이있거나 부력이 있어보이는 어떤것을 모델링하기 위해 그 책의 나머지에서 사용될 수 있는 force generator set을 만들었다. 그러나, 우리는 또한 그 책의 나머지 전체를 동기부여시키는 한 문제의 시작을 보았다: 높은 스프링 상수를 가진 스프링들 (즉, 빠르고 강한 bounce를 가진 것들)이 frame-by-frame basis로 재현하기 어렵다는 것이다. 그 스프링의 행동이 재현된 프레임 마다의 시간보다 더 빠를 때, 그 스프링은 다루기 힘들어지고, 통제에 벗어날 수 있다.
만약 이 문제가 없다면, 우리는 거의 springlike forces를 사용하여 어떤 것이든 재현할 수 있다. 예를들어 모든 충돌들은 쉽게 다루어질 수 있다. 비록 우리가 어떤 경우에 stiff springs를 fake할 수 있을지라도, 그 솔루션은 일반적인 경우의 stiff springs를 처리할만큼 충분히 튼튼하지 않다. 그래서 우리는 대안의 접근법을 발견할 필요가 있다 (상당히 좀 더 복잡한 코드를 폼하ㅏ여) 매우 빠르게 충돌하는 bounce를 처리하기 위해. Chapter 7은 이것을 본다 : collisions과 hard constraints를 처리하고기 위한 특별 케이스 코드의 한 집합. 막대나 탄성이 없난 케이블같은.
댓글 없음:
댓글 쓰기