Skip to main content

three_d/renderer/material/
color_material.rs

1use crate::core::*;
2use crate::renderer::*;
3
4///
5/// A material that renders a [Geometry] in a color defined by multiplying a color with an optional texture and optional per vertex colors.
6/// This material is not affected by lights.
7///
8#[derive(Clone, Default)]
9pub struct ColorMaterial {
10    /// Base surface color.
11    pub color: Srgba,
12    /// An optional texture which is samples using uv coordinates (requires that the [Geometry] supports uv coordinates).
13    /// The colors are assumed to be in linear sRGB (`RgbU8`), linear sRGB with an alpha channel (`RgbaU8`) or HDR color space.
14    pub texture: Option<Texture2DRef>,
15    /// Render states.
16    pub render_states: RenderStates,
17    /// Whether this material should be treated as a transparent material (An object needs to be rendered differently
18    /// depending on whether it is transparent or opaque). To ensure correct rendering the `blend` field of the
19    /// `render_states` attribute also needs to be configured correctly. The easiest way to do this is by using [`Self::new_transparent`].
20    pub is_transparent: bool,
21}
22
23impl ColorMaterial {
24    ///
25    /// Constructs a new color material from a [CpuMaterial].
26    /// Tries to infer whether this material is transparent or opaque from the alpha value of the albedo color and the alpha values in the albedo texture.
27    /// Since this is not always correct, it is preferred to use [ColorMaterial::new_opaque] or [ColorMaterial::new_transparent].
28    ///
29    pub fn new(context: &Context, cpu_material: &CpuMaterial) -> Self {
30        if super::is_transparent(cpu_material) {
31            Self::new_transparent(context, cpu_material)
32        } else {
33            Self::new_opaque(context, cpu_material)
34        }
35    }
36
37    /// Constructs a new opaque color material from a [CpuMaterial].
38    pub fn new_opaque(context: &Context, cpu_material: &CpuMaterial) -> Self {
39        let texture =
40            cpu_material
41                .albedo_texture
42                .as_ref()
43                .map(|cpu_texture| match &cpu_texture.data {
44                    TextureData::RgbU8(_) | TextureData::RgbaU8(_) => {
45                        let mut cpu_texture = cpu_texture.clone();
46                        cpu_texture.data.to_linear_srgb();
47                        Texture2DRef::from_cpu_texture(context, &cpu_texture)
48                    }
49                    _ => Texture2DRef::from_cpu_texture(context, cpu_texture),
50                });
51        Self {
52            color: cpu_material.albedo,
53            texture,
54            is_transparent: false,
55            render_states: RenderStates::default(),
56        }
57    }
58
59    /// Constructs a new transparent color material from a [CpuMaterial].
60    pub fn new_transparent(context: &Context, cpu_material: &CpuMaterial) -> Self {
61        let texture =
62            cpu_material
63                .albedo_texture
64                .as_ref()
65                .map(|cpu_texture| match &cpu_texture.data {
66                    TextureData::RgbU8(_) | TextureData::RgbaU8(_) => {
67                        let mut cpu_texture = cpu_texture.clone();
68                        cpu_texture.data.to_linear_srgb();
69                        Texture2DRef::from_cpu_texture(context, &cpu_texture)
70                    }
71                    _ => Texture2DRef::from_cpu_texture(context, cpu_texture),
72                });
73        Self {
74            color: cpu_material.albedo,
75            texture,
76            is_transparent: true,
77            render_states: RenderStates {
78                write_mask: WriteMask::COLOR,
79                blend: Blend::TRANSPARENCY,
80                ..Default::default()
81            },
82        }
83    }
84
85    /// Creates a color material from a [PhysicalMaterial].
86    pub fn from_physical_material(physical_material: &PhysicalMaterial) -> Self {
87        Self {
88            color: physical_material.albedo,
89            texture: physical_material.albedo_texture.clone(),
90            render_states: physical_material.render_states,
91            is_transparent: physical_material.is_transparent,
92        }
93    }
94}
95
96impl FromCpuMaterial for ColorMaterial {
97    fn from_cpu_material(context: &Context, cpu_material: &CpuMaterial) -> Self {
98        Self::new(context, cpu_material)
99    }
100}
101
102impl Material for ColorMaterial {
103    fn id(&self) -> EffectMaterialId {
104        EffectMaterialId::ColorMaterial(self.texture.is_some())
105    }
106
107    fn fragment_shader_source(&self, _lights: &[&dyn Light]) -> String {
108        let mut shader = String::new();
109        if self.texture.is_some() {
110            shader.push_str("#define USE_TEXTURE\nin vec2 uvs;\n");
111        }
112        shader.push_str(include_str!("../../core/shared.frag"));
113        shader.push_str(ColorMapping::fragment_shader_source());
114        shader.push_str(include_str!("shaders/color_material.frag"));
115        shader
116    }
117
118    fn use_uniforms(&self, program: &Program, viewer: &dyn Viewer, _lights: &[&dyn Light]) {
119        viewer.color_mapping().use_uniforms(program);
120        program.use_uniform("surfaceColor", self.color.to_linear_srgb());
121        if let Some(ref tex) = self.texture {
122            program.use_uniform("textureTransformation", tex.transformation);
123            program.use_texture("tex", tex);
124        }
125    }
126    fn render_states(&self) -> RenderStates {
127        self.render_states
128    }
129    fn material_type(&self) -> MaterialType {
130        if self.is_transparent {
131            MaterialType::Transparent
132        } else {
133            MaterialType::Opaque
134        }
135    }
136}