1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203

use crate::*;

#[derive(Debug)]
pub enum Error {
    Core(core::Error),
    LightExtendsMaxLimit {message: String}
}

impl From<core::Error> for Error {
    fn from(other: core::Error) -> Self {
        Error::Core(other)
    }
}

pub struct DeferredPipeline {
    gl: Gl,
    ambient_light_program: program::Program,
    directional_light_program: program::Program,
    point_light_program: program::Program,
    spot_light_program: program::Program,
    geometry_pass_texture: Option<Texture2DArray>,
    geometry_pass_depth_texture: Option<Texture2DArray>,
    full_screen_positions: VertexBuffer,
    full_screen_uvs: VertexBuffer
}


impl DeferredPipeline
{
    pub fn new(gl: &Gl) -> Result<DeferredPipeline, Error>
    {
        let ambient_light_program = program::Program::from_source(gl,
                                                               include_str!("shaders/light_pass.vert"),
                                                               include_str!("shaders/ambient_light.frag"))?;
        let directional_light_program = program::Program::from_source(gl,
                                                               include_str!("shaders/light_pass.vert"),
                                                               &format!("{}\n{}\n{}",
                                                                       &include_str!("shaders/light_shared.frag"),
                                                                       &include_str!("shaders/shadow_shared.frag"),
                                                                       &include_str!("shaders/directional_light.frag")))?;
        let point_light_program = program::Program::from_source(gl,
                                                               include_str!("shaders/light_pass.vert"),
                                                               &format!("{}\n{}",
                                                                       &include_str!("shaders/light_shared.frag"),
                                                                       &include_str!("shaders/point_light.frag")))?;
        let spot_light_program = program::Program::from_source(gl,
                                                               include_str!("shaders/light_pass.vert"),
                                                               &format!("{}\n{}\n{}",
                                                                       &include_str!("shaders/light_shared.frag"),
                                                                       &include_str!("shaders/shadow_shared.frag"),
                                                                       &include_str!("shaders/spot_light.frag")))?;

        let positions = vec![
            -3.0, -1.0, 0.0,
            3.0, -1.0, 0.0,
            0.0, 2.0, 0.0
        ];
        let uvs = vec![
            -1.0, 0.0,
            2.0, 0.0,
            0.5, 1.5
        ];
        let full_screen_positions = VertexBuffer::new_with_static_f32(&gl, &positions).unwrap();
        let full_screen_uvs = VertexBuffer::new_with_static_f32(&gl, &uvs).unwrap();

        let renderer = DeferredPipeline {
            gl: gl.clone(),
            ambient_light_program,
            directional_light_program,
            point_light_program,
            spot_light_program,
            full_screen_positions,
            full_screen_uvs,
            geometry_pass_texture: Some(Texture2DArray::new_empty(gl, 1, 1, 2,
                  Interpolation::Nearest, Interpolation::Nearest, Wrapping::ClampToEdge,
                  Wrapping::ClampToEdge, Format::RGBA8)?),
            geometry_pass_depth_texture: Some(Texture2DArray::new_empty(gl, 1, 1, 1,
                    Interpolation::Nearest, Interpolation::Nearest, Wrapping::ClampToEdge,
                    Wrapping::ClampToEdge, Format::Depth32F)?)};

        renderer.ambient_light_program.use_texture(renderer.geometry_pass_texture(), "gbuffer")?;
        renderer.ambient_light_program.use_texture(renderer.geometry_pass_depth_texture(), "depthMap")?;
        Ok(renderer)
    }

    pub fn geometry_pass(&mut self, width: usize, height: usize, render_scene: &dyn Fn()) -> Result<(), Error>
    {
        state::depth_write(&self.gl, true);
        state::depth_test(&self.gl, state::DepthTestType::LessOrEqual);
        state::cull(&self.gl, state::CullType::None);
        state::blend(&self.gl, state::BlendType::None);

        self.geometry_pass_texture = Some(Texture2DArray::new_empty(&self.gl, width, height, 2,
                  Interpolation::Nearest, Interpolation::Nearest, Wrapping::ClampToEdge,
                  Wrapping::ClampToEdge, Format::RGBA8)?);
        self.geometry_pass_depth_texture = Some(Texture2DArray::new_empty(&self.gl, width, height, 1,
                    Interpolation::Nearest, Interpolation::Nearest, Wrapping::ClampToEdge,
                    Wrapping::ClampToEdge, Format::Depth32F)?);
        RenderTarget::write_array(&self.gl,0, 0, width, height,
            Some(&vec4(0.0, 0.0, 0.0, 0.0)), Some(1.0),
            self.geometry_pass_texture.as_ref(), self.geometry_pass_depth_texture.as_ref(),
            2, &|channel| {channel},
            0, render_scene)?;
        Ok(())
    }

