기본 물리 기반 파티클 시뮬레이션
질량·중력·힘·공기 저항을 Force와 Solve 단계에 적용해 낙하하고 감속하는 폭발 파편의 움직임을 만듭니다.
폭발 파편이 그럴듯해 보이려면 처음에 힘차게 튀어 나간 뒤 공기 저항으로 느려지고, 중력에 끌려 떨어져 바닥에서 조금 튕겨야 합니다. 나이아가라에서는 초기 속도와, 틱마다 적용되는 가속과 저항을 조합해 이런 궤적을 만듭니다. 다만 이것은 시각 효과를 위한 입자 시뮬레이션이며, 모든 입자가 Chaos 강체가 되는 것은 아닙니다. 이 글에서는 힘과 가속도, 질량을 구분하고 속도가 어떻게 바뀌는지 계산해 본 뒤, 모듈이 실행되는 순서를 정리하고 중력과 저항을 받는 폭발 파편을 만들어 봅니다.
힘과 가속도, 질량은 다르다
물리의 식 a = F / m에서 힘(F)과 가속도(a)는 서로 다른 양입니다. 위치를 cm, 시간을 s, 질량을 kg으로 두면 가속도의 단위는 cm/s²이고 힘의 단위는 kg·cm/s²입니다. 그래서 힘을 그대로 속도에 더하지 않고, 힘을 질량으로 나눠 가속도로 바꾼 뒤 시뮬레이션의 시간 간격을 곱해 속도의 변화를 구합니다.
나이아가라 모듈의 이름에 Force가 들어 있어도, 그 입력이 어떤 단위이고 질량을 어떻게 다루는지는 모듈마다 확인해야 합니다. Linear Force, Acceleration Force, Point Attraction Force는 목적이 서로 다르며, 힘을 더하는 모든 모듈이 저절로 Particles.Mass로 나눈다고 가정하지 않습니다. 자주 쓰는 모듈과 속성은 다음과 같습니다.
Gravity Force: 아래 방향의 가속도를 줍니다.(0, 0, -980)cm/s²는 출발점이며 프로젝트나 효과에 따라 바꿀 수 있습니다. 같은 중력 가속도만 받는다면 무거운 입자가 더 빨리 떨어지지는 않습니다.Drag: 속도를 줄입니다. 기본Drag는 질량과 상관없이 적용되므로, 질량만 바꿔서 감속의 차이를 기대하지 않습니다.- Mass(질량):
Initialize Particle이나 Spawn의 속성 설정에서 0보다 큰 값으로 정합니다. 물리량을 직접 흉내 내려면 질량을 읽는 계산을 분명히 만들고, 다른 모듈에서 같은 질량 보정을 한 번 더 하지 않습니다. - 초기 속도: Particle Spawn의
Add Velocity에서 한 번 정합니다. Particle Update에서 틱마다 같은 속도를 더하면 다른 종류의 가속 효과가 됩니다.
숫자로 확인해 봅니다. 직접 만든 계산에서 같은 바깥 힘 F = 1000kg·cm/s²를 질량 2kg과 4kg으로 각각 나눈다고 분명히 정해 둡시다. 그러면 가속도는 1000 ÷ 2 = 500cm/s²와 1000 ÷ 4 = 250cm/s²입니다. 시간 간격이 0.1s라면 속도의 증가량 a × Δt는 각각 50cm/s와 25cm/s입니다. 힘 1000을 속도에 그대로 더하거나 시간 간격을 두 번 곱하지 않습니다. 이것은 이렇게 하겠다고 정한 수치 모델이며, 이름에 Force가 있는 모든 모듈이 이 질량 처리를 저절로 해 준다는 뜻은 아닙니다.
업데이트 모듈의 순서
Particle Update의 모듈은 서로의 결과에 의존하므로 순서가 정해져 있습니다. 먼저 Particle Spawn에서 Lifetime과 크기, 색, 질량, 초기 위치와 속도를 준비합니다. 다음으로 Particle Update의 Particle State가 입자의 나이와 수명 종료를 관리하고, Gravity Force와 고른 힘 모듈, Drag가 이번 틱의 입력을 준비합니다. 충돌이 필요하다면 Collision을 Solve Forces and Velocity 바로 앞에 둡니다. 마지막으로 Solve Forces and Velocity가 쌓인 입력과 시간 간격으로 속도와 위치를 갱신합니다. Collision을 Solve 뒤에 두는 것을 일반 규칙으로 생각하지 않으며, 위치를 따로 덮어쓰는 제약이나 후처리 모듈이 있다면 그 의존 관계를 확인합니다.
충돌과 solver의 입력 순서
Particle State→힘/Drag 준비→필요한 Collision을 solver 직전→속도/위치 갱신이라는 본문 명시적 모듈 입력 소비 순서이다. 전체 모듈 목록이나 Chaos 강체로 일반화하지 않는다.
NIAGARA · CH7-3
Collision을 solver 바로 앞에 배치합니다
본문의 기본 Particle Update 구성입니다. 나이·힘·저항과 필요한 충돌 입력을 준비한 뒤 solver가 시간 간격을 사용해 속도·위치를 갱신합니다.
초기 속도와 매 틱 입력
초기 속도는 Particle Spawn에서 한 번 정합니다. Update에서 같은 속도를 매번 더하는 것은 다른 효과입니다.
힘·질량·Drag
같은 중력 가속도만 받는다면 질량이 크다고 더 빨리 낙하하지 않습니다. 기본 Drag는 질량과 무관합니다. 힘 입력의 단위와 질량 처리 계약은 모듈별로 확인합니다.
후속 작성자
위치를 덮어쓰는 제약·후처리가 있다면 solver 이후의 의존성을 확인합니다. 이 순서는 모든 효과의 모든 모듈을 나열한 목록이 아닙니다.
Niagara의 시각 효과용 입자는 모두 Chaos 강체인 것이 아닙니다. 실제 게임의 명중·피해 판정은 별도 책임입니다.
Particle Spawn의 초기 속도는 별도입니다. 실제 사용 모듈의 의존성 안내를 따르고 위치를 덮어쓰는 후처리도 확인합니다.
이 예의 조건
- 본문과 원 도표에 제시된 사례의 입력·조건을 사용합니다. 설명용 수치나 독립 비교 경로는 각 단계에 표시합니다.
- 현재 상태·값과 전환 근거의 위치는 모든 단계에서 같습니다. 조건이 맞지 않거나 실행되지 않은 경로를 성공한 동작으로 해석하지 않습니다.
관계도의 보충 설명
| 범위 | 원문 |
|---|---|
| 관계·조건 설명 | 원 도표와 같은 4개 노드·3개 간선의 순서다. Particle State→Forces/Drag→Collision→Solve Forces and Velocity 순서다. 초기 Spawn 한 번의 속도와 반복 Update는 별도이며 Collision은 필요할 때만 활성화한다. |
| 관찰·상황 | 값·보충 정보 | 변화·이유 |
|---|---|---|
| 상태와 힘 Particle State 뒤 힘·Drag 입력을 준비한다. |
|
|
| Collision 경계 필요한 Collision을 solver 직전에 둔다. |
|
|
| 갱신 결과 solver가 입력을 소비해 속도·위치를 갱신한다. |
|
|
충돌과 solver의 입력 순서
Particle State→힘/Drag 준비→필요한 Collision을 solver 직전→속도/위치 갱신이라는 본문 명시적 모듈 입력 소비 순서이다. 전체 모듈 목록이나 Chaos 강체로 일반화하지 않는다.
NIAGARA · CH7-3
Collision을 solver 바로 앞에 배치합니다
본문의 기본 Particle Update 구성입니다. 나이·힘·저항과 필요한 충돌 입력을 준비한 뒤 solver가 시간 간격을 사용해 속도·위치를 갱신합니다.
초기 속도와 매 틱 입력
초기 속도는 Particle Spawn에서 한 번 정합니다. Update에서 같은 속도를 매번 더하는 것은 다른 효과입니다.
힘·질량·Drag
같은 중력 가속도만 받는다면 질량이 크다고 더 빨리 낙하하지 않습니다. 기본 Drag는 질량과 무관합니다. 힘 입력의 단위와 질량 처리 계약은 모듈별로 확인합니다.
후속 작성자
위치를 덮어쓰는 제약·후처리가 있다면 solver 이후의 의존성을 확인합니다. 이 순서는 모든 효과의 모든 모듈을 나열한 목록이 아닙니다.
Niagara의 시각 효과용 입자는 모두 Chaos 강체인 것이 아닙니다. 실제 게임의 명중·피해 판정은 별도 책임입니다.
Particle Spawn의 초기 속도는 별도입니다. 실제 사용 모듈의 의존성 안내를 따르고 위치를 덮어쓰는 후처리도 확인합니다.
이 예의 조건
- 본문과 원 도표에 제시된 사례의 입력·조건을 사용합니다. 설명용 수치나 독립 비교 경로는 각 단계에 표시합니다.
- 현재 상태·값과 전환 근거의 위치는 모든 단계에서 같습니다. 조건이 맞지 않거나 실행되지 않은 경로를 성공한 동작으로 해석하지 않습니다.
관계도의 보충 설명
| 범위 | 원문 |
|---|---|
| 관계·조건 설명 | 원 도표와 같은 4개 노드·3개 간선의 순서다. Particle State→Forces/Drag→Collision→Solve Forces and Velocity 순서다. 초기 Spawn 한 번의 속도와 반복 Update는 별도이며 Collision은 필요할 때만 활성화한다. |
| 관찰·상황 | 값·보충 정보 | 변화·이유 |
|---|---|---|
| 상태와 힘 Particle State 뒤 힘·Drag 입력을 준비한다. |
|
|
| Collision 경계 필요한 Collision을 solver 직전에 둔다. |
|
|
| 갱신 결과 solver가 입력을 소비해 속도·위치를 갱신한다. |
|
|
위 그림은 이 글의 기본 Particle Update 구성을 네 상자로 보여 줍니다. 나이와 수명 종료를 다루는 Particle State 다음에 힘 모듈과 Drag가 이번 틱의 힘과 가속도, 감쇠 입력을 준비하고, 필요할 때만 켜는 Collision이 Solve Forces and Velocity 바로 앞에서 충돌 입력을 준비합니다. 마지막 상자인 Solve Forces and Velocity는 시간 간격과 충돌 입력을 받아 속도와 위치를 갱신합니다. 단계 그림은 이 흐름을 상태와 힘의 준비, Solve 바로 앞의 Collision, Solve의 갱신 결과라는 세 장면으로 나누어 보여 줍니다.
그림 아래의 설명은 몇 가지를 덧붙입니다. 초기 속도는 Particle Spawn에서 한 번 정하는 값이고, Update에서 같은 속도를 틱마다 더하는 것은 다른 효과입니다. 같은 중력 가속도만 받는다면 질량이 크다고 더 빨리 떨어지지 않고, 기본 Drag도 질량과 상관이 없으며, 힘 입력의 단위와 질량 처리는 모듈마다 확인해야 합니다. 또 위치를 덮어쓰는 제약이나 후처리가 있다면 Solve 뒤의 의존 관계를 확인하며, 이 순서가 모든 효과의 모든 모듈을 나열한 목록은 아닙니다.
중력과 저항을 받는 폭발 파편
먼저 생성 조건을 정합니다. NS_BouncingParticles를 복제해 NS_PhysicsBlast를 만들고, 계속 만드는 Spawn Rate는 끈 뒤 Emitter Update의 Spawn Burst Instantaneous로 0초에 100개를 만듭니다. Emitter State는 Life Cycle Mode를 Self, Loop Behavior를 Once로 두어 입자가 스스로 사라질 때까지 처리되게 합니다.
다음으로 입자의 기본 속성을 정합니다. Initialize Particle에서 Lifetime은 2초에서 3초 사이, 스프라이트 크기는 5cm, 색은 회색이나 갈색 계열로 둡니다. Mesh Renderer를 쓴다면 메시의 원래 크기를 확인하는데, 한 변이 100cm인 큐브에 Scale (0.05, 0.05, 0.05)를 주면 약 5cm가 됩니다. Mass는 1에서 5 사이로 한 번 정하지만, 아래의 기본 궤적이 질량만으로 달라진다고 가정하지 않습니다.
그다음 초기 속도를 줍니다. Particle Spawn의 Add Velocity에서 아무 방향의 단위 벡터에 속력 100cm/s를 곱한 벡터를 초기 속도로 씁니다. 쓰는 버전의 구면 방향 입력을 쓰거나, 안전한 정규화로 방향을 만듭니다. 위치 분포의 반경과 속력은 서로 다른 값이라는 점도 구분합니다.
이어서 Particle Update에 Gravity Force와 Drag를 추가합니다. 먼저 Drag 0.5로 결과를 보고, 파편마다 저항을 다르게 하고 싶다면 Spawn에서 직접 만든 Float 속성 Particles.DragAmount에 0.5에서 2 사이의 값을 한 번 쓴 뒤 Drag 입력에 연결합니다. Update에서 틱마다 난수를 다시 뽑는 것과는 다른 효과입니다.
바닥에서 튕겨야 한다면 Collision에서 Restitution 0.2, Friction 0.8로 시작합니다. 순서는 Particle State, 힘과 Drag, Collision, Solve Forces and Velocity이며, 쓰는 모듈의 의존 관계 안내도 확인합니다. 마지막으로 같은 카메라에서 초기 속력과 중력, 저항, 충돌 반응을 하나씩 바꿔 가며 비교합니다. 떨어지는 동안에는 중력이 계속 속도를 키우므로, 모든 방향으로 속력이 계속 줄어드는 결과를 기대하지 않습니다.
질량의 효과를 비교하고 싶다면, 같은 바깥 힘 F를 서로 다른 양의 질량 m으로 나눠 a = F / m을 계산하고 그 가속도를 입력으로 쓰는 별도의 실험을 만듭니다. 지금의 기본 예제는 초기 속도와 중력, 질량과 상관없는 Drag만 쓰므로 질량에 따른 차이가 생기지 않습니다.
더 복잡한 움직임
벡터 필드(Vector Field)나 Curl Noise Force로 위치에 따라 다른 힘을 만들 수 있고, 입자의 위치에서 목표까지의 방향으로 직접 끌어당기는 힘을 만들 수도 있습니다. 끌어당기는 힘을 만들 때는 목표와 지금 위치가 같아 방향 벡터가 0이 되는 경우를 정규화하기 전에 처리합니다. Physics Asset과 어울리게 하려면 그 충돌 데이터를 읽는 별도의 경로를 골라야 하며, 스켈레탈 메시 표면의 샘플링을 더하는 것만으로 물리 충돌이 완성되지는 않습니다.
움직임이 폭주하거나 입자가 표면을 뚫는다면, 프레임 간격과 좌표 공간, 최댓값, 수명을 고정한 채 값을 하나씩 조절하면 원인을 찾기 쉽습니다.
정리
- 힘과 가속도는 단위가 다른 양이므로, 힘을 질량으로 나눠 가속도로 바꾸고 시간 간격을 곱해 속도의 변화를 구합니다.
- 이름에 Force가 있어도 모듈마다 입력의 단위와 질량 처리가 다르며, 모든 힘 모듈이 저절로 질량으로 나눈다고 가정하지 않습니다.
- 같은 중력 가속도를 받으면 질량과 상관없이 같은 속도로 떨어지고, 기본
Drag도 질량과 상관이 없습니다. - Particle Update의 순서는
Particle State, 힘과Drag, 필요할 때의Collision,Solve Forces and Velocity입니다. - 초기 속도는 Particle Spawn에서 한 번 정하고, 파편마다 다른 값은 Spawn에서 한 번 뽑아 속성에 써 둡니다.
연습 문제
1. 질량이 다른 두 입자의 낙하
중력만 있는 조건에서 두 입자의 초기 Z 속도는 모두 +100cm/s이고, 가속도는 -980cm/s², 시간 간격 Δt는 0.2s입니다. Drag와 Collision, 다른 힘은 이 계산에서 뺍니다. 한 번 갱신한 Z 속도를 각각 구하고, 질량 2kg과 4kg 때문에 차이가 나는지 판단해 보세요.
해설 보기
두 입자 모두 100 + (-980) × 0.2 = -96cm/s입니다. 같은 중력 가속도를 받으므로 질량만으로는 차이가 나지 않습니다.
이 문제는 속도를 갱신하는 모델만 주었으므로, 위치를 갱신하는 방법이나 본문 폭발 실습의 Drag를 포함한 실제 좌표까지 계산했다고 말할 수는 없습니다.
2. 질량을 바꿔도 같은 감속
Drag를 쓰는 파편들의 질량을 1에서 5로 다양하게 바꿨는데, 모든 파편이 똑같이 느려집니다. 무거운 파편이 덜 느려지게 하려면 어떻게 해야 할까요?
해설 보기
기본 Drag는 질량과 상관없이 속도를 줄이므로, 질량만 바꿔서는 감속에 차이가 생기지 않습니다.
무거운 파편이 덜 느려지게 하려면 질량을 읽는 계산을 따로 만들어야 합니다. 예를 들어 Spawn에서 질량에 따라 Particles.DragAmount를 작게 정해 Drag 입력에 연결하거나, 저항력을 직접 계산해 질량으로 나눈 가속도로 적용합니다. 이때 다른 모듈에서 같은 질량 보정을 한 번 더 하지 않도록 합니다.
3. 틱마다 더한 처음 속도
Add Velocity를 Particle Spawn이 아니라 Particle Update에 넣었더니, 파편이 처음에 튀어 나간 뒤 느려지지 않고 점점 빨라집니다. 왜 그럴까요?
해설 보기
Particle Update는 틱마다 실행되므로, 그곳의 Add Velocity는 초기 속도가 아니라 틱마다 같은 속도를 계속 더하는 가속이 됩니다. 그래서 저항과 중력보다 더해지는 속도가 커서 파편이 점점 빨라집니다.
처음에 한 번 튀어 나가는 움직임을 원한다면 Add Velocity를 Particle Spawn으로 옮깁니다. 그러면 입자가 생길 때 한 번만 속도를 받고, 그 뒤로는 중력과 Drag가 속도를 바꿉니다.
각 모듈의 역할은 Epic의 Particle Update 그룹 레퍼런스에서 확인할 수 있습니다.
다음 글 유체 효과 구현에서는 입자와 격자로 연기나 물 같은 유체를 흉내 내는 방법과, 나이아가라 유체 플러그인을 쓸 때의 조건을 살펴봅니다.