Skip to main content

three_d/renderer/
material.rs

1//!
2//! A collection of materials implementing the [Material] trait.
3//!
4//! A material together with a [geometry] can be rendered directly (using [Geometry::render_with_material] or [Geometry::render_with_effect]).
5//! A [Material] can also be combined into an [object] (see [Gm]) and be used in a render call, for example [RenderTarget::render].
6//!
7
8macro_rules! impl_material_body {
9    ($inner:ident) => {
10        fn fragment_shader_source(&self, lights: &[&dyn Light]) -> String {
11            self.$inner().fragment_shader_source(lights)
12        }
13        fn use_uniforms(&self, program: &Program, viewer: &dyn Viewer, lights: &[&dyn Light]) {
14            self.$inner().use_uniforms(program, viewer, lights)
15        }
16        fn render_states(&self) -> RenderStates {
17            self.$inner().render_states()
18        }
19        fn material_type(&self) -> MaterialType {
20            self.$inner().material_type()
21        }
22        fn id(&self) -> EffectMaterialId {
23            self.$inner().id()
24        }
25    };
26}
27
28use crate::renderer::*;
29
30pub use three_d_asset::material::{
31    GeometryFunction, LightingModel, NormalDistributionFunction, PbrMaterial as CpuMaterial,
32};
33
34mod color_material;
35#[doc(inline)]
36pub use color_material::*;
37
38mod depth_material;
39#[doc(inline)]
40pub use depth_material::*;
41
42mod intersection_material;
43#[doc(inline)]
44pub use intersection_material::*;
45
46mod normal_material;
47#[doc(inline)]
48pub use normal_material::*;
49
50mod orm_material;
51#[doc(inline)]
52pub use orm_material::*;
53
54mod position_material;
55#[doc(inline)]
56pub use position_material::*;
57
58mod uv_material;
59#[doc(inline)]
60pub use uv_material::*;
61
62mod physical_material;
63#[doc(inline)]
64pub use physical_material::*;
65
66mod deferred_physical_material;
67#[doc(inline)]
68pub use deferred_physical_material::*;
69
70mod skybox_material;
71#[doc(inline)]
72pub(in crate::renderer) use skybox_material::*;
73
74mod wireframe_material;
75#[doc(inline)]
76pub(in crate::renderer) use wireframe_material::*;
77
78mod isosurface_material;
79#[doc(inline)]
80pub use isosurface_material::*;
81
82use std::{ops::Deref, sync::Arc};
83
84///
85/// A reference to a 2D texture and a texture transformation.
86///
87#[derive(Clone)]
88pub struct Texture2DRef {
89    /// A reference to the texture.
90    pub texture: Arc<Texture2D>,
91    /// A transformation applied to the uv coordinates before reading a texel value at those uv coordinates.
92    /// This is primarily used in relation to texture atlasing.
93    pub transformation: Mat3,
94}
95
96impl Texture2DRef {
97    /// Creates a new [Texture2DRef] with an identity transformation from a [CpuTexture].
98    pub fn from_cpu_texture(context: &Context, cpu_texture: &CpuTexture) -> Self {
99        Self {
100            texture: Arc::new(Texture2D::new(context, cpu_texture)),
101            transformation: Mat3::identity(),
102        }
103    }
104
105    /// Creates a new [Texture2DRef] with an identity transformation from a [Texture2D].
106    pub fn from_texture(texture: Texture2D) -> Self {
107        Self {
108            texture: Arc::new(texture),
109            transformation: Mat3::identity(),
110        }
111    }
112}
113
114impl std::ops::Deref for Texture2DRef {
115    type Target = Texture2D;
116    fn deref(&self) -> &Self::Target {
117        &self.texture
118    }
119}
120
121impl std::convert::From<Texture2D> for Texture2DRef {
122    fn from(texture: Texture2D) -> Self {
123        Self::from_texture(texture)
124    }
125}
126
127impl std::convert::From<Arc<Texture2D>> for Texture2DRef {
128    fn from(texture: Arc<Texture2D>) -> Self {
129        Self {
130            texture,
131            transformation: Mat3::identity(),
132        }
133    }
134}
135
136///
137/// Defines the material type which is needed to render the objects in the correct order.
138/// For example, transparent objects need to be rendered back to front, whereas opaque objects need to be rendered front to back.
139///
140#[derive(Clone, Copy, PartialEq, PartialOrd, Ord, Eq, Debug)]
141pub enum MaterialType {
142    /// Forward opaque
143    Opaque,
144    /// Forward transparent
145    Transparent,
146    /// Deferred opaque
147    Deferred,
148}
149
150///
151/// Represents a material that, together with a [geometry], can be rendered using [Geometry::render_with_material].
152/// Alternatively, a geometry and a material can be combined in a [Gm],
153/// thereby creating an [Object] which can be used in a render call, for example [RenderTarget::render].
154///
155/// An implementation of the [Geometry] trait should provide a set of attributes which can be used in the fragment shader.
156/// The following attributes might be available:
157/// - position: `in vec3 pos;` (in world space)
158/// - normal: `in vec3 nor;`
159/// - tangent: `in vec3 tang;`
160/// - bitangent: `in vec3 bitang;`
161/// - uv coordinates: `in vec2 uvs;` (flipped in v compared to standard uv coordinates)
162/// - color: `in vec4 col;`
163///
164pub trait Material {
165    ///
166    /// Returns the fragment shader source for this material.
167    ///
168    fn fragment_shader_source(&self, lights: &[&dyn Light]) -> String;
169
170    ///
171    /// Returns a unique ID for each variation of the shader source returned from [Material::fragment_shader_source].
172    ///
173    /// **Note:** The last bit is reserved to internally implemented materials, so if implementing the [Material] trait
174    /// outside of this crate, always return an id in the public use range as defined by [EffectMaterialId].
175    ///
176    fn id(&self) -> EffectMaterialId;
177
178    ///
179    /// Sends the uniform data needed for this material to the fragment shader.
180    ///
181    fn use_uniforms(&self, program: &Program, viewer: &dyn Viewer, lights: &[&dyn Light]);
182
183    ///
184    /// Returns the render states needed to render with this material.
185    ///
186    fn render_states(&self) -> RenderStates;
187
188    ///
189    /// Returns the type of material.
190    ///
191    fn material_type(&self) -> MaterialType;
192}
193
194///
195/// Implement this for a [Material] that can be created from a [CpuMaterial].
196///
197pub trait FromCpuMaterial: std::marker::Sized {
198    ///
199    /// Creates a new material that can be used for rendering from a [CpuMaterial].
200    ///
201    fn from_cpu_material(context: &Context, cpu_material: &CpuMaterial) -> Self;
202}
203
204///
205/// Implement this for a [Material] that can be created from a [CpuVoxelGrid].
206///
207pub trait FromCpuVoxelGrid: std::marker::Sized {
208    ///
209    /// Creates a new material that can be used for rendering from a [CpuVoxelGrid].
210    ///
211    fn from_cpu_voxel_grid(context: &Context, cpu_voxel_grid: &CpuVoxelGrid) -> Self;
212}
213
214impl<T: Material + ?Sized> Material for &T {
215    impl_material_body!(deref);
216}
217
218impl<T: Material + ?Sized> Material for &mut T {
219    impl_material_body!(deref);
220}
221
222impl<T: Material> Material for Box<T> {
223    impl_material_body!(as_ref);
224}
225
226impl<T: Material> Material for std::rc::Rc<T> {
227    impl_material_body!(as_ref);
228}
229
230impl<T: Material> Material for std::sync::Arc<T> {
231    impl_material_body!(as_ref);
232}
233
234impl<T: Material> Material for std::cell::RefCell<T> {
235    impl_material_body!(borrow);
236}
237
238impl<T: Material> Material for std::sync::RwLock<T> {
239    fn fragment_shader_source(&self, lights: &[&dyn Light]) -> String {
240        self.read().unwrap().fragment_shader_source(lights)
241    }
242    fn use_uniforms(&self, program: &Program, viewer: &dyn Viewer, lights: &[&dyn Light]) {
243        self.read().unwrap().use_uniforms(program, viewer, lights)
244    }
245    fn render_states(&self) -> RenderStates {
246        self.read().unwrap().render_states()
247    }
248    fn material_type(&self) -> MaterialType {
249        self.read().unwrap().material_type()
250    }
251    fn id(&self) -> EffectMaterialId {
252        self.read().unwrap().id()
253    }
254}
255
256fn is_transparent(cpu_material: &CpuMaterial) -> bool {
257    cpu_material.albedo.a != 255
258        || cpu_material
259            .albedo_texture
260            .as_ref()
261            .map(|t| match &t.data {
262                TextureData::RgbaU8(data) => data.iter().any(|d| d[3] != 255),
263                TextureData::RgbaF16(data) => data.iter().any(|d| d[3] < f16::from_f32(0.99)),
264                TextureData::RgbaF32(data) => data.iter().any(|d| d[3] < 0.99),
265                _ => false,
266            })
267            .unwrap_or(false)
268}