    pub fn light_pass(&self, camera: &Camera, ambient_light: Option<&AmbientLight>, directional_lights: &[&DirectionalLight], spot_lights: &[&SpotLight], point_lights: &[&PointLight]) -> Result<(), Error>
    {
        state::depth_write(&self.gl,false);
        state::depth_test(&self.gl, state::DepthTestType::None);
        state::cull(&self.gl,state::CullType::Back);
        state::blend(&self.gl, state::BlendType::None);

        // Ambient light
        if let Some(light) = ambient_light {

            self.ambient_light_program.use_texture(self.geometry_pass_texture(), "gbuffer")?;
            self.ambient_light_program.use_texture(self.geometry_pass_depth_texture(), "depthMap")?;

            self.ambient_light_program.add_uniform_vec3("ambientLight.base.color", &light.color())?;
            self.ambient_light_program.add_uniform_float("ambientLight.base.intensity", &light.intensity())?;

            self.ambient_light_program.use_attribute_vec3_float(&self.full_screen_positions, "position").unwrap();
            self.ambient_light_program.use_attribute_vec2_float(&self.full_screen_uvs, "uv_coordinate").unwrap();
            self.ambient_light_program.draw_arrays(3);
            state::blend(&self.gl, state::BlendType::OneOne);
        }

        // Directional light
        for light in directional_lights {
            self.directional_light_program.use_texture(self.geometry_pass_texture(), "gbuffer")?;
            self.directional_light_program.use_texture(self.geometry_pass_depth_texture(), "depthMap")?;

            self.directional_light_program.add_uniform_vec3("eyePosition", &camera.position())?;
            self.directional_light_program.add_uniform_mat4("viewProjectionInverse", &(camera.get_projection() * camera.get_view()).invert().unwrap())?;

            if let Some(texture) = light.shadow_map() {
                self.directional_light_program.use_texture(texture, "shadowMap")?;
            }
            else {
                let dummy = Texture2D::new_empty(&self.gl, 1, 1, Interpolation::Nearest, Interpolation::Nearest, Wrapping::ClampToEdge, Wrapping::ClampToEdge, Format::Depth32F).unwrap();
                self.directional_light_program.use_texture(&dummy, "shadowMap")?;
            }
            self.directional_light_program.use_uniform_block(light.buffer(), "DirectionalLight");

            self.directional_light_program.use_attribute_vec3_float(&self.full_screen_positions, "position").unwrap();
            self.directional_light_program.use_attribute_vec2_float(&self.full_screen_uvs, "uv_coordinate").unwrap();
            self.directional_light_program.draw_arrays(3);
            state::blend(&self.gl, state::BlendType::OneOne);
        }

        // Spot lights
        for light in spot_lights {
            self.spot_light_program.use_texture(self.geometry_pass_texture(), "gbuffer")?;
            self.spot_light_program.use_texture(self.geometry_pass_depth_texture(), "depthMap")?;

            self.spot_light_program.add_uniform_vec3("eyePosition", &camera.position())?;
            self.spot_light_program.add_uniform_mat4("viewProjectionInverse", &(camera.get_projection() * camera.get_view()).invert().unwrap())?;

            if let Some(texture) = light.shadow_map() {
                self.spot_light_program.use_texture(texture, "shadowMap")?;
            }
            else {
                let dummy = Texture2D::new_empty(&self.gl, 1, 1, Interpolation::Nearest, Interpolation::Nearest, Wrapping::ClampToEdge, Wrapping::ClampToEdge, Format::Depth32F).unwrap();
                self.spot_light_program.use_texture(&dummy, "shadowMap")?;
            }
            self.spot_light_program.use_uniform_block(light.buffer(), "SpotLight");

            self.spot_light_program.use_attribute_vec3_float(&self.full_screen_positions, "position").unwrap();
            self.spot_light_program.use_attribute_vec2_float(&self.full_screen_uvs, "uv_coordinate").unwrap();
            self.spot_light_program.draw_arrays(3);
            state::blend(&self.gl, state::BlendType::OneOne);
        }

        // Point lights
        for light in point_lights {
            self.point_light_program.use_texture(self.geometry_pass_texture(), "gbuffer")?;
            self.point_light_program.use_texture(self.geometry_pass_depth_texture(), "depthMap")?;

            self.point_light_program.add_uniform_vec3("eyePosition", &camera.position())?;
            self.point_light_program.add_uniform_mat4("viewProjectionInverse", &(camera.get_projection() * camera.get_view()).invert().unwrap())?;

            self.point_light_program.use_uniform_block(light.buffer(), "PointLight");

            self.point_light_program.use_attribute_vec3_float(&self.full_screen_positions, "position").unwrap();
            self.point_light_program.use_attribute_vec2_float(&self.full_screen_uvs, "uv_coordinate").unwrap();
            self.point_light_program.draw_arrays(3);
            state::blend(&self.gl, state::BlendType::OneOne);
        }

        Ok(())
    }

    pub fn geometry_pass_texture(&self) -> &Texture2DArray
    {
        &self.geometry_pass_texture.as_ref().unwrap()
    }
    pub fn geometry_pass_depth_texture(&self) -> &Texture2DArray
    {
        &self.geometry_pass_depth_texture.as_ref().unwrap()
    }
}