๐Ÿ”ฎ :: Perlin Line Deformation 1

BamgasiJMยท2025๋…„ 10์›” 11์ผ

Nannou <Generative Art>

๋ชฉ๋ก ๋ณด๊ธฐ
12/55
post-thumbnail

// =============================================================================
// 0. ๋ชจ๋“ˆ ๋ฐ ํฌ๋ ˆ์ดํŠธ ์ž„ํฌํŠธ (Imports)
// =============================================================================
// `nannou::prelude::*`๋Š” nannou ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ฐœ๋ฐœ์— ํ•„์š”ํ•œ ๋Œ€๋ถ€๋ถ„์˜ ๊ธฐ๋ณธ ํƒ€์ž…๊ณผ ํ•จ์ˆ˜๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.
use nannou::prelude::*;

// Perlin ๋…ธ์ด์ฆˆ ์ƒ์„ฑ๊ธฐ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด nannou์˜ noise ๋ชจ๋“ˆ์—์„œ Perlin ๊ตฌ์กฐ์ฒด์™€ NoiseFn ํŠธ๋ ˆ์ดํŠธ๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.
use nannou::noise::{NoiseFn, Perlin};

// =============================================================================
// 1. ๋ฉ”์ธ ํ•จ์ˆ˜ (Application Entry Point)
// =============================================================================
// Rust ํ”„๋กœ๊ทธ๋žจ์˜ ์ง„์ž…์ ์ž…๋‹ˆ๋‹ค.
fn main() {
    nannou::app(model)
        .update(update)
        .run();
}

// =============================================================================
// 2. ๋ผ์ธ ๊ตฌ์กฐ์ฒด ์ •์˜ (Line Structure)
// =============================================================================
// `Line`์€ ํ™”๋ฉด์— ๊ทธ๋ ค์งˆ ํ•˜๋‚˜์˜ ๊ณก์„ ์„ ํ‘œํ˜„ํ•ฉ๋‹ˆ๋‹ค.
// ๊ฐ ๋ผ์ธ์€ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์ ์œผ๋กœ ๊ตฌ์„ฑ๋˜๋ฉฐ, ๋…ธ์ด์ฆˆ ํ•„๋“œ์— ์˜ํ•ด ๋ณ€ํ˜•๋ฉ๋‹ˆ๋‹ค.
struct Line {
    // ์›๋ณธ ์ ๋“ค์˜ ์œ„์น˜ (๋ณ€ํ˜•๋˜์ง€ ์•Š์€ ์ดˆ๊ธฐ ์œ„์น˜)
    // ์ด ์ ๋“ค์€ ๋ณ€ํ•˜์ง€ ์•Š๊ณ , ๋ณ€ํ˜• ๊ณ„์‚ฐ์˜ ๊ธฐ์ค€์ ์œผ๋กœ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
    original_points: Vec<Point2>,
    
    // ํ˜„์žฌ ๋ณ€ํ˜•๋œ ์ ๋“ค์˜ ์œ„์น˜
    // ๋…ธ์ด์ฆˆ ํ•„๋“œ์˜ ์˜ํ–ฅ์„ ๋ฐ›์•„ ๋งค ํ”„๋ ˆ์ž„๋งˆ๋‹ค ๋‹ค์‹œ ๊ณ„์‚ฐ๋ฉ๋‹ˆ๋‹ค.
    deformed_points: Vec<Point2>,
}

