๐Ÿ”ฎ :: Perlin Noise Flow 2

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

Nannou <Generative Art>

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

๐Ÿ“ Rust Code

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

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

// =============================================================================
// 1. ๋ฉ”์ธ ํ•จ์ˆ˜ (Application Entry Point)
// =============================================================================
// Rust ํ”„๋กœ๊ทธ๋žจ์˜ ์ง„์ž…์ ์ž…๋‹ˆ๋‹ค.
// nannou ์•ฑ์„ ์ดˆ๊ธฐํ™”ํ•˜๊ณ  ์ด๋ฒคํŠธ ๋ฃจํ”„๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.
fn main() {
    nannou::app(model)
        .update(update)  // ๋งค ํ”„๋ ˆ์ž„๋งˆ๋‹ค ํ˜ธ์ถœ๋  ์—…๋ฐ์ดํŠธ ํ•จ์ˆ˜ ์ง€์ •
        .run();          // ์•ฑ ์‹คํ–‰ ๋ฐ ์ด๋ฒคํŠธ ๋ฃจํ”„ ์‹œ์ž‘
}

// =============================================================================
// 2. ํŒŒํ‹ฐํด ๊ตฌ์กฐ์ฒด ์ •์˜ (Particle Structure)
// =============================================================================
// `Particle`์€ flow field๋ฅผ ๋”ฐ๋ผ ์›€์ง์ด๋Š” ๊ฐœ๋ณ„ ์ž…์ž๋ฅผ ํ‘œํ˜„ํ•ฉ๋‹ˆ๋‹ค.
struct Particle {
    // ํŒŒํ‹ฐํด์˜ ํ˜„์žฌ ์œ„์น˜ (2D ์ขŒํ‘œ)
    position: Point2,
    
    // ํŒŒํ‹ฐํด์˜ ์ด์ „ ์œ„์น˜ (๊ถค์ ์„ ๊ทธ๋ฆฌ๊ธฐ ์œ„ํ•ด ํ•„์š”)
    prev_position: Point2,
    
    // ํŒŒํ‹ฐํด์˜ ํ˜„์žฌ ์†๋„ ๋ฒกํ„ฐ
    velocity: Vec2,
    
    // ํŒŒํ‹ฐํด์ด ํ™”๋ฉด ๋ฐ–์œผ๋กœ ๋‚˜๊ฐ”๋Š”์ง€ ์—ฌ๋ถ€๋ฅผ ์ถ”์ 
    // true์ผ ๊ฒฝ์šฐ ์ƒˆ ์œ„์น˜์—์„œ ์žฌ์‹œ์ž‘๋ฉ๋‹ˆ๋‹ค.
    is_out_of_bounds: bool,
}

impl Particle {
    // ํŒŒํ‹ฐํด ์ƒ์„ฑ์ž: ๋ฌด์ž‘์œ„ ์œ„์น˜์— ํŒŒํ‹ฐํด์„ ์ดˆ๊ธฐํ™”ํ•ฉ๋‹ˆ๋‹ค.
    // `bounds`๋Š” ํ™”๋ฉด์˜ ๊ฒฝ๊ณ„๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” Rect ๊ตฌ์กฐ์ฒด์ž…๋‹ˆ๋‹ค.
    fn new(bounds: Rect) -> Self {
        // ํ™”๋ฉด์˜ ๋ฌด์ž‘์œ„ ์œ„์น˜๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
        // `random_range(min, max)`๋Š” min๊ณผ max ์‚ฌ์ด์˜ ๋ฌด์ž‘์œ„ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
        let x = random_range(bounds.left(), bounds.right());
        let y = random_range(bounds.bottom(), bounds.top());
        let position = pt2(x, y);
        
        Particle {
            position,
            prev_position: position,  // ์ดˆ๊ธฐ์—๋Š” ์ด์ „ ์œ„์น˜์™€ ํ˜„์žฌ ์œ„์น˜๊ฐ€ ๋™์ผ
            velocity: vec2(0.0, 0.0), // ์ดˆ๊ธฐ ์†๋„๋Š” 0
            is_out_of_bounds: false,
        }
    }
    
