use std::sync::mpsc::Receiver; use std::sync::Arc; use glfw::{Context, Glfw, Window, WindowEvent}; use glow::HasContext; use librashader_common::{GetSize, Size, Viewport}; use librashader_runtime_gl::{FilterChainGL, GLFramebuffer, GLImage}; const WIDTH: u32 = 800; const HEIGHT: u32 = 600; const TITLE: &str = "librashader OpenGL 3.3"; pub fn setup() -> ( Glfw, Window, Receiver<(f64, WindowEvent)>, glow::Program, glow::VertexArray, Arc, ) { let mut glfw = glfw::init(glfw::FAIL_ON_ERRORS).unwrap(); glfw.window_hint(glfw::WindowHint::ContextVersion(3, 3)); glfw.window_hint(glfw::WindowHint::OpenGlProfile( glfw::OpenGlProfileHint::Core, )); glfw.window_hint(glfw::WindowHint::OpenGlForwardCompat(true)); glfw.window_hint(glfw::WindowHint::Resizable(true)); glfw.window_hint(glfw::WindowHint::OpenGlDebugContext(true)); let (mut window, events) = glfw .create_window(WIDTH, HEIGHT, TITLE, glfw::WindowMode::Windowed) .unwrap(); let (screen_width, screen_height) = window.get_framebuffer_size(); window.make_current(); window.set_key_polling(true); let mut gl = unsafe { glow::Context::from_loader_function(|ptr| window.get_proc_address(ptr)) }; unsafe { gl.enable(glow::DEBUG_OUTPUT); gl.enable(glow::DEBUG_OUTPUT_SYNCHRONOUS); gl.debug_message_callback(super::debug_callback); gl.debug_message_control(glow::DONT_CARE, glow::DONT_CARE, glow::DONT_CARE, &[], true); } unsafe { gl.viewport(0, 0, screen_width, screen_height); gl.clear_color(0.4, 0.4, 0.4, 1.0); } // ------------------------------------------- const VERT_SHADER: &str = "#version 330 core layout (location = 0) in vec3 Position; layout (location = 1) in vec3 Color; out VS_OUTPUT { vec3 Color; } OUT; void main() { gl_Position = vec4(Position, 1.0); OUT.Color = Color; }"; const FRAG_SHADER: &str = "#version 330 core in VS_OUTPUT { vec3 Color; } IN; layout(location = 0) out vec4 Color; void main() { Color = vec4(IN.Color, 1.0f); }"; let shader_program = super::compile_program(&gl, VERT_SHADER, FRAG_SHADER); // unsafe { // glow::ObjectLabel( // glow::SHADER, // shader_program, // -1, // b"color_shader\0".as_ptr().cast(), // ); // } let vertices = &[ // positions // colors 0.5f32, -0.5, 0.0, 1.0, 0.0, 0.0, // bottom right -0.5, -0.5, 0.0, 0.0, 1.0, 0.0, // bottom left 0.0, 0.5, 0.0, 0.0, 0.0, 1.0, // top ]; let vbo; unsafe { vbo = gl.create_buffer().unwrap(); // glow::ObjectLabel(glow::BUFFER, vbo, -1, b"triangle_vbo\0".as_ptr().cast()); } unsafe { gl.bind_buffer(glow::ARRAY_BUFFER, Some(vbo)); gl.buffer_data_u8_slice( glow::ARRAY_BUFFER, // target bytemuck::cast_slice(vertices), glow::STATIC_DRAW, // usage ); gl.bind_buffer(glow::ARRAY_BUFFER, None); } // set up vertex array object let vao; unsafe { vao = gl.create_vertex_array().unwrap(); // glow::ObjectLabel(glow::VERTEX_ARRAY, vao, -1, b"triangle_vao\0".as_ptr().cast()); } unsafe { gl.bind_vertex_array(Some(vao)); gl.bind_buffer(glow::ARRAY_BUFFER, Some(vbo)); gl.enable_vertex_attrib_array(0); // this is "layout (location = 0)" in vertex shader gl.vertex_attrib_pointer_f32( 0, // index of the generic vertex attribute ("layout (location = 0)") 3, // the number of components per generic vertex attribute glow::FLOAT, // data type false, // normalized (int-to-float conversion) (6 * std::mem::size_of::()) as i32, // stride (byte offset between consecutive attributes) 0, // offset of the first component ); gl.enable_vertex_attrib_array(1); gl.vertex_attrib_pointer_f32( 1, // index of the generic vertex attribute ("layout (location = 0)") 3, // the number of components per generic vertex attribute glow::FLOAT, // data type false, // normalized (int-to-float conversion) (6 * std::mem::size_of::()) as i32, // stride (byte offset between consecutive attributes) (3 * std::mem::size_of::()) as i32, // offset of the first component ); gl.bind_buffer(glow::ARRAY_BUFFER, None); gl.bind_vertex_array(None); } // set up shared state for window unsafe { gl.viewport(0, 0, 900, 700); gl.clear_color(0.3, 0.3, 0.5, 1.0); } unsafe { // ------------------------------------------- println!("OpenGL version: {}", gl.get_parameter_string(glow::VERSION)); println!( "GLSL version: {}", gl.get_parameter_string(glow::SHADING_LANGUAGE_VERSION) ); } (glfw, window, events, shader_program, vao, Arc::new(gl)) } pub fn do_loop( gl: &Arc, mut glfw: Glfw, mut window: Window, events: Receiver<(f64, WindowEvent)>, triangle_program: glow::Program, triangle_vao: glow::VertexArray, filter: &mut FilterChainGL, ) { let mut framecount = 0; let rendered_framebuffer; let rendered_texture; let quad_vbuf; let output_texture; let output_framebuffer_handle; let output_quad_vbuf; unsafe { // do frmaebuffer rendered_framebuffer = gl.create_framebuffer().unwrap(); gl.bind_framebuffer(glow::FRAMEBUFFER, Some(rendered_framebuffer)); // glow::ObjectLabel( // glow::FRAMEBUFFER, // rendered_framebuffer, // -1, // b"rendered_framebuffer\0".as_ptr().cast(), // ); // make tetxure rendered_texture = gl.create_texture().unwrap(); gl.bind_texture(glow::TEXTURE_2D, Some(rendered_texture)); // glow::ObjectLabel( // glow::TEXTURE, // rendered_texture, // -1, // b"rendered_texture\0".as_ptr().cast(), // ); // empty image gl.tex_storage_2d( glow::TEXTURE_2D, 1, glow::RGBA8, WIDTH as i32, HEIGHT as i32, ); gl.tex_parameter_i32( glow::TEXTURE_2D, glow::TEXTURE_MAG_FILTER, glow::NEAREST as i32, ); gl.tex_parameter_i32( glow::TEXTURE_2D, glow::TEXTURE_MIN_FILTER, glow::NEAREST as i32, ); gl.tex_parameter_i32( glow::TEXTURE_2D, glow::TEXTURE_WRAP_S, glow::CLAMP_TO_EDGE as i32, ); gl.tex_parameter_i32( glow::TEXTURE_2D, glow::TEXTURE_WRAP_T, glow::CLAMP_TO_EDGE as i32, ); // set color attachment gl.framebuffer_texture_2d( glow::FRAMEBUFFER, glow::COLOR_ATTACHMENT0, glow::TEXTURE_2D, Some(rendered_texture), 0, ); gl.draw_buffer(glow::COLOR_ATTACHMENT0); if gl.check_framebuffer_status(glow::FRAMEBUFFER) != glow::FRAMEBUFFER_COMPLETE { panic!("failed to create fbo") } let fullscreen_fbo = [ -1.0f32, -1.0, 0.0, 1.0, -1.0, 0.0, -1.0, 1.0, 0.0, -1.0, 1.0, 0.0, 1.0, -1.0, 0.0, 1.0, 1.0, 0.0, ]; quad_vbuf = gl.create_buffer().unwrap(); gl.bind_buffer(glow::ARRAY_BUFFER, Some(quad_vbuf)); gl.buffer_data_u8_slice( glow::ARRAY_BUFFER, bytemuck::cast_slice(&fullscreen_fbo), glow::STATIC_DRAW, ); } unsafe { // do frmaebuffer output_framebuffer_handle = gl.create_framebuffer().unwrap(); gl.bind_framebuffer(glow::FRAMEBUFFER, Some(output_framebuffer_handle)); // glow::ObjectLabel( // glow::FRAMEBUFFER, // output_framebuffer_handle, // -1, // b"output_framebuffer\0".as_ptr().cast(), // ); // make tetxure output_texture = gl.create_texture().unwrap(); gl.bind_texture(glow::TEXTURE_2D, Some(output_texture)); // glow::ObjectLabel( // glow::TEXTURE, // output_texture, // -1, // b"output_texture\0".as_ptr().cast(), // ); // empty image gl.tex_storage_2d( glow::TEXTURE_2D, 1, glow::RGBA8, WIDTH as i32, HEIGHT as i32, ); gl.tex_parameter_i32( glow::TEXTURE_2D, glow::TEXTURE_MAG_FILTER, glow::NEAREST as i32, ); gl.tex_parameter_i32( glow::TEXTURE_2D, glow::TEXTURE_MIN_FILTER, glow::NEAREST as i32, ); gl.tex_parameter_i32( glow::TEXTURE_2D, glow::TEXTURE_WRAP_S, glow::CLAMP_TO_EDGE as i32, ); gl.tex_parameter_i32( glow::TEXTURE_2D, glow::TEXTURE_WRAP_T, glow::CLAMP_TO_EDGE as i32, ); // set color attachment gl.framebuffer_texture_2d( glow::FRAMEBUFFER, glow::COLOR_ATTACHMENT0, glow::TEXTURE_2D, Some(output_texture), 0, ); gl.draw_buffer(glow::COLOR_ATTACHMENT0); if gl.check_framebuffer_status(glow::FRAMEBUFFER) != glow::FRAMEBUFFER_COMPLETE { panic!("failed to create fbo") } let fullscreen_fbo = [ -1.0f32, -1.0, 0.0, 1.0, -1.0, 0.0, -1.0, 1.0, 0.0, -1.0, 1.0, 0.0, 1.0, -1.0, 0.0, 1.0, 1.0, 0.0, ]; output_quad_vbuf = gl.create_buffer().unwrap(); gl.bind_buffer(glow::ARRAY_BUFFER, Some(output_quad_vbuf)); gl.buffer_data_u8_slice( glow::ARRAY_BUFFER, // target bytemuck::cast_slice(&fullscreen_fbo), glow::STATIC_DRAW, // usage ); } const VERT_SHADER: &str = r"#version 150 core out vec2 v_tex; const vec2 pos[4]=vec2[4](vec2(-1.0, 1.0), vec2(-1.0,-1.0), vec2( 1.0, 1.0), vec2( 1.0,-1.0)); void main() { v_tex=0.5*pos[gl_VertexID] + vec2(0.5); gl_Position=vec4(pos[gl_VertexID], 0.0, 1.0); } "; const FRAG_SHADER: &str = r" #version 150 core in vec2 v_tex; uniform sampler2D texSampler; out vec4 color; void main() { color=texture(texSampler, v_tex); }"; let quad_programid = super::compile_program(gl, VERT_SHADER, FRAG_SHADER); let quad_vao; unsafe { quad_vao = gl.create_vertex_array().unwrap(); } let (fb_width, fb_height) = window.get_framebuffer_size(); let (vp_width, vp_height) = window.get_size(); let output = GLFramebuffer::new_from_raw( Arc::clone(gl), Some(output_texture), output_framebuffer_handle, glow::RGBA8, Size::new(vp_width as u32, vp_height as u32), 1, ); while !window.should_close() { glfw.poll_events(); for (_, event) in glfw::flush_messages(&events) { glfw_handle_event(&mut window, event); } unsafe { // render to fb gl.bind_framebuffer(glow::FRAMEBUFFER, Some(rendered_framebuffer)); gl.viewport(0, 0, vp_width, vp_height); // clear color gl.clear_color(0.3, 0.4, 0.6, 1.0); gl.clear(glow::COLOR_BUFFER_BIT); // do the drawing gl.use_program(Some(triangle_program)); // select vertices gl.bind_vertex_array(Some(triangle_vao)); // draw to bound target gl.draw_arrays(glow::TRIANGLES, 0, 3); // unselect vertices gl.bind_vertex_array(None); // unselect fbo gl.bind_framebuffer(glow::FRAMEBUFFER, None); } let viewport = Viewport { x: 0f32, y: 0f32, output: &output, mvp: None, size: output.size().unwrap(), }; let rendered = GLImage { handle: Some(rendered_texture), format: glow::RGBA8, size: Size { width: fb_width as u32, height: fb_height as u32, }, }; unsafe { filter .frame(&rendered, &viewport, framecount, None) .unwrap(); } unsafe { // texture is done now. // draw quad to screen gl.use_program(Some(quad_programid)); gl.active_texture(glow::TEXTURE0); gl.bind_texture(glow::TEXTURE_2D, Some(output_texture)); gl.bind_vertex_array(Some(quad_vao)); gl.draw_arrays(glow::TRIANGLE_STRIP, 0, 4); } framecount += 1; window.swap_buffers(); } } fn glfw_handle_event(window: &mut glfw::Window, event: glfw::WindowEvent) { use glfw::Action; use glfw::Key; use glfw::WindowEvent as Event; match event { Event::Key(Key::Escape, _, Action::Press, _) => { window.set_should_close(true); } Event::Size(width, height) => window.set_size(width, height), _ => {} } }