impl Line {
    // ๋ผ์ธ ์ƒ์„ฑ์ž: ์‹œ์ž‘์ , ๋์ , ๊ทธ๋ฆฌ๊ณ  ์ ์˜ ๊ฐœ์ˆ˜๋ฅผ ๋ฐ›์•„ ๋ผ์ธ์„ ์ดˆ๊ธฐํ™”ํ•ฉ๋‹ˆ๋‹ค.
    // `segments`๋Š” ๋ผ์ธ์„ ๋ช‡ ๊ฐœ์˜ ์ ์œผ๋กœ ๋‚˜๋ˆŒ์ง€ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค (๋งŽ์„์ˆ˜๋ก ๋ถ€๋“œ๋Ÿฌ์šด ๋ณ€ํ˜•).
    fn new(start: Point2, end: Point2, segments: usize) -> Self {
        let mut original_points = Vec::new();
        
        // ์‹œ์ž‘์ ์—์„œ ๋์ ๊นŒ์ง€ ๊ท ๋“ฑํ•˜๊ฒŒ ๋ถ„ํ• ๋œ ์ ๋“ค์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
        // ์„ ํ˜• ๋ณด๊ฐ„(linear interpolation)์„ ์‚ฌ์šฉํ•˜์—ฌ ์ค‘๊ฐ„ ์ ๋“ค์„ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค.
        for i in 0..=segments {
            // t๋Š” 0.0์—์„œ 1.0๊นŒ์ง€์˜ ๋ณด๊ฐ„ ๋น„์œจ์ž…๋‹ˆ๋‹ค.
            // i=0์ผ ๋•Œ t=0.0 (์‹œ์ž‘์ ), i=segments์ผ ๋•Œ t=1.0 (๋์ )
            let t = i as f32 / segments as f32;
            
            // ์„ ํ˜• ๋ณด๊ฐ„ ๊ณต์‹: point = start + t * (end - start)
            // start * (1-t) + end * t ์™€ ๋™์ผํ•œ ๊ฒฐ๊ณผ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
            let x = start.x + t * (end.x - start.x);
            let y = start.y + t * (end.y - start.y);
            
            original_points.push(pt2(x, y));
        }
        
        // ์ดˆ๊ธฐ์—๋Š” ๋ณ€ํ˜•๋œ ์ ๋“ค๋„ ์›๋ณธ๊ณผ ๋™์ผํ•˜๊ฒŒ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
        let deformed_points = original_points.clone();
        
        Line {
            original_points,
            deformed_points,
        }
    }
    
    // ๋…ธ์ด์ฆˆ ํ•„๋“œ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋ผ์ธ์„ ๋ณ€ํ˜•์‹œํ‚ต๋‹ˆ๋‹ค.
    // `perlin`: ๋…ธ์ด์ฆˆ ์ƒ์„ฑ๊ธฐ
    // `time`: ์‹œ๊ฐ„ ์˜คํ”„์…‹ (์• ๋‹ˆ๋ฉ”์ด์…˜์„ ์œ„ํ•ด ์‚ฌ์šฉ)
    // `strength`: ๋ณ€ํ˜•์˜ ๊ฐ•๋„ (๊ฐ’์ด ํด์ˆ˜๋ก ๋” ๋งŽ์ด ํœ˜์–ด์ง)
    fn update(&mut self, perlin: &Perlin, time: f64, strength: f32) {
        // ๊ฐ ์ ์— ๋Œ€ํ•ด ๋…ธ์ด์ฆˆ ๊ธฐ๋ฐ˜ ๋ณ€ํ˜•์„ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.
        for i in 0..self.original_points.len() {
            let original = self.original_points[i];
            
            // ํ˜„์žฌ ์ ์˜ ์œ„์น˜์—์„œ ๋…ธ์ด์ฆˆ ๊ฐ’์„ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค.
            // 3D ๋…ธ์ด์ฆˆ๋ฅผ ์‚ฌ์šฉ: (x, y, time)
            // - x, y๋ฅผ 0.003๋ฐฐ๋กœ ์Šค์ผ€์ผ๋งํ•˜์—ฌ ๋ถ€๋“œ๋Ÿฌ์šด ๋ณ€ํ˜•์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค.
            //   (์Šค์ผ€์ผ ๊ฐ’์ด ์ž‘์„์ˆ˜๋ก ๋” ํฐ ํŒจํ„ด, ๋ถ€๋“œ๋Ÿฌ์šด ๋ณ€ํ˜•)
            // - time์„ ์„ธ ๋ฒˆ์งธ ์ฐจ์›์œผ๋กœ ์‚ฌ์šฉํ•˜์—ฌ ์‹œ๊ฐ„์— ๋”ฐ๋ผ ๋…ธ์ด์ฆˆ๊ฐ€ ๋ณ€ํ•ฉ๋‹ˆ๋‹ค.
            let noise_x = perlin.get([
                original.x as f64 * 0.004,
                original.y as f64 * 0.001,
                time,
            ]);
            
            // y ๋ฐฉํ–ฅ ๋ณ€ํ˜•์„ ์œ„ํ•œ ๋…๋ฆฝ์ ์ธ ๋…ธ์ด์ฆˆ๋ฅผ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค.
            // 10000.0 ์˜คํ”„์…‹์„ ์ถ”๊ฐ€ํ•˜์—ฌ x์™€ y๊ฐ€ ์„œ๋กœ ๋‹ค๋ฅธ ๋…ธ์ด์ฆˆ ํŒจํ„ด์„ ๋ฐ›๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.
            // ์˜คํ”„์…‹์ด ์—†์œผ๋ฉด x์™€ y๊ฐ€ ๋™์ผํ•œ ํŒจํ„ด์œผ๋กœ ๋ณ€ํ˜•๋˜์–ด ๋Œ€๊ฐ์„  ๋ฐฉํ–ฅ์œผ๋กœ๋งŒ ์›€์ง์ž…๋‹ˆ๋‹ค.
            let noise_y = perlin.get([
                original.x as f64 * 0.004 + 10000.0, // offset ์ถ”๊ฐ€
                original.y as f64 * 0.001 + 10000.0, // offset ์ถ”๊ฐ€
                time,
            ]);
            
            // ๋…ธ์ด์ฆˆ ๊ฐ’์„ ๋ณ€์œ„(displacement)๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
            // Perlin ๋…ธ์ด์ฆˆ๋Š” -1.0 ~ 1.0 ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๋ฏ€๋กœ, ์ด๋ฅผ ์ง์ ‘ ๋ณ€์œ„๋กœ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
            // `strength`๋ฅผ ๊ณฑํ•˜์—ฌ ๋ณ€ํ˜•์˜ ์ •๋„๋ฅผ ์กฐ์ ˆํ•ฉ๋‹ˆ๋‹ค.
            let offset_x = noise_x as f32 * strength;
            let offset_y = noise_y as f32 * strength;
            
            // ์›๋ณธ ์œ„์น˜์— ๋ณ€์œ„๋ฅผ ๋”ํ•ด ๋ณ€ํ˜•๋œ ์œ„์น˜๋ฅผ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค.
            // ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋ผ์ธ์ด ๋…ธ์ด์ฆˆ ํ•„๋“œ๋ฅผ ๋”ฐ๋ผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ํœ˜์–ด์ง‘๋‹ˆ๋‹ค.
            self.deformed_points[i] = pt2(
                original.x + offset_x,
                original.y + offset_y,
            );
        }
    }
}

