Going deeper

Mickey·2022년 1월 30일

gfx-rs Tutorial

목록 보기
5/6
post-thumbnail

https://suhr.github.io/gsgt/

창의 크기를 조절하면 이전 내용까지의 사각형은 정사각형이 아닐 수 있음
OpenGL은 x와 y가 -1 ~ 1 까지의 정규화된 좌표를 사용
창의 크기가 변경되면 화면의 내용도 비율에 맞게 조정되어야 함

vertex와 index가 미리 정의 되어있어서 일정함

위 두 내용을 수정하기위해 내용 추가

#[derive(Debug, Clone, Copy)]
struct Square {
    pub pos: (f32, f32),
    pub size: f32,
    pub color: [f32; 3]
}

// A cube is a pile of infinitely (as continuum) many squares
// This data stucture is finite, so we call it “pseudo”
#[derive(Debug)]
struct Pseudocube {
    squares: Vec<Square>,
    ratio: f32,
}

impl Pseudocube {
    pub fn new() -> Self {
        Pseudocube {
            squares: vec![],
            ratio: 1.0,
        }
    }

    pub fn add_square(&mut self, x: f32, y: f32, size: f32, color: [f32; 3]) {
        let sq = Square {
            pos: (x, y),
            size, color
        };
        self.squares.push(sq);
    }

    pub fn get_vertices_indices(&self) -> (Vec<Vertex>, Vec<u16>) {
        let (mut vs, mut is) = (vec![], vec![]);
        for (i, sq) in self.squares.iter().enumerate() {
            let (pos, half) = (sq.pos, 0.5 * sq.size);
            let i = i as u16;

            let (hx, hy);
            if self.ratio > 1.0 {
                hx = half / self.ratio;
                hy = half;
            }
            else {
                hx = half;
                hy = half * self.ratio;
            }

            vs.extend(&[
                Vertex { pos: [pos.0 + hx, pos.1 - hy], color: sq.color },
                Vertex { pos: [pos.0 - hx, pos.1 - hy], color: sq.color },
                Vertex { pos: [pos.0 - hx, pos.1 + hy], color: sq.color },
                Vertex { pos: [pos.0 + hx, pos.1 + hy], color: sq.color },
            ]);
            is.extend(&[
                4*i, 4*i + 1, 4*i + 2, 4*i + 2, 4*i + 3, 4*i
            ]);
        }

        (vs, is)
    }

    pub fn update_ratio(&mut self, ratio: f32) {
        self.ratio = ratio
    }
}

사용 코드 수정

pub fn main() {
    let mut cube = Pseudocube::new();
    cube.add_square(0.0, 0.0, 1.0, WHITE);
    // ...
    let (vertices, indices) = cube.get_vertices_indices();
    let (vertex_buffer, mut slice) =
        factory.create_vertex_buffer_with_slice(&vertices, &*indices);
    // ...
    let mut running = true;
    let mut needs_update = false;
    while running {
        if needs_update {
            let (vs, is) = cube.get_vertices_indices();
            let (vbuf, sl) = factory.create_vertex_buffer_with_slice(&vs, &*is);

            data.vbuf = vbuf;
            slice = sl;

            needs_update = false
        }
        // ...
                Resized(w, h) => {
                    gfx_glutin::update_views(&window, &mut data.out, &mut main_depth);
                    cube.update_ratio(w as f32 / h as f32);
                    needs_update = true
                },
        // ...
    }
}

Cursor 추가

#[derive(Debug, Clone, Copy)]
enum Cursor {
    Plain((f32, f32), [f32; 3]),
    Growing((f32, f32), f32, [f32; 3])
}

impl Cursor {
    fn to_square(self) -> Square {
        match self {
            Cursor::Plain(xy, color) => Square { pos: xy, size: 0.05, color },
            Cursor::Growing(xy, size, color) => Square { pos: xy, size, color },
        }
    }
}

// ...

