logo
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

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
}

Creates new instance of standard terrain material.

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
}

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();

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());

Returns a reference to current shader.

Returns immutable reference to internal property storage.

Trait Implementations

Returns a copy of the value. Read more

Performs copy-assignment from source. Read more

Formats the value using the given formatter. Read more

Returns the “default value” for a type. Read more

Auto Trait Implementations

Blanket Implementations

Gets the TypeId of self. Read more

Immutably borrows from an owned value. Read more

Mutably borrows from an owned value. Read more

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

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

Convert &Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &Any’s vtable from &Trait’s. Read more

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

Convert Arc<Trait> (where Trait: Downcast) to Arc<Any>. Arc<Any> can then be further downcast into Arc<ConcreteType> where ConcreteType implements Trait. Read more

Returns the argument unchanged.

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

The alignment of pointer.

The type for initializers.

Initializes a with the given initializer. Read more

Dereferences the given pointer. Read more

Mutably dereferences the given pointer. Read more

Drops the object pointed to by the given pointer. Read more

Casts self to a &dyn Any

Should always be Self

The inverse inclusion map: attempts to construct self from the equivalent element of its superset. Read more

Checks if self is actually part of its subset T (and can be converted to it).

Use with care! Same as self.to_subset but without any property checks. Always succeeds.

The inclusion map: converts self to the equivalent element of its superset.

The resulting type after obtaining ownership.

Creates owned data from borrowed data, usually by cloning. Read more

🔬 This is a nightly-only experimental API. (toowned_clone_into)

Uses borrowed data to replace owned data, usually by cloning. Read more

The type returned in the event of a conversion error.

Performs the conversion.

The type returned in the event of a conversion error.

Performs the conversion.