// =============================================================================
// 3. ๋ชจ๋ธ ์ •์˜ (State Structure)
// =============================================================================
// `Model` ๊ตฌ์กฐ์ฒด๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ „์ฒด ์ƒํƒœ๋ฅผ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.
struct Model {
    // Perlin ๋…ธ์ด์ฆˆ ์ƒ์„ฑ๊ธฐ ์ธ์Šคํ„ด์Šค
    perlin: Perlin,
    
    // ํ™”๋ฉด์— ๊ทธ๋ ค์งˆ ๋ชจ๋“  ๋ผ์ธ๋“ค์˜ ๋ฒกํ„ฐ
    lines: Vec<Line>,
    
    // ์‹œ๊ฐ„ ์˜คํ”„์…‹: ๋…ธ์ด์ฆˆ ํ•„๋“œ๋ฅผ ์‹œ๊ฐ„์— ๋”ฐ๋ผ ๋ณ€ํ™”์‹œํ‚ค๊ธฐ ์œ„ํ•œ ๋ณ€์ˆ˜
    time_offset: f64,
}

// =============================================================================
// 4. ๋ชจ๋ธ ์ดˆ๊ธฐํ™” ํ•จ์ˆ˜ (Model Initialization)
// =============================================================================
// `model` ํ•จ์ˆ˜๋Š” ์•ฑ ์‹œ์ž‘ ์‹œ ํ•œ ๋ฒˆ๋งŒ ํ˜ธ์ถœ๋˜๋ฉฐ, ์ดˆ๊ธฐ ๋ผ์ธ๋“ค์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
fn model(app: &App) -> Model {
    // ์ƒˆ๋กœ์šด ์ฐฝ์„ ์ƒ์„ฑํ•˜๊ณ  ์†์„ฑ์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
    app.new_window()
        .size(1000, 1000)
        .title("Perlin Noise Flow Field - Line Deformation")
        .view(view)
        .build()
        .unwrap();
    
    // Perlin ๋…ธ์ด์ฆˆ ์ƒ์„ฑ๊ธฐ๋ฅผ ์ดˆ๊ธฐํ™”ํ•ฉ๋‹ˆ๋‹ค.
    let perlin = Perlin::new();
    
    // ๋ผ์ธ ๋ฒกํ„ฐ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
    let mut lines = Vec::new();
    
    // ์ˆ˜ํ‰ ๋ผ์ธ๋“ค์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
    // y์ถ• ๋ฐฉํ–ฅ์œผ๋กœ -n๋ถ€ํ„ฐ n๊นŒ์ง€ nํ”ฝ์…€ ๊ฐ„๊ฒฉ์œผ๋กœ ๋ผ์ธ์„ ๋ฐฐ์น˜ํ•ฉ๋‹ˆ๋‹ค.
    // `step_by(n)`์€ nํ”ฝ์…€๋งˆ๋‹ค ํ•˜๋‚˜์˜ ๋ผ์ธ์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
    for y in (-500..=500).step_by(20) {
        // ๊ฐ ์ˆ˜ํ‰ ๋ผ์ธ์€ ํ™”๋ฉด์˜ ์™ผ์ชฝ(-n)์—์„œ ์˜ค๋ฅธ์ชฝ(n)๊นŒ์ง€ ๊ทธ๋ ค์ง‘๋‹ˆ๋‹ค.
        // `segments: n`์€ ๊ฐ ๋ผ์ธ์„ n๊ฐœ์˜ ์ ์œผ๋กœ ๋‚˜๋ˆ•๋‹ˆ๋‹ค.
        // ์ ์ด ๋งŽ์„์ˆ˜๋ก ๋” ๋ถ€๋“œ๋Ÿฝ๊ณ  ์„ธ๋ฐ€ํ•œ ๋ณ€ํ˜•์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
        lines.push(Line::new(
            pt2(-500.0, y as f32),
            pt2(500.0, y as f32),
            100,  // ๊ฐ ๋ผ์ธ์„ n๊ฐœ์˜ ์„ธ๊ทธ๋จผํŠธ๋กœ ๋ถ„ํ• 
        ));
    }
    
    // ์ˆ˜์ง ๋ผ์ธ๋“ค์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
    // x์ถ• ๋ฐฉํ–ฅ์œผ๋กœ -n๋ถ€ํ„ฐ n๊นŒ์ง€ nํ”ฝ์…€ ๊ฐ„๊ฒฉ์œผ๋กœ ๋ผ์ธ์„ ๋ฐฐ์น˜ํ•ฉ๋‹ˆ๋‹ค.
    for x in (-500..=500).step_by(20) {
        // ๊ฐ ์ˆ˜์ง ๋ผ์ธ์€ ํ™”๋ฉด์˜ ์•„๋ž˜(-n)์—์„œ ์œ„(n)๊นŒ์ง€ ๊ทธ๋ ค์ง‘๋‹ˆ๋‹ค.
        lines.push(Line::new(
            pt2(x as f32, -500.0),
            pt2(x as f32, 500.0),
            100,  // ๊ฐ ๋ผ์ธ์„ n๊ฐœ์˜ ์„ธ๊ทธ๋จผํŠธ๋กœ ๋ถ„ํ• 
        ));
    }
    
    Model {
        perlin,
        lines,
        time_offset: 0.0,
    }
}

