정적 메시와의 기본 콜리전
Collision 모듈과 Distance Field를 이용해 파티클이 정적 메시를 감지하고 바닥에서 반사·마찰·소멸하도록 설정합니다.
빗방울이 바닥에서 튀거나 파편이 벽에 닿아 사라지게 하려면, 입자가 표면을 알아차리고 반응하도록 충돌(collision)을 설정해야 합니다. 그런데 나이아가라의 충돌은 시뮬레이션 방식에 따라 표면을 읽는 데이터가 다르기 때문에, 같은 바닥이라도 카메라를 돌리면 입자가 바닥을 뚫고 지나가는 일이 생길 수 있습니다. 이 글에서는 CPU와 GPU의 충돌 경로가 각각 무엇을 읽는지 비교하고, 반발과 마찰 같은 반응 값을 계산해 본 뒤, 바닥에서 튕기는 입자를 만들고 카메라 방향만 바꿔 두 GPU 경로를 비교합니다. 나이아가라의 충돌은 시각 효과를 위한 근사이므로, 게임의 명중이나 피해 판정은 별도의 게임플레이 로직으로 처리합니다.
충돌 경로마다 읽는 표면이 다르다
먼저 용어 하나를 짚습니다. 스태틱 메시(Static Mesh)는 메시 에셋의 종류를 가리키는 말이며, 반드시 움직이지 않는다는 뜻은 아닙니다. 이 글에서는 움직이지 않는 바닥 메시를 기준으로 시작합니다.
나이아가라의 충돌은 이미터의 시뮬레이션 대상(Sim Target)에 따라 표면을 읽는 경로가 다릅니다. 세 가지 경로를 비교하면 다음과 같습니다.
| 경로 | 읽는 표면 | 확인할 제한 |
|---|---|---|
| CPU 충돌 | 월드를 향한 트레이스(trace) | 대상 컴포넌트의 query collision과 트레이스 채널 응답, 입자 수에 따른 CPU 비용 |
| GPU Scene Depth | 지금 화면에 그려진 깊이 | 화면 밖이나 가려진 표면의 정보, 카메라의 변화 |
| GPU Distance Fields | 전역 거리 필드 | 거리 필드의 생성, 지원 플랫폼, 얇은 표면과 해상도의 오차 |
이 세 가지를 하나의 Collision Mode 목록에서 트레이스, 스태틱 메시, 랜드스케이프를 고르듯 같은 수준의 선택지로 이해하지 않습니다. 모듈에 나타나는 항목은 Sim Target과 엔진 버전에 따라 다르고, GPU Ray Traces 같은 추가 기능은 따로 지원 조건을 확인해야 합니다.
CPU 트레이스는 입자가 움직이는 경로에 충돌 대상이 있는지 검사합니다. 화면의 깊이가 아니라 월드의 query collision, 즉 조회 요청에 응답하도록 설정된 충돌을 쓰므로, 바닥의 트레이스 채널 응답을 확인하고 적은 수의 입자로 반응을 비교할 때 기준으로 삼을 수 있습니다. 콜리전 프리셋과 트레이스 채널, Block 같은 응답은 콜리전 시스템 기본 개념에서 자세히 다룹니다.
Scene Depth는 지금 카메라의 깊이 데이터를 읽습니다. 그래서 월드에 같은 바닥이 있어도, 카메라 뒤나 화면 밖에 있거나 다른 물체에 가려져 깊이에 남지 않은 표면은 조회할 수 없습니다.
Distance Fields는 월드의 위치에서 거리 필드로 표현된 표면을 찾습니다. 화면에 보이지 않는 표면도 표현 범위 안에 있으면 조회할 수 있어서, 오래 남는 파편에 고를 만한 이유가 있습니다. 다만 전역 거리 필드(Global Distance Field)는 카메라 주변의 한정된 영역을 낮은 해상도로 표현하므로, 화면 밖 어디서나 부딪히거나 얇은 표면에서도 정확히 튕긴다고 기대하지 않습니다.
NIAGARA · CH7-1
같은 표면도 읽는 데이터가 다릅니다
위에서 본 배치로 한 시점의 카메라와 세 표면을 같은 위치에 둡니다. 실선 삼각형은 카메라가 보는 방향, 작은 원은 각 입자의 위치입니다. 표면의 포함 여부를 설명하는 개념도이며 공간 비율은 고정된 모델이 아닙니다.
CPU query collision
CPU Sim에서는 각 입자의 위치에서 월드 query collision을 조회합니다. 이 예시는 A·B·C 모두 query 대상으로 준비돼 있다고 가정합니다.
입자에서 표면으로 향하는 짧은 화살표가 조회 경로입니다. C가 카메라 뒤에 있거나 B가 A에 가려져도 화면 깊이가 직접적인 조회 기준은 아닙니다.
대상 컴포넌트의 query collision·trace 채널 응답을 확인합니다. 카메라에서 쏘는 게임플레이 trace나 실제 충돌 성공 결과를 그린 것이 아닙니다.
GPU Scene Depth
GPU Compute Sim · Scene Depth에서는 현재 카메라 깊이에 남은 표면을 읽습니다. 강조한 A의 앞표면 부분이 그 예시입니다.
B는 A에 가려지고 C는 카메라 뒤에 있어 이 시점의 깊이 데이터에 남지 않습니다. 점선 표면은 월드에 없는 메시가 아니라 이 조회에 필요한 화면 데이터가 없는 경우입니다.
카메라 방향이 바뀌면 깊이에 남는 표면도 달라질 수 있습니다. 입자가 반드시 관통하거나 소멸한다는 결과를 약속하지 않습니다.
GPU Global Distance Field
GPU Compute Sim · Distance Fields에서는 생성된 거리 필드의 근사 표면을 읽습니다. 둥글게 표시한 A·B·C는 이 예시의 표현 범위 안에 있습니다.
카메라 주변의 점선 테두리는 유한한 표현 영역입니다. 화면에 보이지 않아도 이 범위 안의 표면은 조회할 수 있습니다. 실제 clipmap 개수·거리·해상도 또는 경계 모양을 그린 것은 아닙니다.
Generate Mesh Distance Fields와 지원 플랫폼·렌더 경로를 확인합니다. 표현 범위 밖이나 얇은 형상의 오차까지 해결하는 무한 월드 조회는 아닙니다.
표면 데이터와 Bounds·표시·수명·시뮬레이션 조건은 별도로 확인합니다. 그림은 데이터가 포함되는 조건을 설명하며 모든 입자의 반사를 보장하지 않습니다. 게임의 명중·피해 판정은 별도 게임플레이 로직의 책임입니다.
위 그림은 같은 카메라와 표면을 위에서 내려다본 배치로, 세 경로가 각각 어떤 표면 데이터를 읽는지 비교합니다. A는 카메라 앞쪽의 표면, B는 A에 가려진 표면, C는 카메라 뒤의 표면이며, 실선 삼각형은 카메라가 보는 방향이고 작은 원은 입자의 위치입니다. 첫 번째 CPU 그림에서는 각 입자에서 query collision 대상인 A, B, C로 짧은 조회 화살표가 나갑니다. B가 가려졌거나 C가 카메라 뒤에 있어도 화면의 깊이가 조회의 기준이 아니며, 카메라의 시야 삼각형이 트레이스의 범위인 것도 아닙니다.
두 번째 GPU Scene Depth 그림에서는 지금 깊이에 남은 A의 앞쪽 표면만 강조되고, A에 가려진 B와 카메라 뒤의 C는 점선으로 그려집니다. 점선은 월드에 메시가 없다는 뜻이 아니라, 이 조회에 필요한 화면 데이터가 없다는 뜻입니다. 세 번째 GPU Distance Fields 그림에서는 카메라 주변의 점선 테두리가 한정된 표현 영역이고, 그 안에 있는 A, B, C의 근사 표면을 화면에 보이지 않아도 읽을 수 있습니다. 그림 아래의 설명처럼 이 그림은 실제 엔진 화면이나 정확한 공간 비율이 아니며, 표면 데이터와 바운드, 표시, 수명, 시뮬레이션 조건은 따로 확인해야 하고 모든 입자가 튕긴다는 것을 보장하지 않습니다.
모듈의 순서도 정해져 있습니다. Epic의 Particle Update 그룹 레퍼런스에 따라 기본 Collision 모듈은 Solve Forces and Velocity 바로 앞에 둡니다. 힘과 충돌의 입력을 준비하는 순서는 기본 물리 기반 파티클 시뮬레이션에서 이어서 살펴봅니다.
충돌 반응 값
충돌했을 때 입자가 어떻게 반응할지는 몇 가지 값으로 정합니다.
- Restitution(반발): 표면의 법선 방향으로 얼마나 튕길지를 정합니다. 0은 법선 방향으로 튕기지 않는다는 뜻이지, 표면을 따라 미끄러지는 접선 방향의 움직임까지 바로 0이 된다는 뜻은 아닙니다. 1이라고 해서 중력이나 저항 같은 다른 손실까지 없어지지도 않습니다.
- Friction(마찰): 표면을 따라 미끄러지는 움직임을 줄이는 데 관여합니다. 정확한 범위와 반응은 모듈 버전과 계산 조건을 확인합니다.
- Collision Radius(충돌 반경): 보이는 스프라이트나 메시와 충돌의 근사가 맞도록 조절합니다. 스프라이트 크기는 전체 폭과 높이이므로 반경과 같은 숫자가 아니며, 모든 메시가 저절로 정확한 바운딩 박스 충돌을 쓰는 것도 아닙니다.
- 종료 조건: 충돌한 뒤 입자를 없애야 한다면 모듈의 Kill 설정이나 충돌 결과를 쓰는 Kill 처리를 구성합니다. 쓰는 버전에 없는 Max Collisions 같은 항목에 기대지 말고, 충돌 횟수가 필요하다면 실제로 제공되는 충돌 횟수나 상태와 수명 제한을 확인합니다.
반발을 숫자로 따라가 봅니다. 표면의 법선을 +Z로 두는 단순한 계산 모델에서, 충돌 직전의 속도를 (100, 0, -300)cm/s라고 합시다. 이 한 번의 계산에서는 중력과 마찰, 다른 손실을 빼고, 법선 성분만 충돌 후 = -Restitution × 충돌 전으로 바뀐다고 가정합니다. Restitution이 0.7이면 Z 성분은 -0.7 × -300 = +210cm/s가 되고, 접선 방향인 X 성분은 그대로 +100cm/s입니다. 아래 실습처럼 Friction 0.5를 적용한 실제 엔진의 궤적이 이 계산과 같다는 보장은 없습니다.
거리 필드 준비하기
GPU Distance Fields 경로를 쓰려면 먼저 거리 필드를 준비해야 합니다. 프로젝트 설정(Project Settings)의 Rendering에서 Generate Mesh Distance Fields를 검색해 켜고, 에디터가 요구하는 재시작과 데이터 생성이 끝나기를 기다립니다. 다음으로 거리 필드 시각화로 대상 바닥의 표면이 표현되는지 확인합니다. 너무 얇은 면이나 작은 세부 형태, 축마다 다른 스케일은 근사 오차를 만들 수 있으므로, 거리 필드가 언제나 가장 정확한 충돌 방식이라고 보지 않습니다. 마지막으로 GPU 이미터의 Collision에서 Distance Fields 경로를 고릅니다. CPU 트레이스와 비교하려면 Sim Target과 충돌 설정을 함께 바꾸고, 두 경로가 읽는 데이터가 다르다는 것을 기록해 둡니다.
비교하기 전에는 엔진의 마이너 버전과 플랫폼, GPU, RHI(그래픽 API), 렌더 경로를 적어 둡니다. Sim Target이 CPU Sim이면 트레이스를, GPU Compute Sim이면 지원되는 Scene Depth나 Distance Fields 경로를 씁니다. 프로젝트 설정을 켰더라도 고른 플랫폼과 렌더 경로가 GPU 거리 필드 충돌을 지원해야 하며, 예를 들어 지금의 렌더링 기능 레퍼런스는 모바일 표에서 이 기능을 지원하지 않는 것으로 표시합니다.
거리 필드 생성이 끝나지 않았거나 지원되는 경로가 없다면, 그 비교는 조건이 갖춰지지 않은 것으로 남겨 두고 CPU 예제를 이어 갑니다. 메시의 거리 필드 표현과 전역 거리 필드 시각화에서 목표 표면이 표현 범위 안에 있는지도 따로 확인합니다. 설정을 켰다는 사실만으로 그 표면의 데이터까지 준비되었다고 판단하지 않습니다. 거리 필드의 표현 범위와 생성 조건은 Epic의 메시 거리 필드 문서에서 확인할 수 있습니다.
바닥에서 튕기는 입자 만들기
먼저 기본 스프라이트 시스템을 복제해 NS_BouncingParticles로 저장합니다. Emitter State는 계속 재생되게 하고 Emitter Update에 Spawn Rate를 두는데, 처음에는 초당 20개처럼 적은 수로 시작합니다. Particle Spawn의 Initialize Particle에서 Lifetime을 2초, 스프라이트 크기를 10cm, 색을 밝은 색으로 두고, 같은 그룹의 Add Velocity로 Z를 300cm/s, X와 Y를 각각 -50cm/s에서 50cm/s 사이의 값으로 줍니다.
다음으로 Particle Update에 Particle State, Gravity Force, Collision, Solve Forces and Velocity를 차례로 둡니다. Gravity Force는 아래 방향 가속도 (0, 0, -980)cm/s²를 기준으로 하고, Collision은 Solve Forces and Velocity 바로 앞에 둡니다.
그다음 충돌을 설정합니다. 우선 CPU Sim으로 두고, Collision이 쓰는 트레이스 채널에 대해 바닥이 Block으로 응답하게 합니다. Restitution 0.7, Friction 0.5에서 시작하고, 반경은 폭 10cm의 스프라이트에 맞춰 약 5cm를 기준으로 보이는 크기와 비교합니다. 충돌하자마자 입자를 없애는 설정은 끕니다.
이어서 시스템을 바닥에서 약 100cm 위에 놓습니다. 단순하고 충분히 두꺼운 바닥에서 재생해 튕기는 모습과 미끄러지는 모습을 각각 확인하고, Particle State에서 수명이 다하면 입자가 사라지게 합니다. 기본 반응이 맞으면 복사본을 GPU Compute Sim으로 바꾸고, 앞에서 준비한 Distance Fields 경로를 적용합니다. 움직임이 들어갈 만큼 바운드를 설정하고, 화면 밖과 얇은 표면에서 결과가 어떻게 다른지 비교합니다.
예상되는 결과는 입자가 위로 올라갔다가 떨어지고 표면에서 튕기는 움직임입니다. 중력이 계속 작용하므로, 저항과 충돌 반응, 수명 설정에 따라 완전히 멈추기 전에 사라질 수도 있습니다. 입자가 표면을 아예 알아차리지 못한다면 반발 값보다 표면 데이터와 반경부터 확인합니다.
카메라 방향만 바꿔 비교하기
두 GPU 경로가 지원되고 거리 필드의 준비가 끝났다면, CPU 예제를 기준으로 GPU 복사본을 두 개 만듭니다. NS_BouncingParticles_Depth는 Scene Depth를, NS_BouncingParticles_DF는 준비한 Distance Fields 경로를 쓰며, 둘 다 GPU Compute Sim으로 둡니다. 크기와 수명, 초기 속도, 중력, 반응 값, 바닥과 이미터의 위치는 그대로 두고, 한 번에 한 시스템만 재생해 비교합니다. CPU에서 GPU로 바꾸는 단계는 Sim Target과 충돌 데이터의 경로를 함께 바꾸는 비교이므로, 두 경로의 궤적이나 튕기는 횟수가 같아야 한다는 실험으로 보지 않습니다.
먼저 바닥의 윗면과 입자가 떨어지는 영역이 함께 보이는 방향을 시점 1로 정합니다. 시점 2에서는 카메라의 위치를 그대로 둔 채, 바닥이 화면 밖이나 카메라 뒤가 되도록 방향만 돌립니다. 각 경로 안에서는 이 카메라 방향만 바꾸고, 입자 설정 값과 메시를 함께 고치지 않습니다. Scene Depth 시각화에서는 목표 표면이 지금 깊이에 남는지를, 거리 필드 시각화에서는 표면이 표현되어 있는지를 비교합니다.
| 조건 | 예상할 차이 | 다르면 먼저 확인할 곳 |
|---|---|---|
| CPU 트레이스에서 시점 1에서 시점 2로 | 같은 query 대상이라면 화면 깊이가 있는지는 조회의 직접 기준이 아님 | 바닥의 query collision과 채널 응답, 시뮬레이션이 계속 유효한지 |
| GPU Scene Depth에서 목표 표면이 깊이에 있음 | 그 화면 데이터가 충돌의 입력이 될 수 있음 | Scene Depth에 표면이 남는지, 반경과 반응 값 |
| GPU Scene Depth에서 표면이 화면 밖이거나 뒤에 있거나 가려짐 | 목표 표면의 지금 깊이 데이터가 부족할 수 있음 | 먼저 표면 데이터, 그다음 바운드와 표시, 수명 조건 |
| GPU Distance Fields에서 표면이 생성된 표현 범위 안에 있음 | 화면 깊이가 없다고 표면 데이터가 사라지는 경로와 구별됨 | 메시와 전역 거리 필드 시각화, 얇은 형태와 해상도, 반경 |
| 거리 필드가 생성되지 않았거나 경로가 지원되지 않거나 표현 범위 밖 | 지금 조건으로는 그 비교를 판정할 수 없음 | 생성 완료 여부, 지원 플랫폼과 RHI, 렌더 경로와 표현 범위 |
시점 2에서 보이지 않는 입자가 충돌했다고 화면으로 확인했다고 적지 않습니다. 다시 시점 1로 돌아왔을 때 새 입자가 튕기는 모습도, 화면 밖에 있던 그 입자가 충돌했다는 것을 보여 주지는 않습니다. 표면 데이터가 있었는지와 실제로 눈으로 본 반응을 나누어 적고, 보지 못한 반응은 확인할 수 없었다고 남깁니다.
효과가 사라졌다고 해서 곧바로 충돌이 실패했다고 판단하지도 않습니다. 바운드(Bounds)는 효과의 움직임을 감싸는 영역이므로, 이 영역과 입자의 수명, 표시 여부, 조건에 따라 표시나 시뮬레이션을 줄이는 컬링 설정을 함께 확인합니다. 입자가 계속 시뮬레이션되는 조건에서 표면을 읽는 경로를 비교해야 데이터의 차이를 해석할 수 있습니다. 위 표는 이런 조건을 갖췄을 때의 예상이며, 모든 입자가 튕긴다는 것을 보장하지는 않습니다.
충돌 이벤트와 비용
CPU 입자의 충돌을 다른 이미터에 알리려면, 원본 이미터에 Generate Collision Event를 두고 받는 이미터에 Event Handler와 Receive Collision Event를 구성합니다. 원본의 Persistent IDs와 CPU 이벤트의 지원도 확인합니다. 이벤트 핸들러는 블루프린트 이벤트나 사운드를 저절로 부르는 기능이 아니므로, 게임플레이의 Hit나 나이아가라 데이터 내보내기, 오디오는 각각 따로 전달 경로가 필요합니다.
충돌은 비용이 드는 계산이므로, 동시에 충돌하는 입자의 수와 수명, 조회 범위를 줄이고 실제 장면에서 비용을 측정합니다. 눈에 잘 띄는 핵심 파편만 충돌시키거나, 첫 충돌 뒤에 입자를 없애는 설계도 함께 비교해 봅니다.
정리
- 나이아가라의 충돌은 시각 효과를 위한 근사이며, CPU는 월드 트레이스를, GPU는 Scene Depth나 Distance Fields를 읽어 표면을 찾습니다.
- Scene Depth는 지금 화면의 깊이에 남은 표면만 읽으므로 화면 밖이나 가려진 표면을 놓칠 수 있고, Distance Fields는 카메라 주변의 한정된 범위를 근사합니다.
- 기본
Collision모듈은Solve Forces and Velocity바로 앞에 두며, Restitution은 법선 방향의 반발, Friction은 접선 방향의 감쇠에 관여합니다. - GPU Distance Fields를 쓰려면
Generate Mesh Distance Fields를 켜고 데이터 생성과 플랫폼 지원을 확인해야 합니다. - 경로를 비교할 때는 카메라 방향만 바꾸고, 표면 데이터의 유무와 눈으로 본 반응을 나누어 기록하며, 바운드와 컬링도 함께 확인합니다.
연습 문제
1. Restitution을 0으로
본문의 계산 모델에서 충돌 직전의 속도가 (100, 0, -300)cm/s일 때, Restitution만 0으로 바꾸면 충돌 뒤의 속도는 어떻게 될까요? 입자가 모든 방향으로 곧바로 멈춘다는 설명이 맞을까요?
해설 보기
법선 성분은 -0 × -300 = 0이 되므로, 충돌 뒤의 속도는 (100, 0, 0)cm/s입니다. 법선 방향으로는 튕기지 않지만, 접선 방향의 X 성분은 이 모델에서 그대로 남습니다.
그래서 모든 방향으로 곧바로 멈춘다는 설명은 맞지 않습니다. 접선 방향의 움직임까지 줄이려면 마찰이나 다른 감쇠가 필요합니다. 실제 모듈의 결과를 확인할 때는 시간 간격과 반경, 표면의 근사, 다른 힘도 함께 기록합니다.
2. 카메라를 돌리자 뚫고 지나가는 입자
GPU Scene Depth 경로를 쓰는 효과에서, 바닥이 보이는 동안은 입자가 잘 튕기는데 카메라를 위로 돌려 바닥이 화면 밖으로 나가자 입자가 바닥을 뚫고 떨어집니다. 왜 그럴까요? 어떤 경로를 검토하면 좋을까요?
해설 보기
Scene Depth는 지금 카메라의 깊이 데이터를 읽으므로, 바닥이 화면 밖으로 나가면 그 표면의 깊이 정보가 없어집니다. 그래서 입자가 부딪힐 표면을 찾지 못하고 뚫고 지나갑니다.
화면에 보이지 않는 표면에도 반응해야 한다면 GPU Distance Fields 경로를 검토합니다. 거리 필드는 화면과 상관없이 카메라 주변의 표현 범위 안에 있는 표면을 읽을 수 있습니다. 다만 얇은 표면이나 해상도의 오차, 표현 범위를 확인하고, 정확한 반응이 꼭 필요한 적은 수의 입자라면 CPU 트레이스도 비교합니다.
3. 아예 감지되지 않는 바닥
CPU Sim의 NS_BouncingParticles에서 입자가 바닥을 전혀 알아차리지 못하고 그대로 통과합니다. Restitution을 1로 올려도 같습니다. 무엇부터 확인해야 할까요?
해설 보기
입자가 표면을 아예 감지하지 못한다면 반응 값이 아니라 표면 데이터 쪽을 먼저 봅니다. 바닥 컴포넌트에 query collision이 켜져 있는지, Collision 모듈이 쓰는 트레이스 채널에 대해 바닥이 Block으로 응답하는지 확인합니다.
그다음 Collision 모듈이 Solve Forces and Velocity 바로 앞에 있는지, 충돌 반경이 0이거나 너무 작지 않은지, 입자가 한 틱에 바닥을 건너뛸 만큼 빠르지 않은지 확인합니다. 감지가 된 뒤에야 Restitution과 Friction이 의미를 가집니다.
다음 글 동적 액터와의 기본 상호작용에서는 움직이는 캐릭터나 물체와 입자가 반응하는 방법과, 그때 충돌 데이터가 따라가는 조건을 살펴봅니다.