impl Pseudocube {
// ...
    pub fn update_cursor_position(&mut self, x: f32, y: f32) {
        let x = 2.0*x - 1.0;
        let y = -2.0*y + 1.0;
        let cursor = match self.cursor {
            Cursor::Plain(_, color) => Cursor::Plain((x, y), color),
            Cursor::Growing(_, size, color) => Cursor::Growing((x, y), size, color),
        };
        self.cursor = cursor;
    }
}
// ...
                Resized(w, h) => {
                    gfx_glutin::update_views(&window, &mut data.out, &mut main_depth);
                    cube.update_ratio(w as f32 / h as f32);
                    window_size = (w as f32, h as f32);
                    needs_update = true
                },
                MouseMoved(x, y) => {
                    cube.update_cursor_position(
                        x as f32 / window_size.0,
                        y as f32 / window_size.1
                    );
                    needs_update = true
                },

Cargo.toml의 dependencies에 rand 추가

전체 코드

#[macro_use]
extern crate gfx;

extern crate gfx_window_glutin;
extern crate glutin;

use gfx::traits::FactoryExt;
use gfx::Device;
use gfx_window_glutin as gfx_glutin;

pub type ColorFormat = gfx::format::Srgba8;
pub type DepthFormat = gfx::format::DepthStencil;

const BLACK: [f32; 4] = [0.0, 0.0, 0.0, 1.0];
const WHITE: [f32; 3] = [1.0, 1.0, 1.0];

gfx_defines! {
    vertex Vertex {
        pos: [f32; 2] = "a_Pos",
        color: [f32; 3] = "a_Color",
    }

    pipeline pipe {
        vbuf: gfx::VertexBuffer<Vertex> = (),
        out: gfx::RenderTarget<ColorFormat> = "Target0",
    }
}

#[derive(Debug, Clone, Copy)]
struct Square {
    pub pos: (f32, f32),
    pub size: f32,
    pub color: [f32; 3]
}

#[derive(Debug)]
struct Pseudocube {
    cursor: Cursor,
    squares: Vec<Square>,
    ratio: f32
}

impl Pseudocube {
    pub fn new() -> Self {
        Pseudocube {
            cursor: Cursor::Plain((0.0, 0.0), WHITE),
            squares: vec![],
            ratio: 1.0,
        }
    }

    pub fn add_square(&mut self, x: f32, y: f32, size: f32, color: [f32; 3]) {
        let sq = Square {
            pos: (x, y),
            size, color
        };
        self.squares.push(sq);
    }

    pub fn get_vertices_indices(&self)->(Vec<Vertex>, Vec<u16>) {
        let (mut vs, mut is) = (vec![], vec![]);
        for (i, sq) in self.squares.iter().enumerate() {
            let (pos, half) = (sq.pos, 0.5 * sq.size);
            let i = i as u16;

            let (hx, hy);
            if self.ratio > 1.0 {
                hx = half / self.ratio;
                hy = half;
            } else {
                hx = half;
                hy = half / self.ratio;
            }

            vs.extend(&[
                Vertex { pos: [pos.0 + hx, pos.1 - hy], color: sq.color },
                Vertex { pos: [pos.0 - hx, pos.1 - hy], color: sq.color },
                Vertex { pos: [pos.0 - hx, pos.1 + hy], color: sq.color },
                Vertex { pos: [pos.0 + hx, pos.1 + hy], color: sq.color },
            ]);

            is.extend(&[
                4 * i, 
                4 * i + 1, 
                4 * i + 2, 
                4 * i + 2, 
                4 * i + 3, 
                4 * i, 
            ]);
        }

        (vs, is)
    }

    pub fn update_ratio(&mut self, ratio: f32) {
        self.ratio = ratio;
    }

    pub fn update_cursor_position(&mut self, x: f32, y: f32) {
        let x =  2.0 * x - 1.0;
        let y = -2.0 * y + 1.0;
        let cursor = match self.cursor {
            Cursor::Plain(_, color) => Cursor::Plain((x, y), color),
            Cursor::Growing(_, size, color) => Cursor::Growing((x, y), size, color),
        };
        
        self.cursor = cursor;
    }

    pub fn start_growing(&mut self) {
        if let Cursor::Plain(xy, color) = self.cursor {
            self.cursor = Cursor::Growing(xy, 0.05, color)
        }
    }

    pub fn stop_growing(&mut self) {
        if let Cursor::Growing(xy, size, color) = self.cursor {
            self.squares.push (Cursor::Growing(xy, size, color).to_square());
            self.cursor = Cursor::Plain(xy, rand::random())
        }
    }

