This uses the same principles as the custom drawable object but is a little more complicated on the GLSL side (which I don't really go into here) ```rust use glium::framebuffer::SimpleFrameBuffer; use glium::uniform; use glium::Surface; use lumenpyx::lights::LightDrawable; use lumenpyx::lights::DEFAULT_LIGHT_BLENDING; use lumenpyx::shaders::FULL_SCREEN_QUAD; use lumenpyx::LumenpyxProgram; use lumenpyx::Transform; pub(crate) const POINT_LIGHT_VERTEX_SHADER_SRC: &str = include_str!("../shaders/shading/lighting/point_light.vert"); pub(crate) const POINT_LIGHT_FRAGMENT_SHADER_SRC: &str = include_str!("../shaders/shading/lighting/point_light.frag"); pub struct PointLight { position: [f32; 3], color: [f32; 3], intensity: f32, falloff: f32, } impl LightDrawable for PointLight { fn draw( &self, program: &LumenpyxProgram, matrix_transform: [[f32; 4]; 4], albedo_framebuffer: &mut SimpleFrameBuffer, height_uniform: glium::uniforms::Sampler, albedo_uniform: glium::uniforms::Sampler, reflection_uniform: glium::uniforms::Sampler, shadow_strength_uniform: glium::uniforms::Sampler, ) { let display = &program.display; let indices = &program.indices; // get the shader you loaded in in the load_shaders function let shader = &program.get_shader("point_light_shader").unwrap(); let shape = FULL_SCREEN_QUAD; // the magic numbers are to transform the light position from -1.0 to 1.0 to 0.0 to 1.0 let light_pos = [ ((matrix_transform[3][0]) + 1.0) * 0.5, ((matrix_transform[3][1]) + 1.0) * 0.5, self.position[2] * matrix_transform[2][2], ]; let vertex_buffer = glium::VertexBuffer::new(display, &shape).unwrap(); // provide all the uniforms mentioned in your shader let uniforms = &uniform! { heightmap: height_uniform, albedomap: albedo_uniform, shadow_strength_map: shadow_strength_uniform, light_pos: light_pos, light_color: self.color, light_intensity: self.intensity, light_falloff: self.falloff, }; // be careful with the blending function here // it should be the DEFAULT_LIGHT_BLENDING constant from the lights module albedo_framebuffer .draw( &vertex_buffer, indices, &shader, uniforms, &glium::DrawParameters { blend: DEFAULT_LIGHT_BLENDING, ..Default::default() }, ) .unwrap(); } // load the shader just like in drawable object fn try_load_shaders(&self, program: &mut LumenpyxProgram) { if program.get_shader("point_light_shader").is_none() { let shader = glium::Program::from_source( &program.display, POINT_LIGHT_VERTEX_SHADER_SRC, POINT_LIGHT_FRAGMENT_SHADER_SRC, None, ) .unwrap(); program.add_shader(shader, "point_light_shader"); } } /// this is implemented for every custom light so it can be adjusted for the camera fn get_transform(&self) -> lumenpyx::Transform { Transform::new(self.position) } fn set_transform(&mut self, transform: Transform) { self.position = [transform.get_x(), transform.get_y(), transform.get_z()]; } } ```