// =============================================================================
// 5. ์—…๋ฐ์ดํŠธ ํ•จ์ˆ˜ (Per-Frame Update Logic)
// =============================================================================
// `update` ํ•จ์ˆ˜๋Š” ๋งค ํ”„๋ ˆ์ž„๋งˆ๋‹ค ํ˜ธ์ถœ๋˜์–ด ๋…ธ์ด์ฆˆ ํ•„๋“œ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๊ณ 
// ๋ชจ๋“  ๋ผ์ธ์„ ๋ณ€ํ˜•์‹œํ‚ต๋‹ˆ๋‹ค.
fn update(_app: &App, model: &mut Model, _update: Update) {
    // ์‹œ๊ฐ„ ์˜คํ”„์…‹์„ ์ฆ๊ฐ€์‹œ์ผœ ๋…ธ์ด์ฆˆ ํ•„๋“œ๋ฅผ ๋ณ€ํ™”์‹œํ‚ต๋‹ˆ๋‹ค.
    // 0.01์€ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์†๋„๋ฅผ ์กฐ์ ˆํ•˜๋Š” ๊ฐ’์ž…๋‹ˆ๋‹ค.
    // - ๊ฐ’์ด ํด์ˆ˜๋ก ๋น ๋ฅด๊ฒŒ ๋ณ€ํ™”ํ•ฉ๋‹ˆ๋‹ค.
    // - ๊ฐ’์ด ์ž‘์„์ˆ˜๋ก ์ฒœ์ฒœํžˆ, ๋ถ€๋“œ๋Ÿฝ๊ฒŒ ๋ณ€ํ™”ํ•ฉ๋‹ˆ๋‹ค.
    model.time_offset += 0.005;
    
    // ๊ฐ ๋ผ์ธ์„ ๋…ธ์ด์ฆˆ ํ•„๋“œ์— ๋”ฐ๋ผ ๋ณ€ํ˜•์‹œํ‚ต๋‹ˆ๋‹ค.
    for line in &mut model.lines {
        // `strength: 100.0`์€ ๋ณ€ํ˜•์˜ ๊ฐ•๋„์ž…๋‹ˆ๋‹ค.
        // ์ด ๊ฐ’์ด ํด์ˆ˜๋ก ๋ผ์ธ์ด ๋” ๋งŽ์ด ํœ˜์–ด์ง‘๋‹ˆ๋‹ค.
        line.update(&model.perlin, model.time_offset, 100.0);
    }
}

