
코딩 어드벤처: 행성 유체 시뮬레이션 (Planetary Fluid Sim)
요약
Sebastian Lague가 행성 중심의 중력을 적용한 유체 시뮬레이션 구현 과정을 다룹니다. 뉴턴의 중력 법칙을 코드로 구현하여 입자의 가속도를 계산하고, 행성의 구형 표면을 가정하여 충돌 감지를 준비하는 과정을 보여줍니다.
핵심 포인트
- 행성 중심을 향하는 가변적 중력 벡터 구현
- 뉴턴의 만유인력 법칙을 활용한 입자 가속도 계산
- 입자 질량과 중력 가속도 간의 관계 적용
- 구형 행성 모델을 통한 충돌 감지 단순화
비디오: 코딩 어드벤처: 행성 유체 시뮬레이션 (Planetary Fluid Sim)
채널: Sebastian Lague
길이: 27분 22초
출처: 자막 (수동, en-GB)
전사(Transcript):
안녕하세요 여러분, Coding Adventures의 새로운 에피소드에 오신 것을 환영합니다! 지난 영상 마지막에, 저는 여기 있는 이 유체 시뮬레이션 (fluid simulation)의 비주얼과 동작을 개선하고 싶은 몇 가지 사항들을 언급했었는데요, 오늘은 그중 어느 것도 다루지 않을 예정입니다. 왜냐하면 시뮬레이션을 행성 위에 구현해 보라는 제안을 계속 보고 있기 때문입니다. 그것이 잘 작동할지는 전혀 모르겠지만, 아주 재미있을 것 같으니 한번 시도해 봅시다!
이것이 현재 우리의 중력 (gravity) 함수입니다. 사실 함수라고 하기도 민망하네요. 항상 아래를 향하는 동일한 벡터를 반환하니까요. 그럼 각 유체 입자 (fluid particle)의 위치에 따라 중력이 변할 수 있도록 하여 조금 더 흥미롭게 만들어 봅시다. 어떤 행성의 중심을 정의한다면, '아래'라는 의미를 이제 그 중심점을 향하는 방향으로 재정의할 수 있습니다. 그리고 당분간은 중력이 새로운 아래 방향으로 매초 초당 10미터의 일정한 속도로 입자들을 가속한다고 가정해 봅시다.
좋습니다, 이것을 실행해서 어떤 일이 일어나는지 확인해 봅시다. 음, 이건... 의심스러울 정도로 이전과 비슷해 보이는데요. 아, 파일을 저장하는 것을 잊었네요. 자, 이제 가봅시다! 중앙에 거대한 더미 형태로 유체가 움직이고 있는데, 확실히 흥미로워 보입니다. 하지만 중력의 방향을 변화시키도록 허용했으면서, 아직 중력의 세기 (strength) 또한 변화할 수 있도록 허용해야 합니다. 그러니 두 물체 사이의 중력 (gravitational force)은 두 질량의 곱을 거리의 제곱으로 나눈 값에 어떤 상수를 곱한 것과 같다는 뉴턴 (Newton)의 관찰을 떠올려 봅시다.
또한 힘은 질량 (mass) 곱하기 가속도 (acceleration)와 같다는 것을 알고 있으므로, 이 질량들은 서로 상쇄됩니다. 이는 당연하게도 자신의 질량이 자신의 중력 가속도 (gravitational acceleration)에는 아무런 영향을 미치지 않으며, 오직 상대 물체의 질량에 달려 있음을 보여줍니다. 자, 이제 코드에서 각 유체 입자 (fluid particle)에 대한 가속도를 계산했습니다. 행성이 그에 반응하여 어떻게 가속되는지도 계산할 수 있겠지만, 입자 자체의 질량이 매우 작기 때문에 여기서는 그냥 무시하겠습니다.
참고로 행성의 질량은 완전히 임의적인 값인 5000으로 설정했습니다. 이 값이 적절한지는 모르겠지만, 한번 확인해 봅시다. 음, 보아하니 값이 좀 높네요! 질량을 줄이거나 중력 상수 (gravitational constant)를 조정할 수 있는데, 어느 쪽이든 상관없으니 다시 시도해 봅시다. 좋습니다, 이번에는 훨씬 나아 보이네요. 비록 일부 입자들이 매우 에너제틱하게(energetically) 빠르게 움직이고 있긴 하지만, 이는 보이지 않는 행성의 질량이 중심점에 집중되어 있다는 단순화된 가정 (simplifying assumption)을 하고 있기 때문입니다.
하지만 아직 유체가 충돌할 표면이 없기 때문에, 일부 입자들이 가속도가 당연히 엄청나게 큰 중심부 아주 가까이로 지나가게 되고, 그 결과 한 프레임과 다음 프레임 사이에 멀리 튕겨 나가 버립니다. 재미 삼아, 우리가 만든 스크린 공간 렌더링 (screen-space rendering) 기술을 적용하면 어떻게 보일지 확인해 보고 싶은데, 제 생각에 꽤 멋져 보이네요. 어쨌든, 다음 단계는 입자들이 튕겨 나갈 표면을 만드는 것이어야 한다고 생각하며, 일단은 행성이 완벽한 구형 (spherical)이라고 가정하여 상황을 아주 단순하게 유지해 봅시다.
물론 그렇게 하면 충돌 감지 (collision detection)가 아주 쉽고 깔끔해집니다. 입자가 행성의 반지름 (radius) 안에 있는지 테스트하고, 만약 그렇다면 표면으로 튕겨 올려주기만 하면 됩니다. 또한 입자의 속도 (velocity) 중 얼마나 많은 양이 구의 중심을 향해 아래로 향하고 있는지 살펴보고, 이를 상향 방향으로 더해주어 상쇄시킬 수도 있습니다. 그렇지 않으면 입자가 결국 행성을 그대로 통과해 버릴 때까지 아래쪽 방향의 속도가 계속 누적될 것이라고 생각합니다.
어쨌든, 그것을 한번 시도해 봅시다. 행성의 반지름을 여기서 빠르게 설정하고 — 바로 실행해 보겠습니다! 유체가 행성에 부딪히고 전체를 빠르게 감싸 안은 뒤, 반대편에서 멋진 분수처럼 터져 나오는 것을 볼 수 있습니다. 또한 반대편으로 되돌아가는 큰 파도도 있으며, 저 멀리 반대쪽에서 튀어 오르는 물방울들도 겨우 식별할 수 있습니다. 이제 파도가 다시 돌아오고 있으므로, 여기서 또 다른 분수가 나타날 것이며, 아마도 이 과정은 분수들이 마침내 사그라들 때까지 앞뒤로 계속 반복될 것입니다.
그렇게 되면 유체를 다시 움직이게 할 요소가 아무것도 없게 되어 조금 지루해지므로, 마치 끝없는 물의 줄다리기처럼 여러 천체 (bodies)가 서로 궤도를 돌 수 있도록 설정하고 싶습니다. 따라서 이러한 천체들을 위해 위치 (position), 속도 (velocity), 반지름 (radius), 그리고 질량 (mass)을 저장하는 작은 구조체 (structure)를 정의할 수 있습니다. 그런 다음 각 천체 쌍을 루프 (loop) 돌면서 서로에게 가하는 힘을 계산하고, 이를 가속도 (acceleration)에 적용하면 됩니다.
각 천체의 전체 가속도 (total acceleration)를 알게 되면, 다시 한번 루프 (loop)를 돌며 속도 (velocities)와 위치 (positions)를 업데이트할 수 있습니다. 이제 간단한 것을 설정해 봅시다. 예를 들어, 여기 빨간 행성이 있고 그 주위를 공전하는 작은 파란 달이 있다고 가정해 보죠. 자, 어떻게 보이나요? 음, 지금은 달이 행성에 그대로 충돌해 버리네요. 따라서 달이 멋진 궤도 (orbit)에 진입할 수 있도록 초기 속도 (initial velocity)를 어떻게든 조정해야 합니다. 이를 돕기 위해, 저는 시뮬레이션을 수천 단계 (steps) 앞의 미래로 실행하여 천체들이 이동할 경로 (paths)를 실제로 그려낼 수 있는 코드를 여기에 설정해 두었습니다.
그 덕분에 이제 초기 조건 (initial conditions)을 미세하게 조정하여 우리가 만족할 만한 결과를 얻기가 훨씬 쉬워졌습니다. 참고로 현재 달은 질량 (mass)이 거의 없어서 행성이 거의 움직이지 않지만, 질량을 조금 늘리면 이제 달이 행성을 자신 쪽으로 눈에 띄게 끌어당기는 것을 볼 수 있습니다. 따라서 이것을 실행하면, 두 천체가 함께 흔들리며 광활한 우주 속으로 나아가는 모습을 지켜볼 수 있습니다. 좋습니다, 이제 이 빨간 행성을 물로 덮을 예정입니다만, 그전에 먼저 우리의 작은 파란 달이 저 물 입자 (water particles)들에 어떤 영향을 미치는지 빠르게 시각화해 보고 싶습니다.
여기 구(sphere) 주변에 점들을 생성하는 이 작은 함수를 사용하여, 행성 표면에 일정 수의 테스트 점(test points)들을 도배할 수 있습니다. 그리고 이 각 점마다 달의 중력 영향 (gravitational influence)을 나타내는 작은 화살표들을 그리고 싶습니다. 그것이 바로 제가 여기서 빠르게 작업해 온 내용입니다. 즉, 행성 표면에 있는 이 모든 점들을 루프(loop)로 돌면서, 각 점에서의 달의 질량에 의한 가속도 (acceleration)를 계산하고, 이를 나타내는 작은 화살표들을 그리는 것입니다.
자, 결과가 어떻게 나왔는지 봅시다 — 음, 일부 화살표들은 행성을 향해 바로 꽂혀 있어서 보기가 조금 어렵네요. 그래서 약간의 여유를 가질 수 있도록 패딩 (padding) 파라미터를 추가했고, 화살표를 제대로 볼 수 있도록 길이를 스케일 업 (scale up) 할 수도 있게 했습니다. 하지만 분명히, 이 모든 화살표는 그저 달을 향해 직접적으로 가리키고 있기 때문에 그리 대단히 흥미롭지는 않습니다. 그래도 달의 반대편에 있는 화살표들이 달과 가까운 쪽의 화살표들보다 약간 더 짧다는 점은 주목할 수 있습니다. 당연히 이쪽(달과 가까운 쪽)에서 달의 인력이 아주 조금 더 강하기 때문입니다.
제가 정말 보고 싶은 것은 물이 행성 전체에 대해 상대적으로 어떻게 가속되는가 하는 점입니다. 그러니 아까 그 작은 함수로 돌아가서 행성의 가속도를 계산한 다음, 각 테스트 점에서의 가속도에서 이를 단순히 빼줌으로써 상대 가속도 (relative accelerations)만 남도록 해봅시다. 이제 어떻게 보이는지 확인해 보죠! 달과 가까운 쪽의 화살표들은 여전히 달을 향하고 있지만, 달의 반대편에 있는 화살표들은 이제 달로부터 멀어지는 방향을 가리키고 있는 것을 볼 수 있습니다. 왜냐하면 그곳은 행성 전체가 받는 인력보다 달의 인력을 아주 약간 덜 받기 때문입니다.
그 외에도, 행성의 나머지 부분에서 유체가 안쪽으로 뭉쳐지며 달과 정렬된 두 끝단 중 한 곳을 향해 유도되는 모습을 볼 수 있습니다. 이는 궁극적으로 양쪽 끝에 약간의 유체 팽창(bulge)이 형성되어, 두 개의 만조(high tide) 지역을 만들어내기를 기대해야 함을 의미합니다. 하지만 화살표를 가지고 노는 것은 이 정도로 하고, 이것이 시뮬레이션(simulation)에서 실제로 작동하는지 확인해 봅시다… 어이, 내 케이블 먹지 마! 그거 필요하단 말이야. 좋습니다, 현재 우리는 입자(particles)의 커다란 큐브 형태로 유체를 생성하고 있는데, 우리가 보았듯이 꽤 극적으로 보이기는 하지만 안정화되는 데 시간이 상당히 오래 걸립니다. 시뮬레이션을 실행할 때마다 매번 기다리고 싶지는 않거든요.
그래서 제 생각에 괜찮은 해결책은 대신 행성 주변에 일종의 껍질(shell) 형태로 입자들을 생성하는 것입니다. 참고로, 우리가 여기서 보고 있는 것은 약 220만 개의 입자입니다. 이 입자들이 우리의 작은 행성을 충분히 깊게 덮어서 실제로 조석 효과(tidal effects)를 볼 수 있기를 바랍니다. 제 컴퓨터가 이보다 훨씬 더 많은 양을 처리할 수 있을지 확신이 서지 않거든요. 어쨌든, 이 새로운 생성 설정을 위해 저는 구(sphere) 주변의 점들을 생성하는 함수를 재사용했고, 각 벡터의 크기가 최소 및 최대 생성 반경 내에 있도록 무작위로 스케일링(scaling)했습니다.
그럼 이 값들을 빠르게 정의해 보겠습니다... 그러고 나서 유체 쉘 (fluid shell)을 생성할 수 있습니다. 확실히 이전보다 훨씬 덜 극적으로 안정화되는군요. 좋습니다, 작업이 끝났으니 달을 켜고 — 다시 실행해서 조석 (tides) 현상을 포착할 수 있는지 확인해 보겠습니다. 물이 조금씩 움직이는 것처럼 보이긴 하지만, 제가 보기에는 순전히 행성이 이동하는 방향으로 움직이고 있습니다. 행성이 이동하면서 입자들의 가장 안쪽 층과 충돌하며 발생하는 일종의 잔물결 (ripple) 효과처럼 보입니다.
하지만 유체 입자들의 초기 속도 (initial velocities) 설정을 잊은 게 아니라면, 왜 이런 현상이 발생하는지 모르겠습니다. 당연히 입자들은 생성 대상이 되는 행성의 초기 속도를 상속받아야 합니다. 좋습니다, 이제 그 작업을 마치니 그 이상한 잔물결 현상은 더 이상 발생하지 않지만 — 여전히 조석의 징후는 보이지 않네요. 음, 달의 질량 (mass)을 아주 높게 확 올려보고 어떻게 되는지 보겠습니다. 좋습니다, 왼쪽에 우리의 작은 달이 살짝 보이는군요 — 더 잘 볼 수 있도록 조감도 (bird's eye view) 시점으로 전환해 보겠습니다. 그리고... 여기 작은 팽창 (bulge)이 형성되기 시작하는 게 보이는 것 같네요 — 빠르게 꽤 큰 팽창으로, 아니 사실상 거대한 파도 (giant wave)로 변하더니 — 달을 뒤쫓아 달려가다가 — 하나의 긴 손가락을 뻗어 달을 빠르게 휘감아 버립니다.
어떤 의미에서는, 이것을 만조 (high tide)라고 부를 수도 있겠지만 — 그렇게 말하는 것은 상황의 심각성을 다소 과소평가하는 것일지도 모르겠습니다. 어쨌든, 잠시 시간을 두고 지켜본 뒤에 우리는 달이 행성의 대부분을 빨아들여 말려버렸다는 것을 알 수 있습니다. 아마도 미래에는 달과 행성 또한 입자 (particles)로 구성하여 구축해 보는 것도 멋질 것 같습니다. 왜냐하면 이 정도 거리라면 이들이 아주 많이 변형되거나, 어쩌면 아예 분해 (disintegrating)될 수도 있을 것 같고, 그것을 보는 것도 재미있을 것 같기 때문입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 YouTube Sebastian Lague (절차적 생성)의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기