    // flow field์—์„œ ํž˜์„ ๋ฐ›์•„ ํŒŒํ‹ฐํด์„ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค.
    // `force`๋Š” ๋…ธ์ด์ฆˆ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ณ„์‚ฐ๋œ ๋ฐฉํ–ฅ ๋ฒกํ„ฐ์ž…๋‹ˆ๋‹ค.
    fn update(&mut self, force: Vec2, bounds: Rect) {
        // ์ด์ „ ์œ„์น˜๋ฅผ ํ˜„์žฌ ์œ„์น˜๋กœ ์—…๋ฐ์ดํŠธ (๊ถค์  ๊ทธ๋ฆฌ๊ธฐ์šฉ)
        self.prev_position = self.position;
        
        // ๊ฐ€์†๋„ = ํž˜ (๋‰ดํ„ด์˜ ์ œ2๋ฒ•์น™ F=ma์—์„œ ์งˆ๋Ÿ‰์„ 1๋กœ ๊ฐ€์ •)
        let acceleration = force;
        
        // ์†๋„์— ๊ฐ€์†๋„๋ฅผ ๋”ํ•ฉ๋‹ˆ๋‹ค (์†๋„ ์—…๋ฐ์ดํŠธ)
        self.velocity += acceleration;
        
        // ์†๋„์— ์ œํ•œ์„ ๋‘ก๋‹ˆ๋‹ค (๋„ˆ๋ฌด ๋น ๋ฅด๊ฒŒ ์›€์ง์ด๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€)
        // ๋ฒกํ„ฐ์˜ ํฌ๊ธฐ๋ฅผ ๊ณ„์‚ฐํ•˜๊ณ , ์ตœ๋Œ€๊ฐ’๋ณด๋‹ค ํฌ๋ฉด ์ •๊ทœํ™” ํ›„ ์ตœ๋Œ€๊ฐ’์„ ๊ณฑํ•ฉ๋‹ˆ๋‹ค.
        let max_speed = 2.0;
        let speed = self.velocity.length();
        if speed > max_speed {
            self.velocity = self.velocity.normalize() * max_speed;
        }
        
        // ์œ„์น˜๋ฅผ ์†๋„๋งŒํผ ์ด๋™์‹œํ‚ต๋‹ˆ๋‹ค
        self.position += self.velocity;
        
        // ๊ฒฝ๊ณ„ ๊ฒ€์‚ฌ: ํŒŒํ‹ฐํด์ด ํ™”๋ฉด ๋ฐ–์œผ๋กœ ๋‚˜๊ฐ”๋Š”์ง€ ํ™•์ธ
        if self.position.x < bounds.left() 
            || self.position.x > bounds.right()
            || self.position.y < bounds.bottom() 
            || self.position.y > bounds.top() 
        {
            self.is_out_of_bounds = true;
        }
    }
    
    // ํŒŒํ‹ฐํด์„ ์ƒˆ๋กœ์šด ๋ฌด์ž‘์œ„ ์œ„์น˜์—์„œ ์žฌ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.
    fn reset(&mut self, bounds: Rect) {
        let x = random_range(bounds.left(), bounds.right());
        let y = random_range(bounds.bottom(), bounds.top());
        self.position = pt2(x, y);
        self.prev_position = self.position;
        self.velocity = vec2(0.0, 0.0);
        self.is_out_of_bounds = false;
    }
}

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

// =============================================================================
// 4. ๋ชจ๋ธ ์ดˆ๊ธฐํ™” ํ•จ์ˆ˜ (Model Initialization)
// =============================================================================
// `model` ํ•จ์ˆ˜๋Š” ์•ฑ ์‹œ์ž‘ ์‹œ ํ•œ ๋ฒˆ๋งŒ ํ˜ธ์ถœ๋˜๋ฉฐ, ์ดˆ๊ธฐ ์ƒํƒœ๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
fn model(app: &App) -> Model {
    // ์ƒˆ๋กœ์šด ์ฐฝ์„ ์ƒ์„ฑํ•˜๊ณ  ์†์„ฑ์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
    app.new_window()
        .size(800, 800)                      // ์ฐฝ ํฌ๊ธฐ: 800x800 ํ”ฝ์…€
        .title("Perlin Noise Flow Field")    // ์ฐฝ ์ œ๋ชฉ
        .view(view)                          // ๋ Œ๋”๋ง ํ•จ์ˆ˜ ์ง€์ •
        .build()                             // ์ฐฝ ๋นŒ๋“œ
        .unwrap();                           // ์˜ค๋ฅ˜ ๋ฐœ์ƒ ์‹œ ํŒจ๋‹‰
    
    // Perlin ๋…ธ์ด์ฆˆ ์ƒ์„ฑ๊ธฐ๋ฅผ ์ดˆ๊ธฐํ™”ํ•ฉ๋‹ˆ๋‹ค.
    let perlin = Perlin::new();
    
    // ํŒŒํ‹ฐํด ๋ฒกํ„ฐ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
    let mut particles = Vec::new();
    
    // ํ™”๋ฉด์˜ ๊ฒฝ๊ณ„๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.
    // `app.window_rect()`๋Š” ํ˜„์žฌ ์ฐฝ์˜ ๊ฒฝ๊ณ„ ์ •๋ณด๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
    let bounds = app.window_rect();
    
    // 2000๊ฐœ์˜ ํŒŒํ‹ฐํด์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
    // ์ด ์ˆซ์ž๋ฅผ ์กฐ์ ˆํ•˜๋ฉด ๋ผ์ธ์˜ ๋ฐ€๋„๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    for _ in 0..2000 {
        particles.push(Particle::new(bounds));
    }
    
    // ์ดˆ๊ธฐํ™”๋œ Model์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
    Model {
        perlin,
        particles,
        time_offset: 0.0,  // ์‹œ๊ฐ„ ์˜คํ”„์…‹์€ 0์—์„œ ์‹œ์ž‘
    }
}