    pub fn tick(&mut self) {
        if let Cursor::Growing(xy, size, color) = self.cursor {
            self.cursor = Cursor::Growing(xy, size + 0.01, color)
        }
    }
}

#[derive(Debug, Clone, Copy)]
enum Cursor {
    Plain((f32, f32), [f32; 3]),
    Growing((f32, f32), f32, [f32; 3])
}

impl Cursor {
    fn to_square(self) -> Square {
        match self {
            Cursor::Plain(xy, color) => Square { pos:xy, size: 0.05, color },
            Cursor::Growing(xy, size, color) => Square { pos:xy, size: size, color },
        }
    }
}

pub fn main() {
    
    const SQUARE: [Vertex; 4] = [
        Vertex { pos: [ 0.5, -0.5], color: WHITE },
        Vertex { pos: [-0.5, -0.5], color: WHITE },
        Vertex { pos: [-0.5,  0.5], color: WHITE },
        Vertex { pos: [ 0.5,  0.5], color: WHITE },
    ];

    const INDICES: &[u16] = &[0, 1, 2, 2, 3, 0];

    let vertex_shader_src = r#"
        #version 150 core

        in vec2 a_Pos;
        in vec3 a_Color;
        out vec4 v_Color;
        
        void main() {
            v_Color = vec4(a_Color, 1.0);
            gl_Position = vec4(a_Pos, 0.0, 1.0);
        }
    "#;

    let fragment_shader_src = r#"
        #version 150 core

        in vec4 v_Color;
        out vec4 Target0;
        
        void main() {
            Target0 = v_Color;
        }
    "#;

    let events_loop = glutin::EventsLoop::new();
    let builder = glutin::WindowBuilder::new()
        .with_title("Square Toy".to_string())
        .with_dimensions(800, 800)
        .with_vsync();
    let (window, mut device, mut factory, mut main_color, mut main_depth) =
        gfx_glutin::init::<ColorFormat, DepthFormat>(builder, &events_loop);

    let mut encoder: gfx::Encoder<_, _> = factory.create_command_buffer().into();
    let pso = factory.create_pipeline_simple(
        vertex_shader_src.as_bytes(),
        fragment_shader_src.as_bytes(),
        pipe::new()
    ).unwrap();

    let mut cube = Pseudocube::new();
    cube.add_square(0.0, 0.0, 1.0, WHITE);

    let (vertices, indices) = cube.get_vertices_indices();
    let (vertex_buffer, mut slice) = factory.create_vertex_buffer_with_slice(&vertices, &*indices);

    let mut data = pipe::Data {
        vbuf: vertex_buffer,
        out: main_color.clone()
    };

    let mut running = true;
    let mut needs_update = false;
    let mut window_size = (800.0, 800.0);
    while running {
        if needs_update {
            let (vs, is) = cube.get_vertices_indices();
            let (vbuf, sl) = factory.create_vertex_buffer_with_slice(&vs, &*is);
            data.vbuf = vbuf;
            slice = sl;

            needs_update = false;
        }

        events_loop.poll_events(
            |glutin::Event::WindowEvent {
                 window_id: _,
                 event,
             }| {
                use glutin::WindowEvent::*;
                use glutin::{MouseButton, ElementState, VirtualKeyCode};
                match event {
                    KeyboardInput(_, _, Some(glutin::VirtualKeyCode::Escape), _) | Closed => {
                        running = false
                    }
                    Resized(w, h) => {
                        gfx_glutin::update_views(&window, &mut data.out, &mut main_depth);
                        cube.update_ratio(w as f32 / h as f32);
                        window_size = (w as f32, h as f32);
                        needs_update = true;
                    },
                    MouseMoved(x, y) => {
                        cube.update_cursor_position(
                            x as f32 / window_size.0,
                            y as f32 / window_size.1,
                        );
                        needs_update = true
                    }
                    MouseInput(ElementState::Pressed, MouseButton::Left) =>
                    cube.start_growing(),
                    MouseInput(ElementState::Released, MouseButton::Left) =>
                    cube.stop_growing(),
                    _ => (),
                }

                cube.tick();
            },
        );

        encoder.clear(&main_color, BLACK);
        encoder.draw(&slice, &pso, &data);
        encoder.flush(&mut device);
        window.swap_buffers().unwrap();
        device.cleanup();
    }
}

profile
Mickey

0개의 댓글