본문 바로가기

기본 수학 함수와 벡터 연산

산술·삼각·클램프 함수와 벡터 생성·분해·정규화·내적·외적을 조합해 위치와 속도 변화를 계산합니다.

입자의 위치와 속도, 색, 크기는 모두 숫자로 표현됩니다. 그래서 입자를 원하는 대로 움직이고 바꾸는 일은 결국 이 숫자들을 계산하는 일입니다. 나이아가라 스크립트 기본 개념에서 파라미터 맵으로 값을 읽고 쓰는 흐름을 익혔으니, 이번에는 그 흐름 사이에 넣을 계산을 다룹니다. 이 글에서는 숫자 하나를 다루는 기본 수학 함수와 여러 성분을 함께 다루는 벡터 연산을 살펴보고, 속도의 방향은 그대로 두고 속력만 제한하는 계산을 따라가 봅니다. 나이아가라의 스크립트 에디터는 이런 계산을 노드로 제공하므로, 코드를 쓰지 않고도 복잡한 계산을 눈으로 따라가며 만들 수 있습니다.


숫자 하나를 다루는 수학 함수

먼저 Float이나 Int32 같은 숫자 하나에 쓰는 기본 연산을 살펴봅니다. 노드 하나하나의 계산은 단순하지만, 어느 스크립트 단계에 두느냐에 따라 결과가 크게 달라집니다. Particle Spawn에 둔 계산은 입자가 생길 때 한 번만 실행되고, Particle Update에 둔 계산은 틱마다 다시 실행되어 결과가 쌓이기 때문입니다. 아래 예들은 이 차이와 함께, 0으로 나누기처럼 계산 전에 막아야 하는 입력도 짚습니다.

사칙연산

Add, Subtract, Multiply, Divide는 두 입력 핀으로 값을 받아 더하기, 빼기, 곱하기, 나누기를 하고 결과를 내보냅니다. 예를 들어 Particle Spawn에서 Particles.Lifetime에 Multiply로 2를 곱하면 초기 수명을 한 번 두 배로 늘릴 수 있습니다. 그런데 같은 계산을 Particle Update에 두면 지금 수명에 틱마다 2를 곱하게 되어 수명이 계속 커집니다. 크기도 마찬가지로, Particle Spawn에서 Particles.SpriteSize에 Add로 일정량을 더하면 초기 크기가 커지지만, 수명 동안의 크기 변화는 저장해 둔 초기 크기나 기준값에서 계산해야 틱마다 값이 쌓이지 않습니다.

삼각 함수

Sine, Cosine, Tangent는 주기적으로 반복되는 움직임이나 파동을 만들 때 씁니다. 입력은 라디안 단위의 Float 하나이고, 그 각도의 함숫값을 내보냅니다. 예를 들어 phase = 2π × 반복 횟수 × Particles.NormalizedAge를 Sine에 넣고 진폭을 곱하면, 수명 동안 정해진 횟수만큼 오르내리는 변위가 나옵니다. 이 변위를 저장해 둔 기준 위치에 더하면 입자가 수명 동안 정해진 횟수로 흔들립니다. 틱마다 지금 위치에 같은 변위를 계속 더하는 계산과는 결과가 다르다는 점에 주의합니다. 불꽃이 솟아오르며 좌우로 흔들리는 속도를 만들 때도 같은 방법을 씁니다.

절댓값 (Abs)

Abs는 숫자의 부호를 없애 0 이상의 값으로 만듭니다. Float이나 Vector를 입력으로 받습니다. 예를 들어 입자가 아래로 떨어지며 속도의 한 성분이 음수가 될 때, 그 성분의 크기만 쓰고 싶다면 Abs를 적용합니다. 다만 벡터의 Abs는 성분마다 따로 부호를 없애는 연산이므로, 전체 속력을 구하려면 Length를 써야 합니다.

거듭제곱과 제곱근

