diff --git a/src/core.rs b/src/core.rs index ad2f3c7..98347be 100644 --- a/src/core.rs +++ b/src/core.rs @@ -79,7 +79,7 @@ pub fn init_framebuffer( }; let sampler_location = unsafe { - let location = gl::GetUniformLocation(program, b"u_tex0\0".as_ptr() as *const _); + let location = gl::GetUniformLocation(program, b"u_buffer\0".as_ptr() as *const _); gl::UseProgram(program); gl::Uniform1i(location, 0); gl::UseProgram(0); @@ -323,6 +323,11 @@ impl Framebuffer { self.relink_program(); } + pub fn use_post_process_shader(&mut self, source: &str) { + let source = make_post_process_shader(source); + self.use_fragment_shader(&source); + } + pub fn use_geometry_shader(&mut self, source: &str) { rebuild_shader(&mut self.geometry_shader, gl::GEOMETRY_SHADER, source); self.relink_program(); @@ -455,6 +460,27 @@ fn create_texture() -> GLuint { } } +fn make_post_process_shader(source: &str) -> String { + format!( + " + #version 330 core + + in vec2 v_uv; + + out vec4 r_frag_color; + + uniform sampler2D u_buffer; + + {} + + void main() {{ + main_image(r_frag_color, v_uv); + }} + ", + source, + ) +} + fn rebuild_shader(shader: &mut Option, kind: GLenum, source: &str) { if let Some(shader) = *shader { unsafe { diff --git a/src/default_fragment_shader.glsl b/src/default_fragment_shader.glsl index 6d826a9..8afa282 100644 --- a/src/default_fragment_shader.glsl +++ b/src/default_fragment_shader.glsl @@ -4,8 +4,8 @@ in vec2 v_uv; out vec4 frag_color; -uniform sampler2D u_tex0; +uniform sampler2D u_buffer; void main() { - frag_color = texture(u_tex0, v_uv); + frag_color = texture(u_buffer, v_uv); } diff --git a/src/grayscale_fragment_shader.glsl b/src/grayscale_fragment_shader.glsl index f40bb20..c838825 100644 --- a/src/grayscale_fragment_shader.glsl +++ b/src/grayscale_fragment_shader.glsl @@ -4,8 +4,8 @@ in vec2 v_uv; out vec4 frag_color; -uniform sampler2D u_tex0; +uniform sampler2D u_buffer; void main() { - frag_color = texture(u_tex0, v_uv).rrra; + frag_color = texture(u_buffer, v_uv).rrra; } diff --git a/src/lib.rs b/src/lib.rs index cd4baae..8e9ebcc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -188,6 +188,40 @@ impl MiniGlFb { self.internal.redraw(); } + /// Use a custom post process shader written in GLSL (version 330 core). + /// + /// The interface is unapologetically similar to ShaderToy's. It works by inserting your code + /// (it is implemented as literal substitution) into a supplied fragment shader and calls + /// a function `main_image` that it assumes you define. + /// + /// # Example usage + /// + /// The behavior of the default fragment shader can be emulated by the following: + /// + /// ```rust + /// fb.use_post_process_shader(" + /// void main_image( out vec4 r_frag_color, in vec2 v_uv ) { + /// r_frag_color = texture(u_buffer, v_uv); + /// } + /// "); + /// ``` + /// + /// Regardless of the format of your buffer, the internal texture is always stored as RGBA, + /// so sampling u_buffer will yield a vec4 representing an RGBA color. The built in grayscale + /// shader, for instance, only stores Red components, and then uses the red component for the + /// green and blue components to create gray. + /// + /// The output color is determined by the value of the first output parameter, `r_frag_color`. + /// Your buffer is accessible as a 2D sampler uniform named `u_buffer`. The first input + /// parameter `v_uv` is a vec2 UV coordinate. UV (0, 0) represents the upper left of the screen + /// and (1, 1) represents the bottom right. + /// + /// An API for exposing more built in and custom uniforms is planned, along with support for + /// an arbitrary number of render targets and possibly more user supplied textures. + pub fn use_post_process_shader(&mut self, source: &str) { + self.internal.fb.use_post_process_shader(source); + } + /// Changes the format of the image buffer. /// /// OpenGL will interpret any missing components as 0, except the alpha which it will assume is