// =============================================================================
// 5. ์—…๋ฐ์ดํŠธ ํ•จ์ˆ˜ (Per-Frame Update Logic)
// =============================================================================
// `update` ํ•จ์ˆ˜๋Š” ๋งค ํ”„๋ ˆ์ž„๋งˆ๋‹ค ํ˜ธ์ถœ๋˜์–ด ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ์ง„ํ–‰์‹œํ‚ต๋‹ˆ๋‹ค.
fn update(app: &App, model: &mut Model, _update: Update) {
    // ํ™”๋ฉด ๊ฒฝ๊ณ„๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.
    let bounds = app.window_rect();
    
    // ์‹œ๊ฐ„ ์˜คํ”„์…‹์„ ์ฆ๊ฐ€์‹œ์ผœ ๋…ธ์ด์ฆˆ ํ•„๋“œ๋ฅผ ๋ณ€ํ™”์‹œํ‚ต๋‹ˆ๋‹ค.
    // 0.005๋Š” ๋ณ€ํ™” ์†๋„๋ฅผ ์กฐ์ ˆํ•˜๋Š” ๊ฐ’์ž…๋‹ˆ๋‹ค (๊ฐ’์ด ํด์ˆ˜๋ก ๋น ๋ฅด๊ฒŒ ๋ณ€ํ•จ).
    model.time_offset += 0.005;
    
    // ๊ฐ ํŒŒํ‹ฐํด์„ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค.
    for particle in &mut model.particles {
        // ํŒŒํ‹ฐํด์ด ํ™”๋ฉด ๋ฐ–์œผ๋กœ ๋‚˜๊ฐ”๋‹ค๋ฉด ์žฌ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.
        if particle.is_out_of_bounds {
            particle.reset(bounds);
        }
        
        // ํ˜„์žฌ ํŒŒํ‹ฐํด ์œ„์น˜์— ๋Œ€ํ•œ ๋…ธ์ด์ฆˆ ๊ฐ’์„ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค.
        // 3D ๋…ธ์ด์ฆˆ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉฐ, ์„ธ ๋ฒˆ์งธ ์ฐจ์›์€ ์‹œ๊ฐ„(time_offset)์ž…๋‹ˆ๋‹ค.
        // - x, y ์ขŒํ‘œ๋ฅผ 0.005๋ฐฐ๋กœ ์Šค์ผ€์ผ๋งํ•˜์—ฌ ๋…ธ์ด์ฆˆ์˜ "์ฃผํŒŒ์ˆ˜"๋ฅผ ์กฐ์ ˆํ•ฉ๋‹ˆ๋‹ค.
        //   (๊ฐ’์ด ์ž‘์„์ˆ˜๋ก ๋” ๋ถ€๋“œ๋Ÿฝ๊ณ  ํฐ ํŒจํ„ด์ด ์ƒ์„ฑ๋จ)
        // - time_offset์„ ์ถ”๊ฐ€ํ•˜์—ฌ ์‹œ๊ฐ„์— ๋”ฐ๋ผ ๋…ธ์ด์ฆˆ ํŒจํ„ด์ด ๋ณ€ํ™”ํ•ฉ๋‹ˆ๋‹ค.
        let noise_val = model.perlin.get([
            particle.position.x as f64 * 0.005,
            particle.position.y as f64 * 0.005,
            model.time_offset,
        ]);
        
        // ๋…ธ์ด์ฆˆ ๊ฐ’์„ ๊ฐ๋„๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
        // Perlin ๋…ธ์ด์ฆˆ๋Š” -1.0 ~ 1.0 ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๋ฏ€๋กœ, ์ด๋ฅผ 0 ~ 2ฯ€ (360๋„) ๋ฒ”์œ„๋กœ ๋งคํ•‘ํ•ฉ๋‹ˆ๋‹ค.
        // ์ด ๊ฐ๋„๊ฐ€ ํŒŒํ‹ฐํด์ด ์ด๋™ํ•  ๋ฐฉํ–ฅ์„ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค.
        let angle = map_range(noise_val, -1.0, 1.0, 0.0, std::f64::consts::TAU);
        
        // ๊ฐ๋„๋ฅผ ์ด์šฉํ•ด ๋‹จ์œ„ ๋ฒกํ„ฐ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
        // ์‚ผ๊ฐํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ๋„๋ฅผ x, y ์ขŒํ‘œ๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
        // cos(angle)์€ x ๋ฐฉํ–ฅ, sin(angle)์€ y ๋ฐฉํ–ฅ ์„ฑ๋ถ„์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
        // 0.1์„ ๊ณฑํ•˜์—ฌ ํž˜์˜ ํฌ๊ธฐ๋ฅผ ์กฐ์ ˆํ•ฉ๋‹ˆ๋‹ค (๊ฐ’์ด ํด์ˆ˜๋ก ๋น ๋ฅด๊ฒŒ ์›€์ง์ž„).
        let force = vec2((angle as f32).cos(), (angle as f32).sin()) * 0.1;
        
        // ํŒŒํ‹ฐํด์— ํž˜์„ ์ ์šฉํ•˜์—ฌ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค.
        particle.update(force, bounds);
    }
}