Power는 밑을 주어진 지수만큼 거듭제곱하고, Sqrt는 제곱근을 구합니다. Power는 Float 입력 두 개를, Sqrt는 Float 입력 하나를 받으며, 지수가 정수일 필요는 없습니다. 입자의 밝기를 수명에 따라 곧은 선이 아니라 휘어진 곡선으로 바꾸고 싶을 때 Power를 쓸 수 있고, 밀도를 계산하는 수식 안에서 Sqrt를 쓸 수 있습니다.

이런 함수는 유효하지 않은 입력을 조심해야 합니다. 분모가 0인 나눗셈, 음수의 제곱근, 음수 밑에 정수가 아닌 지수는 올바른 실수 결과를 내지 못하므로 계산하기 전에 처리합니다. 계산 뒤에 Clamp를 붙인다고 이미 생긴 NaN(숫자가 아닌 값)이 해결되지는 않습니다.

최솟값과 최댓값

Min과 Max는 두 Float 입력 가운데 더 작은 값이나 더 큰 값을 고릅니다. 예를 들어 속력 Length(Velocity)에 Min으로 상한을 둔 뒤, 안전하게 구한 단위 방향을 곱하면 속도를 제한할 수 있습니다. 벡터에 성분마다 Min을 적용하는 것은 이것과 다른 계산이라는 점을 뒤에서 숫자로 확인합니다.

클램프

Clamp는 Value, Min, Max 세 입력을 받아, 값을 정한 최솟값과 최댓값 사이에 붙잡아 둡니다. 입자의 알파나 0에서 1 사이여야 하는 제어 값을 그 범위로 제한하거나, 크기가 너무 커지거나 작아지지 않게 할 때 씁니다. HDR로 빛나는 색은 1을 넘을 수 있으므로, 목적 없이 색을 1로 잘라내지 않습니다.


여러 성분을 함께 다루는 벡터 연산

나이아가라에서 속도는 세 성분의 Vector, 스프라이트 크기는 두 성분의 Vector2, 색은 RGBA의 Color로 다룹니다. Particles.Position은 Position 타입이므로, 방향이나 변위를 나타내는 벡터와 좌표 공간을 구분해야 합니다. 벡터 연산은 이런 값들을 다루는 수학이며, 목적에 맞는 노드 조합을 고를 줄 알면 쓸모가 크게 늘어납니다.

벡터 만들기와 나누기

Make Vector, Make Vector2, Make Vector4는 Float 값 여러 개를 X, Y, Z(또는 W) 입력 핀에 받아 벡터 하나로 묶습니다. 원하는 방향과 크기의 속도를 X, Y, Z별로 조절해 만들 때 씁니다. 반대로 Split Vector, Split Vector2, Split Vector4는 벡터 하나를 받아 X, Y, Z(또는 W) 출력 핀으로 나눕니다. 입자의 위치에서 X 값만 꺼내 계산에 쓰고 싶을 때 씁니다.

벡터의 사칙연산

벡터끼리, 또는 벡터와 Float 사이에도 Add, Subtract, Multiply, Divide를 쓸 수 있으며, 연산은 X, Y, Z 성분마다 따로 이루어집니다. 벡터와 벡터를 더하면 같은 성분끼리 더하므로, Particles.Position + Particles.Velocity × Engine.DeltaTime처럼 다음 틱의 위치를 계산할 수 있습니다. 다만 Solve Forces and Velocity가 이미 이동을 계산한다면, 같은 이동을 한 번 더 더하지 않아야 합니다. 벡터에 Float을 곱하면 모든 성분에 그 값을 곱하므로, Particles.Velocity에 0.5를 곱해 속도를 절반으로 줄이거나 색에 값을 곱해 밝기를 조절할 수 있습니다.

힘을 다룰 때는 단위에 주의합니다. 가속도에 Engine.DeltaTime을 곱한 속도 변화량을 Particles.Velocity에 더하는 것이 기본인데, 힘은 질량으로 나누어야 가속도가 됩니다. 기본 Gravity Force와 Solve Forces and Velocity를 쓴다면 직접 계산하지 말고 그 모듈들이 정한 계산 경로를 따릅니다.

벡터의 길이와 정규화

