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