If you're seeing this message, it means we're having trouble loading external resources on our website.

웹 필터가 올바르게 작동하지 않으면 도메인 *. kastatic.org*.kasandbox.org이 차단되어 있는지 확인하세요.

주요 내용

공기 및 유체 저항

비행기 주변 유체의 저항
물체가 액체나 기체를 통과할 때 역시 마찰이 발생합니다. 이 힘은 여러 이름들로 불리는데 점성력, 항력, 유체 저항등이 있습니다. 결과는 이전 마찰 예제들과 동일하지만 (객체의 속도가 느려집니다) 항력을 계산하는 방법은 약간 다릅니다. 다음 공식을 살펴봅시다.
Fd=12ρv2ACdv^
이제 식을 각 성분별로 나눈 후 보다 간단한 식으로 만들면서 ProcessingJS에서 효과적인 시뮬레이션을 위해 실제로 필요한 것이 무엇인지 살펴보겠습니다.
  • Fd는 항력을 뜻합니다. 궁극적으로 계산해서 applyForce() 함수에 전달할 벡터입니다.
  • -1/2는 상수입니다. -0.5.와 같습니다. ProcessingJS에서는 어차피 다른 상수에 원하는 아무 값이나 넣을 것이기 때문에 이 값에 큰 의미는 없습니다. 하지만 상수가 음수라는 사실은 중요합니다. 마찰력처럼 힘이 속도와 반대 방향이라고 알려주기 때문입니다.
  • ρ는 그리스 문자 ,라고 하고 액체의 밀도를 나타내는데, 여기서는 신경 쓰지 않아도 됩니다. 문제를 간단히 하기 위해 이를 상수의 값 1이라고 가정합니다.
  • v는 물체가 움직이는 속력을 나타냅니다. 물체의 속력은 속도 벡터의 크기 velocity.mag()와 같습니다. 그리고 v2v의 제곱, 혹은 vv를 의미합니다.
  • A는 액체나 기체를 통과하는 물체의 앞면 면적을 의미합니다. 예를 들어 공기 역학적으로 설계된 람보르기니는 박스 모양의 볼보보다 적은 공기 저항을 경험합니다. 어찌 됐든 간단한 시뮬레이션에서는 물체가 공 모양이라고 가정하고 이것을 무시할 수 있습니다.
  • Cd는 항력계수이고, 마찰력 계수 μ와 같습니다. 이 상수로 항력이 센지 약한지 결정할 수 있습니다.
  • v^는 속도의 단위 벡터 velocity.normalize()를 의미합니다. 마찰력처럼 항력도 속도의 반대 방향을 가리킵니다.
이제 각 요소를 분석하고 간단한 시뮬레이션에서 필요한 부분을 결정했으니 식을 다음과 같이 간단히 할 수 있습니다:
간단히 한 식: F_drag = ||v^2|| * c_d * v - 1
또는
// 식의 첫 번째 부분 (크기): v^2 * Cd
var c = 0.1;
var speed = velocity.mag();
var dragMagnitude = c * speed * speed;

// 식의 두 번째 부분 (방향): v unit vector * -1 
var drag = velocity.get();
drag.normalize();
drag.mult(-1);

// 크기와 방향을 합친 식
drag.mult(dragMagnitude);
이 힘을 Mover 객체형에 한 가지를 추가하여 구현해 봅시다. 마찰에 관한 예제를 코딩할 때 마찰력이 항상 나타나도록 했습니다. 그래서 물체가 움직일 때마다 마찰력으로 인해 느려졌습니다. 이제 Mover 객체가 헤엄칠 “액체”를 추가합시다. Liquid 객체는 직사각형이며 위치, 너비, 높이, “항력 계수”에 대한 프로퍼티를 갖습니다. 항력계수는 물체가 (공기같이) 통과하기가 쉽거나 (시럽같이) 통과하기 어려운 정도를 말합니다. 추가로 자신을 화면에 출력하는 메소드 (그리고 곧바로 살펴볼 두 개의 함수)를 포함시켜야 합니다.
var Liquid = function(x, y, w, h, c) {
  this.x = x;
  this.y = y;
  this.w = w;
  this.h = h;
  this.c = c;
};