Length(Vector Length)는 벡터 하나를 받아 그 길이를 Float으로 내보냅니다. 속도의 크기, 즉 속력이 정한 값을 넘을 때만 추가 효과를 일으키고 싶을 때 씁니다.

Normalize는 벡터의 방향은 그대로 두고 길이를 1로 만듭니다. 이렇게 길이가 1인 벡터를 단위 벡터라고 하며, 방향만 필요할 때 씁니다. 예를 들어 어떤 방향으로 일정한 세기의 힘을 주고 싶다면 그 방향 벡터를 정규화한 뒤 세기를 곱합니다. 길이가 0이거나 아주 작은 벡터는 나눌 길이가 없으므로, 0일 때를 처리하는 안전한 정규화를 쓰거나 따로 기본 방향을 정해 둡니다.

내적과 외적

Dot Product(내적)는 두 벡터를 받아 Float 하나를 내보냅니다. 두 단위 벡터의 내적은 둘 사이 각도의 코사인이므로, 방향이 같으면 1, 수직이면 0, 반대면 -1입니다. 정규화하지 않은 벡터라면 두 벡터의 길이가 결과에 곱해집니다. 입자의 이동 방향이 어떤 빛의 방향과 비슷할 때만 밝아지게 하는 계산에 씁니다.

Cross Product(외적)는 두 벡터에 모두 수직인 벡터를 내보내며, 3D 공간에서 회전축 같은 것을 구할 때 씁니다. 예를 들어 표면의 노멀 방향을 바탕으로 입자의 회전축을 정할 수 있습니다. 두 벡터가 평행하거나 한쪽이 0이면 결과도 0이고, 입력의 순서를 바꾸면 결과의 방향이 뒤집힙니다. 결과가 저절로 단위 벡터가 되지도 않습니다.


방향은 두고 속력만 제한하기

수학 함수와 벡터 연산을 조합하면 입자의 동작을 정교하게 다룰 수 있습니다. Sine으로 위치나 속도에 주기적인 변화를 주고, Random 노드에 Multiply와 Add를 이어 무작위 값을 원하는 범위 안에서 만들 수 있습니다. Particles.NormalizedAge와 커브, Multiply를 이으면 크기와 색을 수명에 따라 부드럽게 바꿀 수 있고, Add Velocity나 Gravity Force 같은 모듈과 벡터 연산을 함께 쓰면 물리 법칙을 따르는 움직임을 흉내 낼 수 있습니다.

그 가운데 자주 쓰는 계산 하나를 숫자로 따라가 봅니다. 속도가 (300, 400, 0)cm/s인 입자의 속력을 400cm/s 이하로 제한하되, 방향은 그대로 두고 싶다고 합시다.

단계계산결과
속력 구하기sqrt(300² + 400²)500cm/s
단위 방향 구하기속도 ÷ 500(0.6, 0.8, 0)
속력 제한하기Min(500, 400)400cm/s
제한된 속도단위 방향 × 400(240, 320, 0)cm/s
0.1초 동안의 변위속도 × 0.1(24, 32, 0)cm

표처럼 길이로 나누어 방향을 얻고, 속력에만 상한을 둔 뒤 다시 곱하면 방향이 바뀌지 않습니다. 반면 성분마다 400을 상한으로 잘라 내면 (300, 400, 0)의 어느 성분도 400을 넘지 않으므로 그대로 남고, 속력은 500cm/s 그대로입니다. 그래서 성분마다 자르는 것은 속력 제한이 아닙니다.

이 계산을 Particle Update 모듈에서 쓴다면, 먼저 지금 속도를 읽어 제한된 속도를 써 넣고, 그 값을 쓰는 Solve Forces and Velocity와 순서를 맞춥니다. 표의 변위는 계산을 이해하기 위한 예상값이므로, 이미 위치를 계산하는 스택에 같은 변위를 한 번 더 더하지 않습니다.

노드를 많이 이을수록 어디서 값이 어긋났는지 찾기 어려워집니다. 그래서 작은 노드 조합부터 만들어 결과를 확인하고, 계산이 맞는 것을 본 뒤에 다음 단계를 잇는 편이 좋습니다.


