three_d/renderer/material/
orm_material.rs

1use crate::core::*;
2use crate::renderer::*;
3
4///
5/// Render the object with colors that reflect its ORM (occlusion, roughness and metallic) values which primarily is used for debug purposes.
6/// Occlusion is red, roughness green and metallic blue.
7///
8#[derive(Clone)]
9pub struct ORMMaterial {
10    /// A value in the range `[0..1]` specifying how metallic the material is.
11    pub metallic: f32,
12    /// A value in the range `[0..1]` specifying how rough the material surface is.
13    pub roughness: f32,
14    /// Texture containing the metallic and roughness parameters which are multiplied with the [Self::metallic] and [Self::roughness] values in the shader.
15    /// The metallic values are sampled from the blue channel and the roughness from the green channel.
16    pub metallic_roughness_texture: Option<Texture2DRef>,
17    /// A scalar multiplier controlling the amount of occlusion applied from the [Self::occlusion_texture]. A value of 0.0 means no occlusion. A value of 1.0 means full occlusion.
18    pub occlusion_strength: f32,
19    /// An occlusion map. Higher values indicate areas that should receive full indirect lighting and lower values indicate no indirect lighting.
20    /// The occlusion values are sampled from the red channel.
21    pub occlusion_texture: Option<Texture2DRef>,
22    /// Render states.
23    pub render_states: RenderStates,
24}
25
26impl ORMMaterial {
27    /// Constructs a new ORM material from a [CpuMaterial] where only relevant information is used.
28    pub fn new(context: &Context, cpu_material: &CpuMaterial) -> Self {
29        let metallic_roughness_texture =
30            if let Some(ref cpu_texture) = cpu_material.occlusion_metallic_roughness_texture {
31                Some(Texture2DRef::from_cpu_texture(context, cpu_texture))
32            } else {
33                cpu_material
34                    .metallic_roughness_texture
35                    .as_ref()
36                    .map(|cpu_texture| Texture2DRef::from_cpu_texture(context, cpu_texture))
37            };
38        let occlusion_texture = if cpu_material.occlusion_metallic_roughness_texture.is_some() {
39            metallic_roughness_texture.clone()
40        } else {
41            cpu_material
42                .occlusion_texture
43                .as_ref()
44                .map(|cpu_texture| Texture2DRef::from_cpu_texture(context, cpu_texture))
45        };
46        Self {
47            metallic: cpu_material.metallic,
48            roughness: cpu_material.roughness,
49            metallic_roughness_texture,
50            occlusion_texture,
51            occlusion_strength: cpu_material.occlusion_strength,
52            render_states: RenderStates::default(),
53        }
54    }
55
56    /// Creates a ORM material from a [PhysicalMaterial].
57    pub fn from_physical_material(physical_material: &PhysicalMaterial) -> Self {
58        Self {
59            metallic: physical_material.metallic,
60            roughness: physical_material.roughness,
61            metallic_roughness_texture: physical_material.metallic_roughness_texture.clone(),
62            occlusion_strength: physical_material.occlusion_strength,
63            occlusion_texture: physical_material.occlusion_texture.clone(),
64            render_states: RenderStates {
65                write_mask: WriteMask::default(),
66                blend: Blend::Disabled,
67                ..physical_material.render_states
68            },
69        }
70    }
71}
72
73impl FromCpuMaterial for ORMMaterial {
74    fn from_cpu_material(context: &Context, cpu_material: &CpuMaterial) -> Self {
75        Self::new(context, cpu_material)
76    }
77}
78
79impl Material for ORMMaterial {
80    fn id(&self) -> EffectMaterialId {
81        EffectMaterialId::ORMMaterial(
82            self.metallic_roughness_texture.is_some(),
83            self.occlusion_texture.is_some(),
84        )
85    }
86
87    fn fragment_shader_source(&self, _lights: &[&dyn Light]) -> String {
88        let mut source = String::new();
89        if self.metallic_roughness_texture.is_some() || self.occlusion_texture.is_some() {
90            source.push_str("in vec2 uvs;\n");
91            if self.metallic_roughness_texture.is_some() {
92                source.push_str("#define USE_METALLIC_ROUGHNESS_TEXTURE;\n");
93            }
94            if self.occlusion_texture.is_some() {
95                source.push_str("#define USE_OCCLUSION_TEXTURE;\n");
96            }
97        }
98        source.push_str(include_str!("shaders/orm_material.frag"));
99        source
100    }
101
102    fn use_uniforms(&self, program: &Program, _viewer: &dyn Viewer, _lights: &[&dyn Light]) {
103        program.use_uniform("metallic", self.metallic);
104        program.use_uniform("roughness", self.roughness);
105        if let Some(ref texture) = self.metallic_roughness_texture {
106            program.use_texture("metallicRoughnessTexture", texture);
107            program.use_uniform("metallicRoughnessTexTransform", texture.transformation);
108        }
109        if let Some(ref texture) = self.occlusion_texture {
110            program.use_uniform("occlusionStrength", self.occlusion_strength);
111            program.use_texture("occlusionTexture", texture);
112            program.use_uniform("occlusionTexTransform", texture.transformation);
113        }
114    }
115
116    fn render_states(&self) -> RenderStates {
117        self.render_states
118    }
119
120    fn material_type(&self) -> MaterialType {
121        MaterialType::Opaque
122    }
123}
124
125impl Default for ORMMaterial {
126    fn default() -> Self {
127        Self {
128            metallic: 0.0,
129            roughness: 1.0,
130            metallic_roughness_texture: None,
131            occlusion_texture: None,
132            occlusion_strength: 1.0,
133            render_states: RenderStates::default(),
134        }
135    }
136}