#[macro_use]
extern crate glium;
use glium::Surface;
mod support;
use support::view_matrix;
fn main() {
let event_loop = glium::winit::event_loop::EventLoop::builder()
.build()
.expect("event loop building");
let (window, display) = glium::backend::glutin::SimpleWindowBuilder::new()
.with_title("Glium tutorial #14")
.build(&event_loop);
#[derive(Copy, Clone)]
struct Vertex {
position: [f32; 3],
normal: [f32; 3],
tex_coords: [f32; 2],
}
implement_vertex!(Vertex, position, normal, tex_coords);
let shape = glium::vertex::VertexBuffer::new(&display, &[
Vertex { position: [-1.0, 1.0, 0.0], normal: [0.0, 0.0, -1.0], tex_coords: [0.0, 1.0] },
Vertex { position: [ 1.0, 1.0, 0.0], normal: [0.0, 0.0, -1.0], tex_coords: [1.0, 1.0] },
Vertex { position: [-1.0, -1.0, 0.0], normal: [0.0, 0.0, -1.0], tex_coords: [0.0, 0.0] },
Vertex { position: [ 1.0, -1.0, 0.0], normal: [0.0, 0.0, -1.0], tex_coords: [1.0, 0.0] },
]).unwrap();
let image = image::load(std::io::Cursor::new(&include_bytes!("../book/resources/tuto-14-diffuse.jpg")),
image::ImageFormat::Jpeg).unwrap().to_rgba8();
let image_dimensions = image.dimensions();
let image = glium::texture::RawImage2d::from_raw_rgba_reversed(&image.into_raw(), image_dimensions);
let diffuse_texture = glium::texture::Texture2d::new(&display, image).unwrap();
let image = image::load(std::io::Cursor::new(&include_bytes!("../book/resources/tuto-14-normal.png")),
image::ImageFormat::Png).unwrap().to_rgba8();
let image_dimensions = image.dimensions();
let image = glium::texture::RawImage2d::from_raw_rgba_reversed(&image.into_raw(), image_dimensions);
let normal_map = glium::texture::Texture2d::new(&display, image).unwrap();
let vertex_shader_src = r#"
#version 150
in vec3 position;
in vec3 normal;
in vec2 tex_coords;
out vec3 v_normal;
out vec3 v_position;
out vec2 v_tex_coords;
uniform mat4 perspective;
uniform mat4 view;
uniform mat4 model;
void main() {
v_tex_coords = tex_coords;
mat4 modelview = view * model;
v_normal = transpose(inverse(mat3(modelview))) * normal;
gl_Position = perspective * modelview * vec4(position, 1.0);
v_position = gl_Position.xyz / gl_Position.w;
}
"#;
let fragment_shader_src = r#"
#version 140
in vec3 v_normal;
in vec3 v_position;
in vec2 v_tex_coords;
out vec4 color;
uniform vec3 u_light;
uniform sampler2D diffuse_tex;
uniform sampler2D normal_tex;
const vec3 specular_color = vec3(1.0, 1.0, 1.0);
mat3 cotangent_frame(vec3 normal, vec3 pos, vec2 uv) {
vec3 dp1 = dFdx(pos);
vec3 dp2 = dFdy(pos);
vec2 duv1 = dFdx(uv);
vec2 duv2 = dFdy(uv);
vec3 dp2perp = cross(dp2, normal);
vec3 dp1perp = cross(normal, dp1);
vec3 T = dp2perp * duv1.x + dp1perp * duv2.x;
vec3 B = dp2perp * duv1.y + dp1perp * duv2.y;
float invmax = inversesqrt(max(dot(T, T), dot(B, B)));
return mat3(T * invmax, B * invmax, normal);
}
void main() {
vec3 diffuse_color = texture(diffuse_tex, v_tex_coords).rgb;
vec3 ambient_color = diffuse_color * 0.1;
vec3 v_normal_unit = normalize(v_normal);
vec3 normal_map = texture(normal_tex, v_tex_coords).rgb;
mat3 tbn = cotangent_frame(v_normal_unit, -v_position, v_tex_coords);
vec3 real_normal = normalize(tbn * -(normal_map * 2.0 - 1.0));
float diffuse = max(dot(real_normal, normalize(u_light)), 0.0);
vec3 camera_dir = normalize(-v_position);
vec3 half_direction = normalize(normalize(u_light) + camera_dir);
float specular = pow(max(dot(half_direction, real_normal), 0.0), 16.0);
color = vec4(ambient_color + diffuse * diffuse_color + specular * specular_color, 1.0);
}
"#;
let program = glium::Program::from_source(&display, vertex_shader_src, fragment_shader_src,
None).unwrap();
let start = std::time::Instant::now();
#[allow(deprecated)]
event_loop.run(move |ev, window_target| {
match ev {
glium::winit::event::Event::WindowEvent { event, .. } => match event {
glium::winit::event::WindowEvent::CloseRequested => {
window_target.exit();
},
glium::winit::event::WindowEvent::RedrawRequested => {
let mut target = display.draw();
target.clear_color_and_depth((0.0, 0.0, 1.0, 1.0), 1.0);
let t = (std::time::Instant::now() - start).as_secs_f32() * 2.0;
let ang = t.sin();
let (c, s) = (ang.cos(), ang.sin());
let model = [
[ c, 0.0, s, 0.0],
[0.0, 1.0, 0.0, 0.0],
[ -s, 0.0, c, 0.0],
[0.0, 0.0, 0.0, 1.0f32]
];
let view = view_matrix(&[0.5, 0.2, -3.0], &[-0.5, -0.2, 3.0], &[0.0, 1.0, 0.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 light = [1.4, 0.4, 0.7f32];
let params = glium::DrawParameters {
depth: glium::Depth {
test: glium::draw_parameters::DepthTest::IfLess,
write: true,
.. Default::default()
},
.. Default::default()
};
target.draw(&shape, glium::index::NoIndices(glium::index::PrimitiveType::TriangleStrip), &program,
&uniform! { model: model, view: view, perspective: perspective,
u_light: light, diffuse_tex: &diffuse_texture, normal_tex: &normal_map },
¶ms).unwrap();
target.finish().unwrap();
},
glium::winit::event::WindowEvent::Resized(window_size) => {
display.resize(window_size.into());
},
_ => (),
},
glium::winit::event::Event::AboutToWait => {
window.request_redraw();
},
_ => (),
}
})
.unwrap();
}