post_process.rs
1 #![allow(unsafe_code)] 2 use crate::check_for_gl_error; 3 use crate::misc_util::{compile_shader, link_program}; 4 use crate::vao::BufferInfo; 5 use glow::HasContext as _; 6 7 /// Uses a framebuffer to render everything in linear color space and convert it back to `sRGB` 8 /// in a separate "post processing" step 9 pub(crate) struct PostProcess { 10 gl: std::rc::Rc<glow::Context>, 11 pos_buffer: glow::Buffer, 12 index_buffer: glow::Buffer, 13 vao: crate::vao::VertexArrayObject, 14 is_webgl_1: bool, 15 texture: glow::Texture, 16 texture_size: (i32, i32), 17 fbo: glow::Framebuffer, 18 program: glow::Program, 19 } 20 21 impl PostProcess { 22 pub(crate) unsafe fn new( 23 gl: std::rc::Rc<glow::Context>, 24 shader_prefix: &str, 25 is_webgl_1: bool, 26 width: i32, 27 height: i32, 28 ) -> Result<PostProcess, String> { 29 let fbo = gl.create_framebuffer()?; 30 31 gl.bind_framebuffer(glow::FRAMEBUFFER, Some(fbo)); 32 33 let texture = gl.create_texture().unwrap(); 34 35 gl.bind_texture(glow::TEXTURE_2D, Some(texture)); 36 37 gl.tex_parameter_i32( 38 glow::TEXTURE_2D, 39 glow::TEXTURE_WRAP_S, 40 glow::CLAMP_TO_EDGE as i32, 41 ); 42 43 gl.tex_parameter_i32( 44 glow::TEXTURE_2D, 45 glow::TEXTURE_WRAP_T, 46 glow::CLAMP_TO_EDGE as i32, 47 ); 48 49 gl.tex_parameter_i32( 50 glow::TEXTURE_2D, 51 glow::TEXTURE_MIN_FILTER, 52 glow::NEAREST as i32, 53 ); 54 55 gl.tex_parameter_i32( 56 glow::TEXTURE_2D, 57 glow::TEXTURE_MAG_FILTER, 58 glow::NEAREST as i32, 59 ); 60 61 gl.pixel_store_i32(glow::UNPACK_ALIGNMENT, 1); 62 63 let (internal_format, format) = if is_webgl_1 { 64 (glow::SRGB_ALPHA, glow::SRGB_ALPHA) 65 } else { 66 (glow::SRGB8_ALPHA8, glow::RGBA) 67 }; 68 69 gl.tex_image_2d( 70 glow::TEXTURE_2D, 71 0, 72 internal_format as i32, 73 width, 74 height, 75 0, 76 format, 77 glow::UNSIGNED_BYTE, 78 None, 79 ); 80 check_for_gl_error!(&gl, "post process texture initialization"); 81 82 gl.framebuffer_texture_2d( 83 glow::FRAMEBUFFER, 84 glow::COLOR_ATTACHMENT0, 85 glow::TEXTURE_2D, 86 Some(texture), 87 0, 88 ); 89 gl.bind_texture(glow::TEXTURE_2D, None); 90 gl.bind_framebuffer(glow::FRAMEBUFFER, None); 91 92 let vert_shader = compile_shader( 93 &gl, 94 glow::VERTEX_SHADER, 95 &format!( 96 "{}\n{}", 97 shader_prefix, 98 include_str!("shader/post_vertex_100es.glsl") 99 ), 100 )?; 101 let frag_shader = compile_shader( 102 &gl, 103 glow::FRAGMENT_SHADER, 104 &format!( 105 "{}\n{}", 106 shader_prefix, 107 include_str!("shader/post_fragment_100es.glsl") 108 ), 109 )?; 110 let program = link_program(&gl, [vert_shader, frag_shader].iter())?; 111 112 let positions = vec![0.0f32, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0]; 113 114 let indices = vec![0u8, 1, 2, 1, 2, 3]; 115 116 let pos_buffer = gl.create_buffer()?; 117 gl.bind_buffer(glow::ARRAY_BUFFER, Some(pos_buffer)); 118 gl.buffer_data_u8_slice( 119 glow::ARRAY_BUFFER, 120 bytemuck::cast_slice(&positions), 121 glow::STATIC_DRAW, 122 ); 123 124 let a_pos_loc = gl 125 .get_attrib_location(program, "a_pos") 126 .ok_or_else(|| "failed to get location of a_pos".to_string())?; 127 let vao = crate::vao::VertexArrayObject::new( 128 &gl, 129 pos_buffer, 130 vec![BufferInfo { 131 location: a_pos_loc, 132 vector_size: 2, 133 data_type: glow::FLOAT, 134 normalized: false, 135 stride: 0, 136 offset: 0, 137 }], 138 ); 139 140 let index_buffer = gl.create_buffer()?; 141 gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(index_buffer)); 142 gl.buffer_data_u8_slice(glow::ELEMENT_ARRAY_BUFFER, &indices, glow::STATIC_DRAW); 143 144 gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, None); 145 check_for_gl_error!(&gl, "post process initialization"); 146 147 Ok(PostProcess { 148 gl, 149 pos_buffer, 150 index_buffer, 151 vao, 152 is_webgl_1, 153 texture, 154 texture_size: (width, height), 155 fbo, 156 program, 157 }) 158 } 159 160 pub(crate) unsafe fn begin(&mut self, width: i32, height: i32) { 161 if (width, height) != self.texture_size { 162 self.gl.bind_texture(glow::TEXTURE_2D, Some(self.texture)); 163 self.gl.pixel_store_i32(glow::UNPACK_ALIGNMENT, 1); 164 165 let (internal_format, format) = if self.is_webgl_1 { 166 (glow::SRGB_ALPHA, glow::SRGB_ALPHA) 167 } else { 168 (glow::SRGB8_ALPHA8, glow::RGBA) 169 }; 170 self.gl.tex_image_2d( 171 glow::TEXTURE_2D, 172 0, 173 internal_format as i32, 174 width, 175 height, 176 0, 177 format, 178 glow::UNSIGNED_BYTE, 179 None, 180 ); 181 182 self.gl.bind_texture(glow::TEXTURE_2D, None); 183 self.texture_size = (width, height); 184 } 185 186 self.gl.bind_framebuffer(glow::FRAMEBUFFER, Some(self.fbo)); 187 self.gl.clear_color(0.0, 0.0, 0.0, 0.0); 188 self.gl.clear(glow::COLOR_BUFFER_BIT); 189 190 check_for_gl_error!(&self.gl, "PostProcess::begin"); 191 } 192 193 pub(crate) unsafe fn bind(&self) { 194 self.gl.bind_framebuffer(glow::FRAMEBUFFER, Some(self.fbo)); 195 } 196 197 pub(crate) unsafe fn end(&self) { 198 self.gl.bind_framebuffer(glow::FRAMEBUFFER, None); 199 self.gl.disable(glow::SCISSOR_TEST); 200 201 self.gl.use_program(Some(self.program)); 202 203 self.gl.active_texture(glow::TEXTURE0); 204 self.gl.bind_texture(glow::TEXTURE_2D, Some(self.texture)); 205 let u_sampler_loc = self 206 .gl 207 .get_uniform_location(self.program, "u_sampler") 208 .unwrap(); 209 self.gl.uniform_1_i32(Some(&u_sampler_loc), 0); 210 self.vao.bind(&self.gl); 211 212 self.gl 213 .bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(self.index_buffer)); 214 self.gl 215 .draw_elements(glow::TRIANGLES, 6, glow::UNSIGNED_BYTE, 0); 216 self.vao.unbind(&self.gl); 217 self.gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, None); 218 self.gl.bind_texture(glow::TEXTURE_2D, None); 219 self.gl.use_program(None); 220 221 check_for_gl_error!(&self.gl, "PostProcess::end"); 222 } 223 224 pub(crate) unsafe fn destroy(&self) { 225 self.gl.delete_buffer(self.pos_buffer); 226 self.gl.delete_buffer(self.index_buffer); 227 self.gl.delete_program(self.program); 228 self.gl.delete_framebuffer(self.fbo); 229 self.gl.delete_texture(self.texture); 230 } 231 }