Skip to content

Creating Custom Lights

Ethan Almloff edited this page Mar 27, 2024 · 1 revision

This uses the same principles as the custom drawable object but takes them to the next level.

use lumenpyx::lights::DEFAULT_LIGHT_BLENDING;
use lumenpyx::LumenpyxProgram;
use lumenpyx::shaders::FULL_SCREEN_QUAD;
use glium::framebuffer::SimpleFrameBuffer;
use lumenpyx::lights::LightDrawable;
use glium::uniform;
use glium::Surface;

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<glium::texture::Texture2d>,
        albedo_uniform: glium::uniforms::Sampler<glium::texture::Texture2d>,
        reflection_uniform: glium::uniforms::Sampler<glium::texture::Texture2d>,
        shadow_strength_uniform: glium::uniforms::Sampler<glium::texture::Texture2d>,
    ) {
        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) -> [[f32; 4]; 4] {
        [
            [1.0, 0.0, 0.0, 0.0],
            [0.0, 1.0, 0.0, 0.0],
            [0.0, 0.0, 1.0, 0.0],
            [self.position[0], self.position[1], self.position[2], 0.0],
        ]
    }
}