Drawing a triangle

Mickey·2022년 1월 25일
0

glium

목록 보기
2/14
post-thumbnail

https://github.com/glium/glium/blob/master/book/tuto-02-triangle.md

삼각형 그리기

OpenGL은 도형을 쉽게 그릴 수 있는 기능을 제공하지는 않음
단순 삼각형을 그리든 수천 개의 폴리곤과 고급 섀도잉을 사용하여 3D모델을 그리든 동일한 메커니즘으로 동작

삼각형 하나만 그리려 해도 그래픽 파이프라인을 구축해야 하므로 Learning Curve가 가파름

삼각형을 그리기전에 준비해야 하는 두 가지가 있음

  • 삼각형을 나타내는 도형 정보 지정
  • GPU에서 동작하는 프로그램

Shape

그릴 객체의 기하학적 정보
그래픽스 프로그래밍에서는 삼각형만으로 형태를 처리
삼각형은 3개의 Vertex로 구성
삼각형을 나타내는 도형 정보 지정하는 첫번째 단계는 Vertex struct를 생성하는 것
Vertex의 컬랙션은 Vec<Vertex>로 표현됨

#[derive(Copy, Clone)]
struct Vertex {
    position: [f32; 2],
}

implement_vertex!(Vertex, position);

Vertex struct에는 위치를 나타내는 정보를 포함
OpenGL은 Pixel단위의 좌표를 사용하지 않음
OpenGL은 창의 너비와 높이가 2단위이고 원점이 창의 중앙에 있다고 간주(NDC: Normalized Device Coordinates)


위 삼각형을 코드로 표현하면 아래와 같음

let vertex1 = Vertex { position: [-0.5, -0.5 ] };
let vertex2 = Vertex { position: [ 0.0,  0.5 ] };
let vertex3 = Vertex { position: [ 0.5, -0.25] };
let shape = vec![vertex1, vertex2, vertex3];

다음 단계는 Vertex Buffer라고 하는 비디오카드의 메모리에 삼각형의 정보를 업로드

let vertex_buffer = glium::VertexBuffer::new(&display, &shape).unwrap();

현재 하나의 삼각형만을 그리므로 Index Buffer내용은 더미마커를 사용

let indices = glium::index::NoIndices(glium::index::PrimitiveType::TrianglesList);

Program

삼각형을 그리기위한 파이프라인

좌측 좌표 목록은 미리 지정한 삼각형의 좌표
GPU에 이 삼각형을 그리도록 요청할때, 각 vertex마다 동작하는 vertex shader가 실행
vertex shader는 각 vertex의 좌표가 무엇인지 GPU에게 알려주는 작은 프로그램
fragment shader는 삼각형이 화면 각 픽셀에 대응되어 그려지는 내용을 지정
fragment shader는 GPU에 각 픽셀에 필요한 색상을 알려주는 작은 프로그램

각 프로그램을 작성하기 위해서는 GLSL이라는 C언어와 유사한 프로그래밍 언어를 사용

vertex shader 코드

let vertex_shader_src = r#"
    #version 140

    in vec2 position;

    void main() {
        gl_Position = vec4(position, 0.0, 1.0);
    }
"#;

fragment shader 코드

let fragment_shader_src = r#"
    #version 140

    out vec4 color;

    void main() {
        color = vec4(1.0, 0.0, 0.0, 1.0);
    }
"#;

glium 라이브러리로 만들기 위해 아래 코드를 사용

let program = glium::Program::from_source(&display, vertex_shader_src, fragment_shader_src, None).unwrap();

그리기

let mut target = display.draw();
target.clear_color(0.0, 0.0, 1.0, 1.0);
// draw the triangle here
target.finish().unwrap();

draw the triangle here 부분에 삼각형을 그리는 코드 작성
삼각형을 그리는 코드는 아래와 같음

target.draw(&vertex_buffer, &indices, &program, &glium::uniforms::EmptyUniforms,
            &Default::default()).unwrap();

전체 소스코드

#[macro_use]
extern crate glium;

fn main() {
    #[allow(unused_imports)]
    use glium::{glutin, Surface};

    let event_loop = glutin::event_loop::EventLoop::new();
    let wb = glutin::window::WindowBuilder::new();
    let cb = glutin::ContextBuilder::new();
    let display = glium::Display::new(wb, cb, &event_loop).unwrap();

    #[derive(Copy, Clone)]
    struct Vertex {
        position: [f32; 2],
    }

    implement_vertex!(Vertex, position);

    let vertex1 = Vertex { position: [-0.5, -0.5] };
    let vertex2 = Vertex { position: [ 0.0,  0.5] };
    let vertex3 = Vertex { position: [ 0.5, -0.25] };
    let shape = vec![vertex1, vertex2, vertex3];

    let vertex_buffer = glium::VertexBuffer::new(&display, &shape).unwrap();
    let indices = glium::index::NoIndices(glium::index::PrimitiveType::TrianglesList);

    let vertex_shader_src = r#"
        #version 140
        in vec2 position;
        void main() {
            gl_Position = vec4(position, 0.0, 1.0);
        }
    "#;

    let fragment_shader_src = r#"
        #version 140
        out vec4 color;
        void main() {
            color = vec4(1.0, 0.0, 0.0, 1.0);
        }
    "#;

    let program = glium::Program::from_source(&display, vertex_shader_src, fragment_shader_src, None).unwrap();

    event_loop.run(move |event, _, control_flow| {
        let next_frame_time = std::time::Instant::now() +
            std::time::Duration::from_nanos(16_666_667);
        *control_flow = glutin::event_loop::ControlFlow::WaitUntil(next_frame_time);

        match event {
            glutin::event::Event::WindowEvent { event, .. } => match event {
                glutin::event::WindowEvent::CloseRequested => {
                    *control_flow = glutin::event_loop::ControlFlow::Exit;
                    return;
                },
                _ => return,
            },
            glutin::event::Event::NewEvents(cause) => match cause {
                glutin::event::StartCause::ResumeTimeReached { .. } => (),
                glutin::event::StartCause::Init => (),
                _ => return,
            },
            _ => return,
        }

        let mut target = display.draw();
        target.clear_color(0.0, 0.0, 1.0, 1.0);
        target.draw(&vertex_buffer, &indices, &program, &glium::uniforms::EmptyUniforms,
                    &Default::default()).unwrap();
        target.finish().unwrap();
    });
}

profile
Mickey

0개의 댓글