정리

  • 사칙연산과 삼각 함수, Abs, Power, Sqrt, Min, Max, Clamp는 숫자를 다루는 기본 노드이며, 삼각 함수의 입력은 라디안입니다.
  • 같은 계산이라도 Particle Spawn에 두면 한 번, Particle Update에 두면 틱마다 실행되어 값이 쌓일 수 있으므로, 변화는 기준값에서 계산합니다.
  • 0으로 나누기나 음수의 제곱근 같은 유효하지 않은 입력은 계산 전에 처리하며, 계산 뒤의 Clamp로 NaN을 고칠 수 없습니다.
  • 벡터의 사칙연산과 Abs, Min은 성분마다 이루어지고, 길이는 Length, 방향은 Normalize로 구하며 길이 0을 따로 처리합니다.
  • 속력을 제한할 때는 길이에 상한을 두고 단위 방향을 곱하며, 성분마다 자르는 것은 속력 제한이 아닙니다.

연습 문제

1. 다른 방향의 속도 제한

속도 (0, -300, 400)cm/s를 방향을 바꾸지 않고 200cm/s로 제한합니다. 일정한 속도로 0.25초 움직일 때의 변위를 구하세요. 입력 속도가 (0, 0, 0)으로 바뀌면 같은 나눗셈을 그대로 해도 될까요?

해설 보기

원래 속력은 sqrt(300² + 400²) = 500cm/s이고, 단위 방향은 속도를 500으로 나눈 (0, -0.6, 0.8)입니다. 여기에 200을 곱하면 제한된 속도는 (0, -120, 160)cm/s입니다. 변위는 이 속도에 0.25초를 곱한 (0, -30, 40)cm입니다.

속도가 0이면 길이도 0이므로 그 길이로 나눌 수 없습니다. 입자를 멈춘 채 두려는 것이라면 0 벡터를 그대로 두고, 기본 방향이 필요한 기능이라면 그 기능에 맞는 안전한 대체 방향을 정합니다.

2. 틱마다 커지는 수명

Particle Update에 Particles.Lifetime × 2를 계산해 Map Set으로 다시 쓰는 모듈을 두었습니다. 초기 수명이 1초라면 세 틱 뒤의 수명은 얼마가 될까요? 초기 수명을 한 번만 두 배로 하려면 어떻게 해야 할까요?

해설 보기

Particle Update는 틱마다 실행되므로, 수명이 1초에서 2초, 4초, 8초로 틱마다 두 배가 됩니다. 세 틱 뒤에는 8초입니다. 입자가 사실상 사라지지 않는 문제가 생깁니다.

초기 수명을 한 번만 두 배로 하려면 같은 계산을 Particle Spawn에 둡니다. Particle Spawn은 입자가 생길 때 한 번만 실행되므로 1초가 2초로 한 번 바뀌고 끝납니다.

3. 같은 방향인지 확인하기

입자의 이동 방향이 빛의 방향과 비슷할 때만 밝게 만들려고 두 벡터의 Dot Product를 계산했습니다. 이동 속도 (0, 0, 200)과 빛의 방향 (0, 0, 1)의 내적이 200이 나왔습니다. 이 값을 그대로 밝기의 기준으로 써도 될까요?

해설 보기

그대로 쓰기는 어렵습니다. 속도 벡터를 정규화하지 않았기 때문에 속력 200이 결과에 곱해져, 방향이 같은지와 상관없이 빠를수록 값이 커집니다.

방향만 비교하려면 속도를 정규화해 (0, 0, 1)로 만든 뒤 내적을 구합니다. 그러면 결과는 두 방향 사이 각도의 코사인인 1이 되어, 방향이 같으면 1, 수직이면 0, 반대면 -1로 일정한 기준을 가집니다. 속도가 0이면 정규화할 수 없으므로 그 경우를 따로 처리합니다.

다음 글 조건문과 루프 구조의 기초에서는 값에 따라 다른 결과를 고르는 조건 노드와, 정해진 횟수만큼 계산을 되풀이하는 반복 구조를 다룹니다.