three_d/renderer/effect/
water.rs

1use crate::core::*;
2use crate::renderer::*;
3use std::sync::Arc;
4
5/// The background of the scene.
6#[derive(Clone)]
7pub enum Background {
8    /// Environnment texture.
9    Texture(Arc<TextureCubeMap>),
10    /// Background color.
11    Color(Srgba),
12}
13
14impl Default for Background {
15    fn default() -> Self {
16        Self::Color(Srgba::WHITE)
17    }
18}
19
20///
21/// An effect that simulates a water surface and should therefore only be applied to a water surface geometry.
22/// This effect needs the rendered scene (without the water surface) in a color and depth texture to be able to add reflections and refractions.
23///
24#[derive(Clone)]
25pub struct WaterEffect {
26    /// The background of the scene which is used for reflections.
27    pub background: Background,
28    /// A value in the range `[0..1]` specifying how metallic the surface is.
29    pub metallic: f32,
30    /// A value in the range `[0..1]` specifying how rough the surface is.
31    pub roughness: f32,
32    /// The lighting model used when rendering this effect
33    pub lighting_model: LightingModel,
34}
35
36impl Effect for WaterEffect {
37    fn fragment_shader_source(
38        &self,
39        lights: &[&dyn Light],
40        color_texture: Option<ColorTexture>,
41        depth_texture: Option<DepthTexture>,
42    ) -> String {
43        format!(
44            "{}\n{}\n{}\n{}\n{}\n{}\n{}",
45            match &self.background {
46                Background::Color(_) => "",
47                Background::Texture(_) => "#define USE_BACKGROUND_TEXTURE",
48            },
49            color_texture
50                .expect("Must supply a color texture to apply a water effect")
51                .fragment_shader_source(),
52            depth_texture
53                .expect("Must supply a depth texture to apply a water effect")
54                .fragment_shader_source(),
55            lights_shader_source(lights),
56            ToneMapping::fragment_shader_source(),
57            ColorMapping::fragment_shader_source(),
58            include_str!("shaders/water_effect.frag")
59        )
60    }
61
62    fn id(
63        &self,
64        color_texture: Option<ColorTexture>,
65        depth_texture: Option<DepthTexture>,
66    ) -> EffectMaterialId {
67        EffectMaterialId::WaterEffect(
68            color_texture.expect("Must supply a color texture to apply a water effect"),
69            depth_texture.expect("Must supply a depth texture to apply a water effect"),
70        )
71    }
72
73    fn render_states(&self) -> RenderStates {
74        RenderStates {
75            blend: Blend::TRANSPARENCY,
76            ..Default::default()
77        }
78    }
79
80    fn use_uniforms(
81        &self,
82        program: &Program,
83        viewer: &dyn Viewer,
84        lights: &[&dyn Light],
85        color_texture: Option<ColorTexture>,
86        depth_texture: Option<DepthTexture>,
87    ) {
88        program.use_uniform_if_required("lightingModel", lighting_model_to_id(self.lighting_model));
89        viewer.tone_mapping().use_uniforms(program);
90        viewer.color_mapping().use_uniforms(program);
91        color_texture
92            .expect("Must supply a color texture to apply a water effect")
93            .use_uniforms(program);
94        depth_texture
95            .expect("Must supply a depth texture to apply a water effect")
96            .use_uniforms(program);
97        for (i, light) in lights.iter().enumerate() {
98            light.use_uniforms(program, i as u32);
99        }
100        program.use_uniform("viewProjection", viewer.projection() * viewer.view());
101        program.use_uniform(
102            "viewProjectionInverse",
103            (viewer.projection() * viewer.view()).invert().unwrap(),
104        );
105        program.use_uniform("cameraPosition", viewer.position());
106        program.use_uniform(
107            "screenSize",
108            vec2(
109                viewer.viewport().width as f32,
110                viewer.viewport().height as f32,
111            ),
112        );
113        program.use_uniform("metallic", self.metallic);
114        program.use_uniform("roughness", self.roughness);
115        match &self.background {
116            Background::Color(color) => {
117                program.use_uniform("environmentColor", color.to_linear_srgb())
118            }
119            Background::Texture(tex) => program.use_texture_cube("environmentMap", tex),
120        }
121    }
122}
123
124impl Default for WaterEffect {
125    fn default() -> Self {
126        Self {
127            background: Background::default(),
128            metallic: 0.0,
129            roughness: 1.0,
130            lighting_model: LightingModel::Blinn,
131        }
132    }
133}