Liquid.prototype.display = function() {
  noStroke();
  fill(50);
  rect(this.x, this.y, this.w, this.h);
};
이제 메인 프로그램에서 새로운 Liquid객체의 인스턴스를 선언하고 초기화합니다. 계수는 (0.1) 정도로 낮게 설정한 것을 보세요. 이렇게 하지 않으면 물체는 꽤 빨리 멈출 것입니다 (언제가는 이 효과가 필요할 수 있습니다).
var liquid = new Liquid(0, height/2, width, height/2, 0.1);
이제 흥미로운 생각이 드는군요. Mover 객체가 Liquid 객체에게 어떻게 말을 걸까요? 다시 말하자면 다음과 같은 동작을 수행해야 합니다.
Mover가 액체를 통과할 때 항력을 받습니다.
객체지향적 프로그래밍 관점에서 말하자면 (인덱스 i를 갖는 Mover 객체의 배열에서 반복문을 수행한다고 가정하면) 다음과 같은 코드로 나타낼 수 있습니다.
//Mover가 액체에 있습니까?
if (liquid.contains(movers[i])) {
    // 항력을 계산합니다.
    var dragForce = liquid.calculateDrag(movers[i]);
    // Mover에 항력을 적용합니다.
    movers[i].applyForce(dragForce);
}
위 코드에 의하면, Liquid 객체형에 두 가지 함수를 추가해야 합니다. (1) Mover 객체가 Liquid 객체에 있는지를 결정하는 함수와 (2) Mover 객체에 가해지는 항력을 계산하는 함수가 필요합니다.
1번 함수는 쉽습니다. 단순히 조건문을 이용하여 위치 벡터가 액체로 정의된 직사각형 내에 있는지를 결정하면 됩니다.
Liquid.prototype.contains = function(m) {
    var p = m.position;
    return p.x > this.x && p.x < this.x + this.w &&
         p.y > this.y && p.y < this.y + this.h;
};
drag() 함수는 약간 더 복잡하긴 하지만 이 코드는 이미 한 번 쓴적이 있습니다. 이 코드는 단순히 수식을 구현한 것입니다. 항력은 항력의 계수와 속도의 반대 방향으로 움직이는 Mover의 속도의 제곱을 곱한 것과 동일합니다.
Liquid.prototype.calculateDrag = function(m) {
  // 크기는 계수 * 속도의 제곱
  var speed = m.velocity.mag();
  var dragMagnitude = this.c * speed * speed;

  // 방향은 속도와 반대
  var dragForce = m.velocity.get();
  dragForce.mult(-1);

  // 알맞은 크기로 조정 
  dragForce.normalize();
  dragForce.mult(dragMagnitude);
  return dragForce;
};
Liquid 객체형에 위의 두 함수를 넣어 프로그램을 만들어 보면 다음과 같습니다:
프로그램을 실행하면 물속으로 공이 떨어지는 것을 볼 수 있습니다. 화면 아래 파란색 영역(액체를 나타냅니다)을 통과해서 지나가면 물체가 더 느려집니다. 또한, 작은 물체는 큰 물체보다 더 급격하게 느려지는 것을 볼 수 있습니다. 뉴턴의 제2 법칙을 기억하시나요? A = F / M입니다. 가속도는 힘을 질량으로 나눈 값과 같습니다. 물체의 질량이 클수록 가속도가 작습니다. 작은 물체는 빨리 가속합니다. 여기에서 언급하는 가속도는 항력으로 인해 점점 “느려지는" 가속도입니다. 작은 물체는 큰 물체보다 훨씬 더 높은 비율로 느려질 것입니다.

본 "내추럴 시뮬레이션" 과정은 다니엘 쉬프만(Daniel Shiffman)이 저술한 "The Nature of Code"의 내용을 차용한 것이며, 본 내용물의 저작권은 Creative Commons Attribution-NonCommercial 3.0 Unported License를 적용합니다.