https://github.com/glium/glium/blob/master/book/tuto-10-perspective.md
주전자를 그릴 때 셰이더에 전달하는 행렬에는 주전자 모델의 위치, 회전 및 크기가 포함
OpenGL 에서는 Matrix가 Column Major
예)
Address | Row-major Order | Column-major order |
---|---|---|
0 | a[1, 1] | a[1, 1] |
1 | a[1, 2] | a[2, 1] |
2 | a[1, 3] | a[1, 2] |
3 | a[2, 1] | a[2, 2] |
4 | a[2, 2] | a[1, 3] |
5 | a[2, 3] | a[2, 3] |
이전 내용에서 z 성분을 늘리거나 줄여도 변화가 없는 이유는 Orthogonal Projection 이기 때문
멀리 있는 물체를 작게 보이게 하려면 간단히 x 및 y 좌표를 z 좌표로 나눔
z 좌표가 0일 수 있으므로 vertex shader 연산 이후 w값으로 나눔
원점이 화면 중앙에 있기 때문에 멀리있는 물체 일수록 화면 원점에 가까워 보임
현재는 렌더링된 이미지가 창의 크기에 맞추어 늘어났다 줄어들었다 하는 상태
-1 ~ 1 까지의 좌표가 창의 넓이나 높이에 맞추어지므로 정상적인 상태임
창의 넓이나 높이가 변경되더라도 화면에 표시되는 내용은 늘어나거나 줄어들지 않도록 하기위해서는 x좌표에 화면의 높이 / 너비의 비율을 곱함으로 고정 할 수 있음
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가지 파라미터가 필요
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 },
¶ms).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,
¶ms,
)
.unwrap();
target.finish().unwrap();
});
}