// =============================================================================
// 6. ๋ทฐ ํ•จ์ˆ˜ (Rendering Logic)
// =============================================================================
// `view` ํ•จ์ˆ˜๋Š” ๋งค ํ”„๋ ˆ์ž„๋งˆ๋‹ค ํ™”๋ฉด์„ ๊ทธ๋ฆฝ๋‹ˆ๋‹ค.
fn view(app: &App, model: &Model, frame: Frame) {
    // ๋“œ๋กœ์šฐ ์ปจํ…์ŠคํŠธ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
    let draw = app.draw();
    
    // ๋ฐฐ๊ฒฝ์„ ๋ฐ˜ํˆฌ๋ช…ํ•œ ๊ฒ€์€์ƒ‰์œผ๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
    // RGBA ๋ชจ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉฐ, ์•ŒํŒŒ๊ฐ’(0.05)์„ ๋‚ฎ๊ฒŒ ์„ค์ •ํ•˜์—ฌ ํŽ˜์ด๋“œ ํšจ๊ณผ๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค.
    // ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ด์ „ ํ”„๋ ˆ์ž„์˜ ๋ผ์ธ์ด ์„œ์„œํžˆ ์‚ฌ๋ผ์ง€๋ฉด์„œ ๊ถค์  ํšจ๊ณผ๊ฐ€ ์ƒ๊น๋‹ˆ๋‹ค.
    // ์•ŒํŒŒ๊ฐ’์ด ๋‚ฎ์„์ˆ˜๋ก ๊ถค์ ์ด ๋” ๊ธธ๊ฒŒ ๋‚จ์Šต๋‹ˆ๋‹ค.
    draw.rect()
        .x_y(0.0, 0.0)
        .w_h(800.0, 800.0)
        .color(rgba(0.0, 0.0, 0.0, 0.05));
    
    // ๊ฐ ํŒŒํ‹ฐํด์˜ ๊ถค์ ์„ ๊ทธ๋ฆฝ๋‹ˆ๋‹ค.
    for particle in &model.particles {
        // ํŒŒํ‹ฐํด์˜ ํ˜„์žฌ ์œ„์น˜์™€ ์ด์ „ ์œ„์น˜ ์‚ฌ์ด์— ์„ ์„ ๊ทธ๋ฆฝ๋‹ˆ๋‹ค.
        // ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ํŒŒํ‹ฐํด์ด ์ด๋™ํ•œ ๊ฒฝ๋กœ๊ฐ€ ๋ผ์ธ์œผ๋กœ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.
        draw.line()
            .start(particle.prev_position)  // ์„ ์˜ ์‹œ์ž‘์ 
            .end(particle.position)         // ์„ ์˜ ๋์ 
            .weight(1.0)                    // ์„ ์˜ ๋‘๊ป˜ (ํ”ฝ์…€)
            .color(rgba(1.0, 1.0, 1.0, 0.5)); // RGBA ๋ชจ๋“œ: ๋ฐ˜ํˆฌ๋ช…ํ•œ ํฐ์ƒ‰
            // R=1.0 (๋นจ๊ฐ• ์ตœ๋Œ€), G=1.0 (์ดˆ๋ก ์ตœ๋Œ€), B=1.0 (ํŒŒ๋ž‘ ์ตœ๋Œ€) = ํฐ์ƒ‰
            // A=0.5 (๋ฐ˜ํˆฌ๋ช…)
    }
    
    // ๋“œ๋กœ์šฐ ์ปจํ…์ŠคํŠธ์˜ ๋‚ด์šฉ์„ ํ”„๋ ˆ์ž„ ๋ฒ„ํผ์— ๋ Œ๋”๋งํ•ฉ๋‹ˆ๋‹ค.
    draw.to_frame(app, &frame).unwrap();
}
profile
Coding Art with Blender / oF / Processing / p5.js / nannou

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