mirror of
https://github.com/italicsjenga/mini_gl_fb.git
synced 2024-11-22 23:41:30 +11:00
Allow switching out the default shaders
This commit is contained in:
parent
18fac37072
commit
e6da32862e
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "mini_gl_fb"
|
||||
version = "0.1.1"
|
||||
version = "0.2.0"
|
||||
authors = ["shivshank"]
|
||||
|
||||
[dependencies]
|
||||
|
|
123
examples/custom_shaders.rs
Normal file
123
examples/custom_shaders.rs
Normal file
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
|
70
src/lib.rs
70
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<S: ToString>(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<S: ToString>(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<T: ToGlType>(&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<GLuint> {
|
|||
}
|
||||
}
|
||||
|
||||
fn rebuild_shader(shader: &mut Option<GLuint>, 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>]) -> GLuint {
|
||||
let program = rustic_gl::raw::create_program()
|
||||
.unwrap();
|
||||
|
|
Loading…
Reference in a new issue