pub struct Material { /* private fields */ }
Expand description
Material defines a set of values for a shader. Materials usually contains textures (diffuse, normal, height, emission, etc. maps), numerical values (floats, integers), vectors, booleans, matrices and arrays of each type, except textures. Each parameter can be changed in runtime giving you the ability to create animated materials. However in practice, most materials are static, this means that once it created, it won’t be changed anymore.
Please keep in mind that the actual “rules” of drawing an entity are stored in the shader, material is only a storage for specific uses of the shader.
Multiple materials can share the same shader, for example standard shader covers 95% of most common use cases and it is shared across multiple materials. The only difference are property values, for example you can draw multiple cubes using the same shader, but with different textures.
Material itself can be shared across multiple places as well as the shader. This gives you the ability to render multiple objects with the same material efficiently.
Performance
It is very important re-use materials as much as possible, because the amount of materials used per frame significantly correlates with performance. The more unique materials you have per frame, the more work has to be done by the renderer and video driver to render a frame and the more time the frame will require for rendering, thus lowering your FPS.
Examples
A material can only be created using a shader instance, every material must have a shader. The shader provides information about its properties, and this information is used to populate a set of properties with default values. Default values of each property defined in the shader.
Standard material
Usually standard shader is enough for most cases, Material
even has a Material::standard()
method to create a material with standard shader:
use fyrox::{
material::shader::{Shader, SamplerFallback},
engine::resource_manager::ResourceManager,
material::{Material, PropertyValue},
core::sstorage::ImmutableString,
};
fn create_brick_material(resource_manager: ResourceManager) -> Material {
let mut material = Material::standard();
material.set_property(
&ImmutableString::new("diffuseTexture"),
PropertyValue::Sampler {
value: Some(resource_manager.request_texture("Brick_DiffuseTexture.jpg")),
fallback: SamplerFallback::White
})
.unwrap();
material
}
As you can see it is pretty simple with standard material, all you need is to set values to desired properties and you good to go. All you need to do is to apply the material, for example it could be mesh surface or some other place that supports materials. For the full list of properties of the standard shader see shader module docs.
Custom material
Custom materials is a bit more complex, you need to get a shader instance using the resource manager and then create the material and populate it with a set of property values.
use fyrox::{
engine::resource_manager::ResourceManager,
material::{Material, PropertyValue},
core::{sstorage::ImmutableString, algebra::Vector3}
};
async fn create_grass_material(resource_manager: ResourceManager) -> Material {
let shader = resource_manager.request_shader("my_grass_shader.ron").await.unwrap();
// Here we assume that the material really has the properties defined below.
let mut material = Material::from_shader(shader, Some(resource_manager));
material.set_property(
&ImmutableString::new("windDirection"),
PropertyValue::Vector3(Vector3::new(1.0, 0.0, 0.5))
)
.unwrap();
material
}
As you can see it is only a bit more hard that with the standard shader. The main difference here is that we using resource manager to get shader instance and the we just use the instance to create material instance. Then we populate properties as usual.
Implementations
sourceimpl Material
impl Material
sourcepub fn standard() -> Self
pub fn standard() -> Self
Creates a new instance of material with the standard shader. For the full list of properties of the standard material see shader module docs.
Example
use fyrox::{
material::shader::{Shader, SamplerFallback},
engine::resource_manager::ResourceManager,
material::{Material, PropertyValue},
core::sstorage::ImmutableString
};
fn create_brick_material(resource_manager: ResourceManager) -> Material {
let mut material = Material::standard();
material.set_property(
&ImmutableString::new("diffuseTexture"),
PropertyValue::Sampler {
value: Some(resource_manager.request_texture("Brick_DiffuseTexture.jpg")),
fallback: SamplerFallback::White
})
.unwrap();
material
}
sourcepub fn standard_terrain() -> Self
pub fn standard_terrain() -> Self
Creates new instance of standard terrain material.
sourcepub fn from_shader(
shader: Shader,
resource_manager: Option<ResourceManager>
) -> Self
pub fn from_shader(
shader: Shader,
resource_manager: Option<ResourceManager>
) -> Self
Creates a new material instance with given shader. Each property will have default values defined in the shader.
It is possible to pass resource manager as a second argument, it is needed to correctly resolve
default values of samplers in case if they are bound to some resources - shader’s definition stores
only paths to textures. If you pass None
, no resolving will be done and every sampler will
have None
as default value, which in its turn will force engine to use fallback sampler value.
Example
use fyrox::{
engine::resource_manager::ResourceManager,
material::{Material, PropertyValue},
core::{sstorage::ImmutableString, algebra::Vector3}
};
async fn create_grass_material(resource_manager: ResourceManager) -> Material {
let shader = resource_manager.request_shader("my_grass_shader.ron").await.unwrap();
// Here we assume that the material really has the properties defined below.
let mut material = Material::from_shader(shader, Some(resource_manager));
material.set_property(
&ImmutableString::new("windDirection"),
PropertyValue::Vector3(Vector3::new(1.0, 0.0, 0.5))
)
.unwrap();
material
}
sourcepub fn property_ref(&self, name: &ImmutableString) -> Option<&PropertyValue>
pub fn property_ref(&self, name: &ImmutableString) -> Option<&PropertyValue>
Searches for a property with given name.
Complexity
O(1)
Examples
use fyrox::material::Material;
let mut material = Material::standard();
let color = material.property_ref(&ImmutableString::new("diffuseColor")).unwrap().as_color();
sourcepub fn set_property(
&mut self,
name: &ImmutableString,
new_value: PropertyValue
) -> Result<(), MaterialError>
pub fn set_property(
&mut self,
name: &ImmutableString,
new_value: PropertyValue
) -> Result<(), MaterialError>
Sets new value of the property with given name.
Type checking
A new value must have the same type as in shader, otherwise an error will be generated. This helps to catch subtle bugs when you passing “almost” identical values to shader, like signed and unsigned integers - both have positive values, but GPU is very strict of what it expects as input value.
Example
let mut material = Material::standard();
assert!(material.set_property(&ImmutableString::new("diffuseColor"), PropertyValue::Color(Color::WHITE)).is_ok());
sourcepub fn shader(&self) -> &ShaderⓘNotable traits for Shaderimpl Future for Shader type Output = Result<Self, Option<Arc<ShaderError>>>;
pub fn shader(&self) -> &ShaderⓘNotable traits for Shaderimpl Future for Shader type Output = Result<Self, Option<Arc<ShaderError>>>;
Returns a reference to current shader.
sourcepub fn properties(&self) -> &FxHashMap<ImmutableString, PropertyValue>
pub fn properties(&self) -> &FxHashMap<ImmutableString, PropertyValue>
Returns immutable reference to internal property storage.
Trait Implementations
sourceimpl Visit for Material where
Shader: Visit,
DrawParameters: Visit,
FxHashMap<ImmutableString, PropertyValue>: Visit,
impl Visit for Material where
Shader: Visit,
DrawParameters: Visit,
FxHashMap<ImmutableString, PropertyValue>: Visit,
fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult
Auto Trait Implementations
impl !RefUnwindSafe for Material
impl Send for Material
impl Sync for Material
impl Unpin for Material
impl !UnwindSafe for Material
Blanket Implementations
sourceimpl<T> BorrowMut<T> for T where
T: ?Sized,
impl<T> BorrowMut<T> for T where
T: ?Sized,
const: unstable · sourcefn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more
impl<T> Downcast for T where
T: Any,
impl<T> Downcast for T where
T: Any,
fn into_any(self: Box<T, Global>) -> Box<dyn Any + 'static, Global>ⓘNotable traits for Box<F, A>impl<F, A> Future for Box<F, A> where
F: Future + Unpin + ?Sized,
A: Allocator + 'static, type Output = <F as Future>::Output;
fn into_any(self: Box<T, Global>) -> Box<dyn Any + 'static, Global>ⓘNotable traits for Box<F, A>impl<F, A> Future for Box<F, A> where
F: Future + Unpin + ?Sized,
A: Allocator + 'static, type Output = <F as Future>::Output;
F: Future + Unpin + ?Sized,
A: Allocator + 'static, type Output = <F as Future>::Output;
Convert Box<dyn Trait>
(where Trait: Downcast
) to Box<dyn Any>
. Box<dyn Any>
can
then be further downcast
into Box<ConcreteType>
where ConcreteType
implements Trait
. Read more
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any + 'static>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any + 'static>
Convert Rc<Trait>
(where Trait: Downcast
) to Rc<Any>
. Rc<Any>
can then be
further downcast
into Rc<ConcreteType>
where ConcreteType
implements Trait
. Read more
fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
Convert &Trait
(where Trait: Downcast
) to &Any
. This is needed since Rust cannot
generate &Any
’s vtable from &Trait
’s. Read more
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
Convert &mut Trait
(where Trait: Downcast
) to &Any
. This is needed since Rust cannot
generate &mut Any
’s vtable from &mut Trait
’s. Read more
impl<T> Pointable for T
impl<T> Pointable for T
sourceimpl<T> PropertyValue for T where
T: 'static + Debug,
impl<T> PropertyValue for T where
T: 'static + Debug,
impl<SS, SP> SupersetOf<SS> for SP where
SS: SubsetOf<SP>,
impl<SS, SP> SupersetOf<SS> for SP where
SS: SubsetOf<SP>,
fn to_subset(&self) -> Option<SS>
fn to_subset(&self) -> Option<SS>
The inverse inclusion map: attempts to construct self
from the equivalent element of its
superset. Read more
fn is_in_subset(&self) -> bool
fn is_in_subset(&self) -> bool
Checks if self
is actually part of its subset T
(and can be converted to it).
fn to_subset_unchecked(&self) -> SS
fn to_subset_unchecked(&self) -> SS
Use with care! Same as self.to_subset
but without any property checks. Always succeeds.
fn from_subset(element: &SS) -> SP
fn from_subset(element: &SS) -> SP
The inclusion map: converts self
to the equivalent element of its superset.
sourceimpl<T> ToOwned for T where
T: Clone,
impl<T> ToOwned for T where
T: Clone,
type Owned = T
type Owned = T
The resulting type after obtaining ownership.
sourcefn clone_into(&self, target: &mut T)
fn clone_into(&self, target: &mut T)
toowned_clone_into
)Uses borrowed data to replace owned data, usually by cloning. Read more