From e6da32862e932235420dcc3df5d6667f4ed7ac7e Mon Sep 17 00:00:00 2001 From: shivshank Date: Thu, 31 May 2018 21:58:36 -0400 Subject: [PATCH] Allow switching out the default shaders --- Cargo.toml | 2 +- examples/custom_shaders.rs | 123 +++++++++++++++++++++++++++++++++ src/default_vertex_shader.glsl | 4 +- src/lib.rs | 70 ++++++++++++++++--- 4 files changed, 187 insertions(+), 12 deletions(-) create mode 100644 examples/custom_shaders.rs diff --git a/Cargo.toml b/Cargo.toml index 1c0ca2f..dd842c8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mini_gl_fb" -version = "0.1.1" +version = "0.2.0" authors = ["shivshank"] [dependencies] diff --git a/examples/custom_shaders.rs b/examples/custom_shaders.rs new file mode 100644 index 0000000..c5dff5c --- /dev/null +++ b/examples/custom_shaders.rs @@ -0,0 +1,123 @@ +extern crate mini_gl_fb; + +/// Geometry shaders allow you to procedurally generate new geometry from the vertex data. +/// +/// This shader takes the two triangles submitted by mini_gl_fb and turns them into a circle! +const geometry_source: &str = r" + #version 330 core + + layout (triangles) in; + layout (triangle_strip, max_vertices = 8) out; + + in vec2 v_uv[]; + + out vec2 g_uv; + + vec2 midpoint(vec2 a, vec2 b) { + return (a + b) / 2.0; + } + + void main() { + // n.b., the way we do this relies almost entirely on what we know about the internals of + // mini_gl_fb, so you will need to refer to the source if you want to play with this stuff. + + vec4 center = vec4(0.0, 0.0, 0.0, 1.0); + // for the first triangle, the second (index 1) vertex is the top left + // for the second triangle, it is the bottom right. we will treat that like a direction! + vec2 dir = gl_in[1].gl_Position.xy; + + // we are turning each triangle into 4 triangles, which we output in triangle strips + // (remember this shader will get run twice, once for each input triangle) + + vec4 top_left = vec4(normalize(vec2(dir.x, -dir.y)), 0.0, 1.0); + vec4 left = vec4(sign(dir.x), 0.0, 0.0, 1.0); + vec4 bottom_left = vec4(normalize(dir), 0.0, 1.0); + vec4 bottom = vec4(0.0, sign(dir.y), 0.0, 1.0); + vec4 bottom_right = vec4(normalize(vec2(-dir.x, dir.y)), 0.0, 1.0); + + gl_Position = top_left; + g_uv = v_uv[0]; + EmitVertex(); + + gl_Position = left; + g_uv = midpoint(v_uv[0], v_uv[1]); + EmitVertex(); + + gl_Position = center; + g_uv = midpoint(v_uv[0], v_uv[2]); + EmitVertex(); + + gl_Position = bottom_left; + g_uv = v_uv[1]; + EmitVertex(); + + EndPrimitive(); + + gl_Position = bottom_left; + g_uv = v_uv[1]; + EmitVertex(); + + gl_Position = bottom; + g_uv = midpoint(v_uv[1], v_uv[2]); + EmitVertex(); + + gl_Position = center; + g_uv = midpoint(v_uv[0], v_uv[2]); + EmitVertex(); + + gl_Position = bottom_right; + g_uv = v_uv[2]; + EmitVertex(); + + EndPrimitive(); + } +"; + +const fragment_source: &str = r" + #version 330 core + + in vec2 g_uv; + + out vec4 frag_color; + + // this is the texture uploaded by calls to `update_buffer` + uniform sampler2D u_tex0; + + void main() { + vec4 sample = texture(u_tex0, g_uv); + vec4 color; + if (sample.r == 1.0) { + color = sample; + } else { + // render the uv coords as color otherwise + color = vec4(g_uv, 0.0, 1.0); + } + frag_color = color; + } +"; + +extern crate gl; + +fn main() { + let width = 800; + let height = 600; + + let mut fb = mini_gl_fb::gotta_go_fast("Hello shaders!", width, height); + + let mut buffer = vec![[128u8, 0, 0, 255]; (width * height) as usize]; + // let's write a red line into the buffer roughly along the diagonal (misses many pixels) + for i in 0..100 { + let j = i as f32 / 100.0; + let index = ((width as f32) * j * ((height as f32) + 1.0)).floor() as usize; + buffer[index] = [255, 0, 0, 255]; + } + + // Let's keep using the default vertex shader + // fb.use_vertex_shader(...); + fb.use_geometry_shader(geometry_source); + fb.use_fragment_shader(fragment_source); + + fb.update_buffer(&buffer); + + fb.persist_and_redraw(true); +} diff --git a/src/default_vertex_shader.glsl b/src/default_vertex_shader.glsl index 77f70b4..a3e9fb4 100644 --- a/src/default_vertex_shader.glsl +++ b/src/default_vertex_shader.glsl @@ -1,7 +1,7 @@ #version 330 core -layout(location = 0) in ivec2 pos; -layout(location = 1) in ivec2 uv; +layout(location = 0) in vec2 pos; +layout(location = 1) in vec2 uv; out vec2 v_uv; diff --git a/src/lib.rs b/src/lib.rs index 03a7d6e..6294e98 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,7 +13,7 @@ use gl::types::*; use std::ptr::null; -type VertexFormat = buffer_layout!([i8; 2], [u8; 2]); +type VertexFormat = buffer_layout!([f32; 2], [f32; 2]); pub fn gotta_go_fast(window_title: S, window_width: i32, window_height: i32) -> Framebuffer { let events_loop = glutin::EventsLoop::new(); @@ -61,15 +61,17 @@ pub fn gotta_go_fast(window_title: S, window_width: i32, window_hei gl::BindVertexArray(vao); gl::BindBuffer(gl::ARRAY_BUFFER, vbo); VertexFormat::declare(0); - let verts = [ - [-1i8, 1], [0, 0], - [-1, -1], [0, 1], - [1, -1], [1, 1], - [1, -1], [1, 1], - [1, 1], [1, 0], - [-1, 1], [0, 0], + + let verts: [[f32; 2]; 12] = [ + [-1., 1.], [0., 0.], // top left + [-1., -1.], [0., 1.], // bottom left + [1., -1.], [1., 1.], // bottom right + [1., -1.], [1., 1.], // bottom right + [1., 1.], [1., 0.], // top right + [-1., 1.], [0., 0.], // top left ]; - gl::BufferData(gl::ARRAY_BUFFER, (verts.len() * 2) as _, verts.as_ptr() as *const _, gl::STATIC_DRAW); + use std::mem::size_of_val; + gl::BufferData(gl::ARRAY_BUFFER, size_of_val(&verts) as _, verts.as_ptr() as *const _, gl::STATIC_DRAW); gl::BindBuffer(gl::ARRAY_BUFFER, 0); gl::BindVertexArray(0); } @@ -128,6 +130,21 @@ impl Framebuffer { }) } + pub fn use_vertex_shader(&mut self, source: &str) { + rebuild_shader(&mut self.vertex_shader, gl::VERTEX_SHADER, source); + self.relink_program(); + } + + pub fn use_fragment_shader(&mut self, source: &str) { + rebuild_shader(&mut self.fragment_shader, gl::FRAGMENT_SHADER, source); + self.relink_program(); + } + + pub fn use_geometry_shader(&mut self, source: &str) { + rebuild_shader(&mut self.geometry_shader, gl::GEOMETRY_SHADER, source); + self.relink_program(); + } + pub fn change_buffer_format(&mut self, format: BufferFormat) { self.texture_format = (format, T::to_gl_enum()); } @@ -173,6 +190,17 @@ impl Framebuffer { self.gl_window.swap_buffers().unwrap(); } } + + fn relink_program(&mut self) { + unsafe { + gl::DeleteProgram(self.program); + self.program = build_program(&[ + self.vertex_shader, + self.fragment_shader, + self.geometry_shader, + ]); + } + } } #[derive(Copy, Clone, PartialEq, Eq)] @@ -250,6 +278,30 @@ fn create_gl_buffer() -> Option { } } +fn rebuild_shader(shader: &mut Option, kind: GLenum, source: &str) { + if let Some(shader) = *shader { + unsafe { + gl::DeleteShader(shader); + } + } + let compilation_result = rustic_gl::raw::create_shader(kind, source); + match compilation_result { + Ok(gl_id) => { + *shader = Some(gl_id); + }, + Err(rustic_gl::error::GlError::ShaderCompilation(info)) => { + if let Some(log) = info { + panic!("Shader compilation failed with the following information: {}", log); + } else { + panic!("Shader compilation failed without any information.") + } + }, + Err(err) => { + panic!("An error occured while compiling shader: {}", err); + } + } +} + unsafe fn build_program(shaders: &[Option]) -> GLuint { let program = rustic_gl::raw::create_program() .unwrap();