// =============================================================================
// 6. ๋ทฐ ํ•จ์ˆ˜ (Rendering Logic)
// =============================================================================
// `view` ํ•จ์ˆ˜๋Š” ๋งค ํ”„๋ ˆ์ž„๋งˆ๋‹ค ํ™”๋ฉด์„ ๊ทธ๋ฆฝ๋‹ˆ๋‹ค.
fn view(app: &App, model: &Model, frame: Frame) {
    // ๋“œ๋กœ์šฐ ์ปจํ…์ŠคํŠธ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
    let draw = app.draw();
    
    // ๋ฐฐ๊ฒฝ์ƒ‰์„ HSLA ๋ชจ๋“œ๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
    draw.background().color(hsla(0.0, 0.0, 0.02, 1.0));
    
    // ๋ชจ๋“  ๋ผ์ธ์„ ๊ทธ๋ฆฝ๋‹ˆ๋‹ค.
    for line in &model.lines {
        // ๋ผ์ธ์˜ ๊ฐ ์„ธ๊ทธ๋จผํŠธ๋ฅผ ๊ทธ๋ฆฝ๋‹ˆ๋‹ค.
        // ๋ณ€ํ˜•๋œ ์ ๋“ค์„ ์ˆœํšŒํ•˜๋ฉด์„œ ์—ฐ์†๋œ ๋‘ ์  ์‚ฌ์ด์— ์„ ์„ ๊ทธ๋ฆฝ๋‹ˆ๋‹ค.
        // `.windows(2)`๋Š” ๋ฒกํ„ฐ์˜ ์—ฐ์†๋œ ๋‘ ์š”์†Œ๋ฅผ ์Šฌ๋ผ์ด๋”ฉ ์œˆ๋„์šฐ๋กœ ์ˆœํšŒํ•ฉ๋‹ˆ๋‹ค.
        // ์˜ˆ: [A, B, C, D] -> [A, B], [B, C], [C, D]
        for window in line.deformed_points.windows(2) {
            // window[0]์€ ์„ ์˜ ์‹œ์ž‘์ , window[1]์€ ๋์ ์ž…๋‹ˆ๋‹ค.
            draw.line()
                .start(window[0])
                .end(window[1])
                .weight(0.5)                        // ์„ ์˜ ๋‘๊ป˜ 1ํ”ฝ์…€
                .color(rgba(1.0, 1.0, 1.0, 0.8));   // RGBA ๋ชจ๋“œ: ์•ฝ๊ฐ„ ํˆฌ๋ช…ํ•œ ํฐ์ƒ‰
                // R=1.0, G=1.0, B=1.0 (ํฐ์ƒ‰), A=0.8 (80% ๋ถˆํˆฌ๋ช…)
        }
    }
    
    // ๋“œ๋กœ์šฐ ์ปจํ…์ŠคํŠธ์˜ ๋‚ด์šฉ์„ ํ”„๋ ˆ์ž„ ๋ฒ„ํผ์— ๋ Œ๋”๋งํ•ฉ๋‹ˆ๋‹ค.
    draw.to_frame(app, &frame).unwrap();
}

profile
Coding Art with Blender / oF / Processing / p5.js / nannou

0๊ฐœ์˜ ๋Œ“๊ธ€