Perspective

Mickey·2022년 1월 27일
0

glium

목록 보기
10/14
post-thumbnail

https://github.com/glium/glium/blob/master/book/tuto-10-perspective.md

주전자를 그릴 때 셰이더에 전달하는 행렬에는 주전자 모델의 위치, 회전 및 크기가 포함

Column Major

OpenGL 에서는 Matrix가 Column Major

예)

AddressRow-major OrderColumn-major order
0a[1, 1]a[1, 1]
1a[1, 2]a[2, 1]
2a[1, 3]a[1, 2]
3a[2, 1]a[2, 2]
4a[2, 2]a[1, 3]
5a[2, 3]a[2, 3]

이전 내용에서 z 성분을 늘리거나 줄여도 변화가 없는 이유는 Orthogonal Projection 이기 때문

Correcting the perspective

멀리 있는 물체를 작게 보이게 하려면 간단히 x 및 y 좌표를 z 좌표로 나눔
z 좌표가 0일 수 있으므로 vertex shader 연산 이후 w값으로 나눔
원점이 화면 중앙에 있기 때문에 멀리있는 물체 일수록 화면 원점에 가까워 보임

Aspect ratio

현재는 렌더링된 이미지가 창의 크기에 맞추어 늘어났다 줄어들었다 하는 상태
-1 ~ 1 까지의 좌표가 창의 넓이나 높이에 맞추어지므로 정상적인 상태임

창의 넓이나 높이가 변경되더라도 화면에 표시되는 내용은 늘어나거나 줄어들지 않도록 하기위해서는 x좌표에 화면의 높이 / 너비의 비율을 곱함으로 고정 할 수 있음

Introducing the perspective matrix

let perspective = {
    let (width, height) = target.get_dimensions();
    let aspect_ratio = height as f32 / width as f32;

    let fov: f32 = 3.141592 / 3.0;
    let zfar = 1024.0;
    let znear = 0.1;

    let f = 1.0 / (fov / 2.0).tan();

    [
        [f * aspect_ratio,    0.0,              0.0              , 0.0],
        [       0.0      ,     f ,              0.0              , 0.0],
        [       0.0      ,    0.0,  (zfar+znear)/(zfar-znear)    , 1.0],
        [       0.0      ,    0.0, -(2.0*zfar*znear)/(zfar-znear), 0.0],
    ]
};

perspective projection matrix를 만들기 위해서는 4가지 파라미터가 필요

  • aspect ratio = height / width
  • fov : 카메라 시야각
  • znear : 카메라 시야각의 최소 깊이
  • zfar : 카메라 시야각의 최대 깊이

vertex shader 수정

#version 140

in vec3 position;
in vec3 normal;

out vec3 v_normal;

uniform mat4 perspective;       // new
uniform mat4 matrix;

void main() {
    v_normal = transpose(inverse(mat3(matrix))) * normal;
    gl_Position = perspective * matrix * vec4(position, 1.0);       // new
}

draw 코드 수정

target.draw((&positions, &normals), &indices, &program,
            &uniform! { matrix: matrix, perspective: perspective, u_light: light },
            &params).unwrap();

Matrix 수정

let matrix = [
    [0.01, 0.0, 0.0, 0.0],
    [0.0, 0.01, 0.0, 0.0],
    [0.0, 0.0, 0.01, 0.0],
    [0.0, 0.0, 2.0, 1.0f32]
];

전체 코드

#[macro_use]
extern crate glium;

mod teapot;

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

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

    let positions = glium::VertexBuffer::new(&display, &teapot::VERTICES).unwrap();
    let normals = glium::VertexBuffer::new(&display, &teapot::NORMALS).unwrap();
    let indices = glium::IndexBuffer::new(
        &display,
        glium::index::PrimitiveType::TrianglesList,
        &teapot::INDICES,
    )
    .unwrap();

    let light = [-1.0, 0.4, 0.9f32];

    let vertex_shader_src = r#"
    #version 150      // updated

    in vec3 position;
    in vec3 normal;
    
    out vec3 v_normal;
    
    uniform mat4 perspective;       // new
    uniform mat4 matrix;
    
    void main() {
        v_normal = transpose(inverse(mat3(matrix))) * normal;
        gl_Position = perspective * matrix * vec4(position, 1.0);       // new
    }
"#;

    let fragment_shader_src = r#"
    #version 140

    in vec3 v_normal;
    out vec4 color;
    uniform vec3 u_light;

    void main() {
        float brightness = dot(normalize(v_normal), normalize(u_light));
        vec3 dark_color = vec3(0.6, 0.0, 0.0);
        vec3 regular_color = vec3(1.0, 0.0, 0.0);
        color = vec4(mix(dark_color, regular_color, brightness), 1.0);
    }
"#;

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

    let params = glium::DrawParameters {
        depth: glium::Depth {
            test: glium::draw_parameters::DepthTest::IfLess,
            write: true,
            .. Default::default()
        },
        .. Default::default()
    };

    let mut t: f32 = -0.5;
    event_loop.run(move |event, _, control_flow| {
        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 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);

        t += 0.002;
        if t > 0.5 {
            t = -0.5;
        }

        let mut target = display.draw();
        target.clear_color_and_depth((0.3, 0.5, 0.7, 1.0), 1.0);

        let perspective = {
            let (width, height) = target.get_dimensions();
            let aspect_ratio = height as f32 / width as f32;
    
            let fov: f32 = 3.141592 / 3.0;
            let zfar = 1024.0;
            let znear = 0.1;
    
            let f = 1.0 / (fov / 2.0).tan();
    
            [
                [f *   aspect_ratio   ,    0.0,              0.0              ,   0.0],
                [         0.0         ,     f ,              0.0              ,   0.0],
                [         0.0         ,    0.0,  (zfar+znear)/(zfar-znear)    ,   1.0],
                [         0.0         ,    0.0, -(2.0*zfar*znear)/(zfar-znear),   0.0],
            ]
        };
        
        let uniforms = uniform! {
            // matrix: [
            //     [ t.cos(), t.sin(), 0.0, 0.0],
            //     [-t.sin(), t.cos(), 0.0, 0.0],
            //     [0.0, 0.0, 1.0, 0.0],
            //     [0.0, 0.0, 0.0, 1.0f32],
            // ]
            matrix: [
                [0.01, 0.0, 0.0, 0.0],
                [0.0, 0.01, 0.0, 0.0],
                [0.0, 0.0, 0.01, 0.0],
                [0.0, 0.0, 2.0, 1.0f32]
            ],
            perspective: perspective,
            u_light: light
        };
        
        target
            .draw(
                (&positions, &normals),
                &indices,
                &program,
                &uniforms,
                &params,
            )
            .unwrap();
        target.finish().unwrap();
    });
}

profile
Mickey

0개의 댓글