icon안동민 개발노트

조건문과 루프 구조의 기초


 나이아가라 스크립트에서 조건문과 루프 구조는 파티클의 동작을 제어하고 복잡한 효과를 구현하는 데 필수적인 요소입니다.

 이들을 효과적으로 활용하면 다양하고 역동적인 파티클 시스템을 만들 수 있습니다.

조건문

 if-else 문

 기본 구문

if (condition) {
    // 조건이 참일 때 실행될 코드
} else {
    // 조건이 거짓일 때 실행될 코드
}

 예제 : 파티클 수명에 따른 색상 변경

void UpdateParticleColor(inout float4 Color, float Age, float Lifetime)
{
    if (Age < Lifetime * 0.5) {
        Color = float4(1, 0, 0, 1); // 빨간색
    } else {
        Color = float4(0, 0, 1, 1); // 파란색
    }
}

 else if 문

  • 여러 조건을 연속적으로 검사할 때 사용합니다.

 예제 : 파티클 위치에 따른 동작 변경

void UpdateParticleMovement(inout float3 Velocity, float3 Position)
{
    if (Position.y > 100) {
        Velocity.y = -50; // 높이가 100 초과시 아래로 이동
    } else if (Position.y < 0) {
        Velocity.y = 50;  // 높이가 0 미만시 위로 이동
    } else {
        Velocity.y = 0;   // 그 외의 경우 수평 이동
    }
}

 switch 문

  • 여러 가지 경우 중 하나를 선택할 때 사용합니다.

 예제 : 파티클 타입에 따른 동작 설정

void SetParticleBehavior(int ParticleType, inout float3 Velocity, inout float4 Color)
{
    switch (ParticleType) {
        case 0: // 불 파티클
            Velocity.y = 10;
            Color = float4(1, 0.5, 0, 1);
            break;
        case 1: // 물 파티클
            Velocity.y = -5;
            Color = float4(0, 0.5, 1, 1);
            break;
        default: // 기본 파티클
            Velocity = float3(0, 0, 0);
            Color = float4(1, 1, 1, 1);
            break;
    }
}

루프 구조

 for 루프

 기본 구문

for (초기화; 조건; 증감) {
    // 반복 실행될 코드
}

 예제 : 주변 파티클과의 상호작용

void InteractWithNeighbors(inout float3 Velocity, float3 Position, float3 Neighbors[10])
{
    for (int i = 0; i < 10; i++) {
        float3 ToNeighbor = Neighbors[i] - Position;
        float Distance = length(ToNeighbor);
        if (Distance > 0 && Distance < 10) {
            Velocity += normalize(ToNeighbor) / Distance;
        }
    }
}

 while 루프

  • 조건이 참인 동안 계속 실행됩니다.

 예제 : 충돌 회피

void AvoidObstacles(inout float3 Position, float3 Velocity, float3 Obstacles[5])
{
    int i = 0;
    while (i < 5) {
        float3 ToObstacle = Obstacles[i] - Position;
        if (length(ToObstacle) < 5) {
            Position -= normalize(ToObstacle);
        }
        i++;
    }
}

실제 파티클 시스템 적용 사례

 복합 파티클 효과 구현

  • 여러 조건과 루프를 조합하여 복잡한 파티클 효과를 만들 수 있습니다.

 예제 : 폭죽 효과

void FireworkEffect(inout float3 Position, inout float3 Velocity, inout float4 Color, 
                    float Age, float Lifetime, int ParticleID)
{
    if (Age < Lifetime * 0.5) {
        // 상승 단계
        Velocity.y += 9.8 * DeltaTime;
        Color = float4(1, 1, 0, 1);
    } else if (Age < Lifetime * 0.6) {
        // 폭발 단계
        for (int i = 0; i < 10; i++) {
            float Angle = ParticleID * 36 + i * 36;
            Velocity.x = cos(Angle) * 50;
            Velocity.y = sin(Angle) * 50;
        }
        Color = float4(1, 0, 0, 1);
    } else {
        // 소멸 단계
        Velocity *= 0.98;
        Color.a = lerp(1, 0, (Age - Lifetime * 0.6) / (Lifetime * 0.4));
    }
    
    Position += Velocity * DeltaTime;
}

 이 예제에서는 조건문을 사용하여 파티클의 생명 주기를 여러 단계로 나누고, 루프를 사용하여 폭발 효과를 생성합니다.

성능 고려사항 및 최적화 기법

 1. 조건문 최소화

  • 과도한 조건 분기는 성능 저하를 초래할 수 있습니다.
  • 가능한 경우 수학적 표현식으로 대체하세요.

 예제

// 대신에
float Value = condition ? a : b;

 2. 루프 언롤링

  • 작은 크기의 고정 루프는 수동으로 풀어쓰는 것이 더 효율적일 수 있습니다.

 예제

// 대신에
Result += Data[0];
Result += Data[1];
Result += Data[2];
Result += Data[3];

 3. Early Exit 활용

  • 조건을 만족하면 빠르게 루프나 함수를 종료하세요.

 예제

for (int i = 0; i < 100; i++) {
    if (Condition) {
        Result = Value;
        break; // 조건 만족 시 즉시 종료
    }
}

 4. 병렬 처리 고려

  • 나이아가라는 GPU에서 병렬로 실행됩니다. 파티클 간 독립적인 연산을 선호하세요.

 5. 동적 분기 최소화

  • 파티클마다 다른 조건으로 분기되는 것을 피하세요. 공통된 조건을 사용하세요.

복잡한 로직 구현 팁

 1. 모듈화

  • 복잡한 로직을 작은 함수로 분리하여 가독성과 재사용성을 높이세요.

 2. 상태 머신 패턴

  • 여러 상태를 가진 파티클 시스템의 경우, 상태 머신 패턴을 고려하세요.

 예제

void UpdateParticleState(inout int State, float Age, float Lifetime)
{
    switch (State) {
        case 0: // 초기 상태
            if (Age > Lifetime * 0.2) State = 1;
            break;
        case 1: // 중간 상태
            if (Age > Lifetime * 0.8) State = 2;
            break;
        case 2: // 최종 상태
            // 최종 상태 로직
            break;
    }
}

 3. Look-up Tables (LUT) 활용

  • 복잡한 계산을 미리 계산된 테이블로 대체하여 성능을 향상시킬 수 있습니다.

 4. 조건부 실행 최소화

  • 가능한 경우 조건부 실행 대신 수학적 표현식을 사용하세요.

 예제

// 대신에
float Factor = saturate((Threshold - Value) / Threshold);
Result *= Factor;

 나이아가라 스크립트에서 조건문과 루프 구조를 효과적으로 활용하면 복잡하고 역동적인 파티클 시스템을 구현할 수 있습니다.

 하지만 성능 최적화를 위해 이들의 사용을 신중하게 고려해야 합니다.

 가능한 수학적 표현식과 병렬 처리에 적합한 구조를 우선적으로 고려하고 필요한 경우에만 조건문과 루프를 사용하세요.