(Top)
1 About
1.1 Authors
2 Overview
2.1 Core concepts
3 Material models
3.1 Lit model
3.1.1 Base color
3.1.2 Metallic
3.1.3 Roughness
3.1.4 Non-metals
3.1.5 Metals
3.1.6 Refraction
3.1.7 Reflectance
3.1.8 Sheen color
3.1.9 Sheen roughness
3.1.10 Clear coat
3.1.11 Clear coat roughness
3.1.12 Anisotropy
3.1.13 Anisotropy direction
3.1.14 Ambient occlusion
3.1.15 Normal
3.1.16 Bent normal
3.1.17 Clear coat normal
3.1.18 Emissive
3.1.19 Post-lighting color
3.1.20 Index of refraction
3.1.21 Transmission
3.1.22 Absorption
3.1.23 Micro-thickness and thickness
3.2 Subsurface model
3.2.1 Thickness
3.2.2 Subsurface color
3.2.3 Subsurface power
3.3 Cloth model
3.3.1 Sheen color
3.3.2 Subsurface color
3.4 Unlit model
3.5 Specular glossiness
4 Material definitions
4.1 Format
4.1.1 Differences with JSON
4.1.2 Example
4.2 Material block
4.2.1 General: name
4.2.2 General: featureLevel
4.2.3 General: shadingModel
4.2.4 General: parameters
4.2.5 General: constants
4.2.6 General: variantFilter
4.2.7 General: flipUV
4.2.8 General: quality
4.2.9 General: instanced
4.2.10 General: vertexDomainDeviceJittered
4.2.11 Vertex and attributes: requires
4.2.12 Vertex and attributes: variables
4.2.13 Vertex and attributes: vertexDomain
4.2.14 Vertex and attributes: interpolation
4.2.15 Blending and transparency: blending
4.2.16 Blending and transparency: blendFunction
4.2.17 Blending and transparency: postLightingBlending
4.2.18 Blending and transparency: transparency
4.2.19 Blending and transparency: maskThreshold
4.2.20 Blending and transparency: refractionMode
4.2.21 Blending and transparency: refractionType
4.2.22 Rasterization: culling
4.2.23 Rasterization: colorWrite
4.2.24 Rasterization: depthWrite
4.2.25 Rasterization: depthCulling
4.2.26 Rasterization: doubleSided
4.2.27 Rasterization: alphaToCoverage
4.2.28 Lighting: reflections
4.2.29 Lighting: shadowMultiplier
4.2.30 Lighting: transparentShadow
4.2.31 Lighting: clearCoatIorChange
4.2.32 Lighting: multiBounceAmbientOcclusion
4.2.33 Lighting: specularAmbientOcclusion
4.2.34 Anti-aliasing: specularAntiAliasing
4.2.35 Anti-aliasing: specularAntiAliasingVariance
4.2.36 Anti-aliasing: specularAntiAliasingThreshold
4.2.37 Shading: customSurfaceShading
4.3 Vertex block
4.3.1 Material vertex inputs
4.3.2 Custom vertex attributes
4.4 Fragment block
4.4.1 prepareMaterial function
4.4.2 Material fragment inputs
4.4.3 Custom surface shading
4.5 Shader public APIs
4.5.1 Types
4.5.2 Math
4.5.3 Matrices
4.5.4 Frame constants
4.5.5 Material globals
4.5.6 Vertex only
4.5.7 Fragment only
5 Compiling materials
5.1 Shader validation
5.2 Flags
5.2.1 —platform
5.2.2 —api
5.2.3 —optimize-size
5.2.4 —reflect
5.2.5 —variant-filter
6 Handling colors
6.1 Linear colors
6.2 Pre-multiplied alpha
7 Sampler usage in Materials
7.1 Feature level 1 and 2
7.2 Feature level 3
This document is part of the Filament project. To report errors in this document please use the project's issue tracker.
Filament is a physically based rendering (PBR) engine for Android. Filament offers a customizable material system that you can use to create both simple and complex materials. This document describes all the features available to materials and how to create your own material.
A material defines the visual appearance of a surface. To completely describe and render a surface, a material provides the following information:
Also called shading model or lighting model, the material model defines the intrinsic properties of a surface. These properties have a direct influence on the way lighting is computed and therefore on the appearance of a surface.
A text file that describes all the information required by a material. This is the file that you will directly author to create new materials.
At runtime, materials are loaded from material packages compiled from material definitions
using the matc
tool. A material package contains all the information required to describe a
material, and shaders generated for the target runtime platforms. This is necessary because
different platforms (Android, macOS, Linux, etc.) use different graphics APIs or different
variants of similar graphics APIs (OpenGL vs OpenGL ES for instance).
A material instance is a reference to a material and a set of values for the different values of that material. Material instances are not covered in this document as they are created and manipulated directly from code using Filament's APIs.
Filament materials can use one of the following material models:
The lit model is Filament's standard material model. This physically-based shading model was designed after to offer good interoperability with other common tools and engines such as Unity 5, Unreal Engine 4, Substance Designer or Marmoset Toolbag.
This material model can be used to describe many non-metallic surfaces (dielectrics) or metallic surfaces (conductors).
The appearance of a material using the standard model is controlled using the properties described in table 1.
Property | Definition |
---|---|
baseColor | Diffuse albedo for non-metallic surfaces, and specular color for metallic surfaces |
metallic | Whether a surface appears to be dielectric (0.0) or conductor (1.0). Often used as a binary value (0 or 1) |
roughness | Perceived smoothness (1.0) or roughness (0.0) of a surface. Smooth surfaces exhibit sharp reflections |
reflectance | Fresnel reflectance at normal incidence for dielectric surfaces. This directly controls the strength of the reflections |
sheenColor | Strength of the sheen layer |
sheenRoughness | Perceived smoothness or roughness of the sheen layer |
clearCoat | Strength of the clear coat layer |
clearCoatRoughness | Perceived smoothness or roughness of the clear coat layer |
anisotropy | Amount of anisotropy in either the tangent or bitangent direction |
anisotropyDirection | Local surface direction in tangent space |
ambientOcclusion | Defines how much of the ambient light is accessible to a surface point. It is a per-pixel shadowing factor between 0.0 and 1.0 |
normal | A detail normal used to perturb the surface using bump mapping (normal mapping) |
bentNormal | A normal pointing in the average unoccluded direction. Can be used to improve indirect lighting quality |
clearCoatNormal | A detail normal used to perturb the clear coat layer using bump mapping (normal mapping) |
emissive | Additional diffuse albedo to simulate emissive surfaces (such as neons, etc.) This property is mostly useful in an HDR pipeline with a bloom pass |
postLightingColor | Additional color that can be blended with the result of the lighting computations. See postLightingBlending |
ior | Index of refraction, either for refractive objects or as an alternative to reflectance |
transmission | Defines how much of the diffuse light of a dielectric is transmitted through the object, in other words this defines how transparent an object is |
absorption | Absorption factor for refractive objects |
microThickness | Thickness of the thin layer of refractive objects |
thickness | Thickness of the solid volume of refractive objects |
The type and range of each property is described in table 2.
Property | Type | Range | Note |
---|---|---|---|
baseColor | float4 | [0..1] | Pre-multiplied linear RGB |
metallic | float | [0..1] | Should be 0 or 1 |
roughness | float | [0..1] | |
reflectance | float | [0..1] | Prefer values > 0.35 |
sheenColor | float3 | [0..1] | Linear RGB |
sheenRoughness | float | [0..1] | |
clearCoat | float | [0..1] | Should be 0 or 1 |
clearCoatRoughness | float | [0..1] | |
anisotropy | float | [−1..1] | Anisotropy is in the tangent direction when this value is positive |
anisotropyDirection | float3 | [0..1] | Linear RGB, encodes a direction vector in tangent space |
ambientOcclusion | float | [0..1] | |
normal | float3 | [0..1] | Linear RGB, encodes a direction vector in tangent space |
bentNormal | float3 | [0..1] | Linear RGB, encodes a direction vector in tangent space |
clearCoatNormal | float3 | [0..1] | Linear RGB, encodes a direction vector in tangent space |
emissive | float4 | rgb=[0..n], a=[0..1] | Linear RGB intensity in nits, alpha encodes the exposure weight |
postLightingColor | float4 | [0..1] | Pre-multiplied linear RGB |
ior | float | [1..n] | Optional, usually deduced from the reflectance |
transmission | float | [0..1] | |
absorption | float3 | [0..n] | |
microThickness | float | [0..n] | |
thickness | float | [0..n] |
Several material model properties expect RGB colors. Filament materials use RGB colors in linear space and you must take proper care of supplying colors in that space. See the Linear colors section for more information.
Filament materials expect colors to use pre-multiplied alpha. See the Pre-multiplied alpha section for more information.
absorption
The light attenuation through the material is defined as \(e^{-absorption \cdot distance}\),
and the distance depends on the thickness
parameter. If thickness
is not provided, then
the absorption
parameter is used directly and the light attenuation through the material
becomes \(1 - absorption\). To obtain a certain color at a desired distance, the above
equation can be inverted such as \(absorption = -\frac{ln(color)}{distance}\).
ior
and reflectance
The index of refraction (IOR) and the reflectance represent the same physical attribute, therefore they don't need to be both specified. Typically, only the reflectance is specified, and the IOR is deduced automatically. When only the IOR is specified, the reflectance is then deduced automatically. It is possible to specify both, in which case their values are kept as-is, which can lead to physically impossible materials, however, this might be desirable for artistic reasons.
thickness
and microThickness
for refraction
thickness
represents the thickness of solid objects in the direction of the normal, for
satisfactory results, this should be provided per fragment (e.g.: as a texture) or at least per
vertex. microThickness
represent the thickness of the thin layer of an object, and can
generally be provided as a constant value. For example, a 1mm thin hollow sphere of radius 1m,
would have a thickness
of 1 and a microThickness
of 0.001. Currently thickness
is not
used when refractionType
is set to thin
.
The baseColor
property defines the perceived color of an object (sometimes called albedo). The
effect of baseColor
depends on the nature of the surface, controlled by the metallic
property
explained in the Metallic section.
Defines the diffuse color of the surface. Real-world values are typically found in the range \([10..240]\) if the value is encoded between 0 and 255, or in the range \([0.04..0.94]\) between 0 and 1. Several examples of base colors for non-metallic surfaces can be found in table 3.
Metal | sRGB | Hexadecimal | Color |
---|---|---|---|
Coal | 0.19, 0.19, 0.19 | #323232 | |
Rubber | 0.21, 0.21, 0.21 | #353535 | |
Mud | 0.33, 0.24, 0.19 | #553d31 | |
Wood | 0.53, 0.36, 0.24 | #875c3c | |
Vegetation | 0.48, 0.51, 0.31 | #7b824e | |
Brick | 0.58, 0.49, 0.46 | #947d75 | |
Sand | 0.69, 0.66, 0.52 | #b1a884 | |
Concrete | 0.75, 0.75, 0.73 | #c0bfbb | |
Defines the specular color of the surface. Real-world values are typically found in the range \([170..255]\) if the value is encoded between 0 and 255, or in the range \([0.66..1.0]\) between 0 and 1. Several examples of base colors for metallic surfaces can be found in table 4.
Metal | sRGB | Hexadecimal | Color |
---|---|---|---|
Silver | 0.97, 0.96, 0.91 | #f7f4e8 | |
Aluminum | 0.91, 0.92, 0.92 | #e8eaea | |
Titanium | 0.76, 0.73, 0.69 | #c1baaf | |
Iron | 0.77, 0.78, 0.78 | #c4c6c6 | |
Platinum | 0.83, 0.81, 0.78 | #d3cec6 | |
Gold | 1.00, 0.85, 0.57 | #ffd891 | |
Brass | 0.98, 0.90, 0.59 | #f9e596 | |
Copper | 0.97, 0.74, 0.62 | #f7bc9e | |
The metallic
property defines whether the surface is a metallic (conductor) or a non-metallic
(dielectric) surface. This property should be used as a binary value, set to either 0 or 1.
Intermediate values are only truly useful to create transitions between different types of surfaces
when using textures.
This property can dramatically change the appearance of a surface. Non-metallic surfaces have
chromatic diffuse reflection and achromatic specular reflection (reflected light does not change
color). Metallic surfaces do not have any diffuse reflection and chromatic specular reflection
(reflected light takes on the color of the surfaced as defined by baseColor
).
The effect of metallic
is shown in figure 1 (click on the image to see a
larger version).
The roughness
property controls the perceived smoothness of the surface. When roughness
is set
to 0, the surface is perfectly smooth and highly glossy. The rougher a surface is, the “blurrier”
the reflections are. This property is often called glossiness in other engines and tools, and is
simply the opposite of the roughness (roughness = 1 - glossiness
).
The effect of roughness
on non-metallic surfaces is shown in figure 2 (click
on the image to see a larger version).
The effect of roughness
on metallic surfaces is shown in figure 3
(click on the image to see a larger version).
When refraction through an object is enabled (using a refractonType
of thin
or solid
), the
roughness
property will also affect the refractions, as shown in figure 4 (click on the image to see a larger version).
The reflectance
property only affects non-metallic surfaces. This property can be used to control
the specular intensity and index of refraction of materials. This value is defined
between 0 and 1 and represents a remapping of a percentage of reflectance. For instance, the
default value of 0.5 corresponds to a reflectance of 4%. Values below 0.35 (2% reflectance) should
be avoided as no real-world materials have such low reflectance.
The effect of reflectance
on non-metallic surfaces is shown in figure 5
(click on the image to see a larger version).
Figure 6 shows common values and how they relate to the mapping function.
Table 5 describes acceptable reflectance values for various types of materials (no real world material has a value under 2%).
Material | Reflectance | IOR | Linear value |
---|---|---|---|
Water | 2% | 1.33 | 0.35 |
Fabric | 4% to 5.6% | 1.5 to 1.62 | 0.5 to 0.59 |
Common liquids | 2% to 4% | 1.33 to 1.5 | 0.35 to 0.5 |
Common gemstones | 5% to 16% | 1.58 to 2.33 | 0.56 to 1.0 |
Plastics, glass | 4% to 5% | 1.5 to 1.58 | 0.5 to 0.56 |
Other dielectric materials | 2% to 5% | 1.33 to 1.58 | 0.35 to 0.56 |
Eyes | 2.5% | 1.38 | 0.39 |
Skin | 2.8% | 1.4 | 0.42 |
Hair | 4.6% | 1.55 | 0.54 |
Teeth | 5.8% | 1.63 | 0.6 |
Default value | 4% | 1.5 | 0.5 |
Note that the reflectance
property also defines the index of refraction of the surface.
When this property is defined it is not necessary to define the ior
property. Setting
either of these properties will automatically compute the other property. It is possible
to specify both, in which case their values are kept as-is, which can lead to physically
impossible materials, however, this might be desirable for artistic reasons.
The reflectance
property is designed as a normalized property in the range 0..1 which makes
it easy to define from a texture.
See section 3.1.20 for more information about the ior
property and refractive
indices.
The sheen color controls the color appearance and strength of an optional sheen layer on top of the base layer described by the properties above. The sheen layer always sits below the clear coat layer if such a layer is present.
The sheen layer can be used to represent cloth and fabric materials. Please refer to section 3.3 for more information about cloth and fabric materials.
The effect of sheenColor
is shown in figure 7
(click on the image to see a larger version).
The sheenRoughness
property is similar to the roughness
property but applies only to the
sheen layer.
The effect of sheenRoughness
on a rough metal is shown in figure 8
(click on the image to see a larger version). In this picture, the base layer is a dark blue, with
metallic
set to 0.0
and roughness
set to 1.0
.
Multi-layer materials are fairly common, particularly materials with a thin translucent layer over a base layer. Real world examples of such materials include car paints, soda cans, lacquered wood and acrylic.
The clearCoat
property can be used to describe materials with two layers. The clear coat layer
will always be isotropic and dielectric.
The clearCoat
property controls the strength of the clear coat layer. This should be treated as a
binary value, set to either 0 or 1. Intermediate values are useful to control transitions between
parts of the surface that have a clear coat layers and parts that don't.
The effect of clearCoat
on a rough metal is shown in figure 10
(click on the image to see a larger version).
The clearCoatRoughness
property is similar to the roughness
property but applies only to the
clear coat layer.
The effect of clearCoatRoughness
on a rough metal is shown in figure 11
(click on the image to see a larger version).
Many real-world materials, such as brushed metal, can only be replicated using an anisotropic
reflectance model. A material can be changed from the default isotropic model to an anisotropic
model by using the anisotropy
property.
The effect of anisotropy
on a rough metal is shown in figure 13
(click on the image to see a larger version).
The figure 14 below shows how the direction of the anisotropic highlights can be controlled by using either positive or negative values: positive values define anisotropy in the tangent direction and negative values in the bitangent direction.
anisotropy
property if you don't need anisotropy.
The anisotropyDirection
property defines the direction of the surface at a given point and thus
control the shape of the specular highlights. It is specified as vector of 3 values that usually
come from a texture, encoding the directions local to the surface in tangent space. Because the
direction is in tangent space, the Z component should be set to 0.
The effect of anisotropyDirection
on a metal is shown in figure 16
(click on the image to see a larger version).
The result shown in figure 16 was obtained using the direction map shown in figure 16.
The ambientOcclusion
property defines how much of the ambient light is accessible to a surface
point. It is a per-pixel shadowing factor between 0.0 (fully shadowed) and 1.0 (fully lit). This
property only affects diffuse indirect lighting (image-based lighting), not direct lights such as
directional, point and spot lights, nor specular lighting.
The normal
property defines the normal of the surface at a given point. It usually comes from a
normal map texture, which allows to vary the property per-pixel. The normal is supplied in tangent
space, which means that +Z points outside of the surface.
For example, let's imagine that we want to render a piece of furniture covered in tufted leather. Modeling the geometry to accurately represent the tufted pattern would require too many triangles so we instead bake a high-poly mesh into a normal map. Once the base map is applied to a simplified mesh, we get the result in figure 18.
Note that the normal
property affects the base layer and not the clear coat layer.
The bentNormal
property defines the average unoccluded direction at a point on the surface. It is
used to improve the accuracy of indirect lighting. Bent normals can also improve the quality of
specular ambient occlusion (see section 4.2.33 about
specularAmbientOcclusion
).
Bent normals can greatly increase the visual fidelity of an asset with various cavities and concave areas, as shown in figure 19. See the areas of the ears, nostrils and eyes for instance.
The clearCoatNormal
property defines the normal of the clear coat layer at a given point. It
behaves otherwise like the normal
property.
The emissive
property can be used to simulate additional light emitted by the surface. It is
defined as a float4
value that contains an RGB intensity in nits as well as an exposure
weight (in the alpha channel).
The intensity in nits allows an emissive surface to function as a light and can be used to recreate real world surfaces. For instance a computer display has an intensity between 200 and 1,000 nits.
If you prefer to work in EV (or f-stops), you can simplify multiply your emissive color by the
output of the API filament::Exposure::luminance(ev)
. This API returns the luminance in nits of
the specific EV. You can perform this conversion yourself using the following formula, where \(L\)
is the final intensity in nits: \( L = 2^{EV - 3} \).
The exposure weight carried in the alpha channel can be used to undo the camera exposure, and thus force an emissive surface to bloom. When the exposure weight is set to 0, the emissive intensity is not affected by the camera exposure. When the weight is set to 1, the intensity is multiplied by the camera exposure like with any regular light.
The postLightingColor
can be used to modify the surface color after lighting computations. This
property has no physical meaning and only exists to implement specific effects or to help with
debugging. This property is defined as a float4
value containing a pre-multiplied RGB color in
linear space.
The post-lighting color is blended with the result of lighting according to the blending mode
specified by the postLightingBlending
material option. Please refer to the documentation of
this option for more information.
postLightingColor
can be used as a simpler emissive
property by setting
postLightingBlending
to add
and by providing an RGB color with alpha set to 0.0
.
The ior
property only affects non-metallic surfaces. This property can be used to control the
index of refraction and the specular intensity of materials. The ior
property is intended to
be used with refractive (transmissive) materials, which are enabled when the refractionMode
is
set to cubemap
or screenspace
. It can also be used on non-refractive objects as an alternative
to setting the reflectance.
The index of refraction (or refractive index) of a material is a dimensionless number that describes how fast light travels through that material. The higher the number, the slower light travels through the medium. More importantly for rendering materials, the refractive index determines how the path light travels is bent when entering the material. Higher indices of refraction will cause light to bend further away from the initial path.
Table 6 describes acceptable refractive indices for various types of materials.
Material | IOR |
---|---|
Air | 1.0 |
Water | 1.33 |
Common liquids | 1.33 to 1.5 |
Common gemstones | 1.58 to 2.33 |
Plastics, glass | 1.5 to 1.58 |
Other dielectric materials | 1.33 to 1.58 |
The appearance of a refractive material will greatly depend on the refractionType
and
refractionMode
settings of the material. Refer to section 4.2.21 and section 4.2.20
for more information.
The effect of ior
when refractionMode
is set to cubemap
and refractionType
is set to solid
can be seen in figure 21 (click on the image to see a larger version).
Figure 22 shows the comparison of a sphere of ior
1.0 with a sphere of ior
1.33, with
the refractionMode
set to screenspace
and the refractionType
set to solid
(click on the image to see a larger version).
Note that the ior
property also defines the reflectance (or specular intensity) of the surface.
When this property is defined it is not necessary to define the reflectance
property. Setting
either of these properties will automatically compute the other property. It is possible to specify
both, in which case their values are kept as-is, which can lead to physically impossible materials,
however, this might be desirable for artistic reasons.
See the Reflectance section for more information on the reflectance
property.
roughness
property. Rough materials will scatter
light, creating a diffusion effect useful to recreate “blurry” appearances such as frosted
glass, certain plastics, etc.
The transmission
property defines what ratio of diffuse light is transmitted through a refractive
material. This property only affects materials with a refractionMode
set to cubemap
or
screenspace
.
When transmission
is set to 0, no amount of light is transmitted and the diffuse component of
the surface is 100% visible. When transmission
is set to 1, all the light is transmitted and the
diffuse component is not visible anymore, only the specular component is.
The effect of transmission
on a glossy dielectric (ior
of 1.5, refractionMode
set to
cubemap
, refractionType
set to solid
) is shown in figure 23
(click on the image to see a larger version).
transmission
property is useful to create decals, paint, etc. at the surface of refractive
materials.
The absorption
property defines the absorption coefficients of light transmitted through the
material. Figure 24 shows the effect of absorption
on a refracting object with
an index of refraction of 1.5 and a base color set to white.
Transmittance through a volume is exponential with respect to the optical depth (defined either
with microThickness
or thickness
). The computed color follows the following formula:
$$color \cdot e^{-absorption \cdot distance}$$
Where distance
is either microThickness
or thickness
, that is the distance light will travel
through the material at a given point. If no thickness/distance is specified, the computed color
follows this formula instead:
$$color \cdot (1 - absorption)$$
The effect of varying the absorption
coefficients is shown in figure 25
(click on the image to see a larger version). In this picture, the object has a fixed thickness
of 4.5 and an index of refraction set to 1.3.
Setting the absorption coefficients directly can be unintuitive which is why we recommend working
with a transmittance color and a “at distance” factor instead. These two parameters allow an
artist to specify the precise color the material should have at a specified distance through the
volume. The value to pass to absorption
can be computed this way:
$$absorption = -\frac{ln(transmittanceColor)}{atDistance}$$
While this computation can be done in the material itself we recommend doing it offline whenever
possible. Filament provides an API for this purpose, Color::absorptionAtDistance()
.
The microThickness
and thickness
properties define the optical depth of the material of a
refracting object. microThickness
is used when refractionType
is set to thin
, and thickness
is used when refractionType
is set to volume
.
thickness
represents the thickness of solid objects in the direction of the normal, for
satisfactory results, this should be provided per fragment (e.g.: as a texture) or at least per
vertex.
microThickness
represent the thickness of the thin layer (shell) of an object, and can generally
be provided as a constant value. For example, a 1mm thin hollow sphere of radius 1m, would have a
thickness
of 1 and a microThickness
of 0.001. Currently thickness
is not used when
refractionType
is set to thin
. Both properties are made available for possible future use.
Both thickness
and microThickness
are used to compute the transmitted color of the material
when the absorption
property is set. In solid volumes, thickness
will also affect how light
rays are refracted.
The effect thickness
in a solid volume with refractionMode
set to screenSpace
is shown in
figure 26 (click on the image to see a larger version). Note how the thickness
value not only changes the effect of absorption
but also modifies the direction of the refracted
light.
Figure 27 shows what a prism with spatially varying thickness
looks like when
the refractionType
is set to solid
and absorption
coefficients are set.
All the material models described previously are designed to simulate dense surfaces, both at a macro and at a micro level. Clothes and fabrics are however often made of loosely connected threads that absorb and scatter incident light. When compared to hard surfaces, cloth is characterized by a softer specular lob with a large falloff and the presence of fuzz lighting, caused by forward/backward scattering. Some fabrics also exhibit two-tone specular colors (velvets for instance).
Figure 28 shows how the standard material model fails to capture the appearance of a sample of denim fabric. The surface appears rigid (almost plastic-like), more similar to a tarp than a piece of clothing. This figure also shows how important the softer specular lobe caused by absorption and scattering is to the faithful recreation of the fabric.
Velvet is an interesting use case for a cloth material model. As shown in figure 29 this type of fabric exhibits strong rim lighting due to forward and backward scattering. These scattering events are caused by fibers standing straight at the surface of the fabric. When the incident light comes from the direction opposite to the view direction, the fibers will forward scatter the light. Similarly, when the incident light from the same direction as the view direction, the fibers will scatter the light backward.
It is important to note that there are types of fabrics that are still best modeled by hard surface material models. For instance, leather, silk and satin can be recreated using the standard or anisotropic material models.
The cloth material model encompasses all the parameters previously defined for the standard material mode except for metallic and reflectance. Two extra parameters described in table 7 are also available.
Parameter | Definition |
---|---|
sheenColor | Specular tint to create two-tone specular fabrics (defaults to \(\sqrt{baseColor}\)) |
subsurfaceColor | Tint for the diffuse color after scattering and absorption through the material |
The type and range of each property is described in table 8.
Property | Type | Range | Note |
---|---|---|---|
sheenColor | float3 | [0..1] | Linear RGB |
subsurfaceColor | float3 | [0..1] | Linear RGB |
To create a velvet-like material, the base color can be set to black (or a dark color). Chromaticity information should instead be set on the sheen color. To create more common fabrics such as denim, cotton, etc. use the base color for chromaticity and use the default sheen color or set the sheen color to the luminance of the base color.
roughness
parameter make sure the sheenColor
is brighter than
baseColor
. This can be used to create a fuzz effect. Taking the luminance of baseColor
as the sheenColor
will produce a fairly natural effect that works for common cloth. A dark
baseColor
combined with a bright/saturated sheenColor
can be used to create velvet.
subsurfaceColor
parameter should be used with care. High values can interfere with shadows
in some areas. It is best suited for subtle transmission effects through the material.
The sheenColor
property can be used to directly modify the specular reflectance. It offers
better control over the appearance of cloth and gives give the ability to create
two-tone specular materials.
The effect of sheenColor
is shown in figure 30
(click on the image to see a larger version).
The subsurfaceColor
property is not physically-based and can be used to simulate the scattering,
partial absorption and re-emission of light in certain types of fabrics. This is particularly
useful to create softer fabrics.
subsurfaceColor
property is used.
The effect of subsurfaceColor
is shown in figure 31
(click on the image to see a larger version).
The unlit material model can be used to turn off all lighting computations. Its primary purpose is to render pre-lit elements such as a cubemap, external content (such as a video or camera stream), user interfaces, visualization/debugging etc. The unlit model exposes only two properties described in table 9.
Property | Definition |
---|---|
baseColor | Surface diffuse color |
emissive | Additional diffuse color to simulate emissive surfaces. This property is mostly useful in an HDR pipeline with a bloom pass |
postLightingColor | Additional color to blend with base color and emissive |
The type and range of each property is described in table 10.
Property | Type | Range | Note |
---|---|---|---|
baseColor | float4 | [0..1] | Pre-multiplied linear RGB |
emissive | float4 | rgb=[0..n], a=[0..1] | Linear RGB intensity in nits, alpha encodes the exposure weight |
postLightingColor | float4 | [0..1] | Pre-multiplied linear RGB |
The value of postLightingColor
is blended with the sum of emissive
and baseColor
according to
the blending mode specified by the postLightingBlending
material option.
Figure 32 shows an example of the unlit material model (click on the image to see a larger version).
This alternative lighting model exists to comply with legacy standards. Since it is not a physically-based formulation, we do not recommend using it except when loading legacy assets.
This model encompasses the parameters previously defined for the standard lit mode except for metallic, reflectance, and roughness. It adds parameters for specularColor and glossiness.
Parameter | Definition |
---|---|
baseColor | Surface diffuse color |
specularColor | Specular tint (defaults to black) |
glossiness | Glossiness (defaults to 0.0) |
The type and range of each property is described in table 12.
Property | Type | Range | Note |
---|---|---|---|
baseColor | float4 | [0..1] | Pre-multiplied linear RGB |
specularColor | float3 | [0..1] | Linear RGB |
glossiness | float | [0..1] | Inverse of roughness |
A material definition is a text file that describes all the information required by a material:
The material definition format is a format loosely based on JSON that we call JSONish. At the top level a material definition is composed of 3 different blocks that use the JSON object notation:
material {
// material properties
}
vertex {
// vertex shader, optional
}
fragment {
// fragment shader
}
A minimum viable material definition must contain a material
preamble and a fragment
block. The
vertex
block is optional.
In JSON, an object is made of key/value pairs. A JSON pair has the following syntax:
"key" : value
Where value can be a string, number, object, array or a literal (true
, false
or null
). While
this syntax is perfectly valid in a material definition, a variant without quotes around strings is
also accepted in JSONish:
key : value
Quotes remain mandatory when the string contains spaces.
The vertex
and fragment
blocks contain unescaped, unquoted GLSL code, which is not valid in JSON.
Single-line C++-style comments are allowed.
The key of a pair is case-sensitive.
The value of a pair is not case-sensitive.
The following code listing shows an example of a valid material definition. This definition uses
the lit material model (see Lit model section), uses the default opaque blending mode, requires
that a set of UV coordinates be presented in the rendered mesh and defines 3 user parameters. The
following sections of this document describe the material
and fragment
blocks in detail.
material {
name : "Textured material",
parameters : [
{
type : sampler2d,
name : texture
},
{
type : float,
name : metallic
},
{
type : float,
name : roughness
}
],
requires : [
uv0
],
shadingModel : lit,
blending : opaque
}
fragment {
void material(inout MaterialInputs material) {
prepareMaterial(material);
material.baseColor = texture(materialParams_texture, getUV0());
material.metallic = materialParams.metallic;
material.roughness = materialParams.roughness;
}
}
The material block is mandatory block that contains a list of property pairs to describe all non-shader data.
| |
Any string. Double quotes are required if the name contains spaces. | |
Sets the name of the material. The name is retained at runtime for debugging purpose. |
material {
name : stone
}
material {
name : "Wet pavement"
}
| |
An integer value, either 1, 2 or 3. Defaults to 1. |
Feature Level | Guaranteed features |
---|---|
1 | 9 textures per material |
2 | 9 textures per material, cubemap arrays, ESSL 3.10 |
3 | 12 textures per material, cubemap arrays, ESSL 3.10 |
Sets the feature level of the material. Each feature level defines a set of features the
material can use. If the material uses a feature not supported by the selected level, matc
will generate an error during compilation. A given feature level is guaranteed to support
all features of lower feature levels.
material {
featureLevel : 2
}
|
| |
Any of | |
Selects the material model as described in the Material models section. |
material {
shadingModel : unlit
}
material {
shadingModel : "subsurface"
}
array of parameter objects
Each entry is an object with the properties name
and type
, both of string
type. The
name must be a valid GLSL identifier. Entries also have an optional precision
, which can be
one of default
(best precision for the platform, typically high
on desktop, medium
on
mobile), low
, medium
, high
. The type must be one of the types described in
table 14.
Type | Description |
---|---|
bool | Single boolean |
bool2 | Vector of 2 booleans |
bool3 | Vector of 3 booleans |
bool4 | Vector of 4 booleans |
float | Single float |
float2 | Vector of 2 floats |
float3 | Vector of 3 floats |
float4 | Vector of 4 floats |
int | Single integer |
int2 | Vector of 2 integers |
int3 | Vector of 3 integers |
int4 | Vector of 4 integers |
uint | Single unsigned integer |
uint2 | Vector of 2 unsigned integers |
uint3 | Vector of 3 unsigned integers |
uint4 | Vector of 4 unsigned integers |
float3×3 | Matrix of 3×3 floats |
float4×4 | Matrix of 4×4 floats |
sampler2d | 2D texture |
sampler2dArray | Array of 2D textures |
samplerExternal | External texture (platform-specific) |
samplerCubemap | Cubemap texture |
Sampler types can also specify a format
which can be either int
or float
(defaults to
float
).
A parameter can define an array of values by appending [size]
after the type name, where
size
is a positive integer. For instance: float[9]
declares an array of nine float
values. This syntax does not apply to samplers as arrays are treated as separate types.
Lists the parameters required by your material. These parameters can be set at runtime using Filament's material API. Accessing parameters from the shaders varies depending on the type of parameter:
materialParams_
. For instance,
materialParams_myTexture
.
materialParams
.
For instance, materialParams.myColor
.material {
parameters : [
{
type : float4,
name : albedo
},
{
type : sampler2d,
format : float,
precision : high,
name : roughness
},
{
type : float2,
name : metallicReflectance
}
],
requires : [
uv0
],
shadingModel : lit,
}
fragment {
void material(inout MaterialInputs material) {
prepareMaterial(material);
material.baseColor = materialParams.albedo;
material.roughness = texture(materialParams_roughness, getUV0());
material.metallic = materialParams.metallicReflectance.x;
material.reflectance = materialParams.metallicReflectance.y;
}
}
array of constant objects
Each entry is an object with the properties name
and type
, both of string
type. The name
must be a valid GLSL identifier. Entries also have an optional default
, which can either be a
bool
or number
, depending on the type
of the constant. The type must be one of the types
described in table 15.
Type | Description | Default |
---|---|---|
int | A signed, 32 bit GLSL int | 0 |
float | A single-precision GLSL float | 0.0 |
bool | A GLSL bool | false |
Lists the constant parameters accepted by your material. These constants can be set, or
“specialized”, at runtime when loading a material package. Multiple materials can be loaded from
the same material package with differing constant parameter specializations. Once a material is
loaded from a material package, its constant parameters cannot be changed. Compared to regular
parameters, constant parameters allow the compiler to generate more efficient code. Access
constant parameters from the shader by prefixing the name with materialConstant_
. For example,
a constant parameter named myConstant
is accessed in the shader as
materialConstant_myConstant
. If a constant parameter is not set at runtime, the default is
used.
material {
constants : [
{
name : overrideAlpha,
type : bool
},
{
name : customAlpha,
type : float,
default : 0.5
}
],
shadingModel : lit,
blending : transparent,
}
fragment {
void material(inout MaterialInputs material) {
prepareMaterial(material);
if (materialConstants_overrideAlpha) {
material.baseColor.a = materialConstants_customAlpha;
material.baseColor.rgb *= material.baseColor.a;
}
}
}
array of string
Each entry must be any of dynamicLighting
, directionalLighting
, shadowReceiver
,
skinning
, ssr
, or stereo
.
Used to specify a list of shader variants that the application guarantees will never be
needed. These shader variants are skipped during the code generation phase, thus reducing
the overall size of the material.
Note that some variants may automatically be filtered out. For instance, all lighting related
variants (directionalLighting
, etc.) are filtered out when compiling an unlit
material.
Use the variant filter with caution, filtering out a variant required at runtime may lead
to crashes.
directionalLighting
, used when a directional light is present in the scene
dynamicLighting
, used when a non-directional light (point, spot, etc.) is present in the scene
shadowReceiver
, used when an object can receive shadows
skinning
, used when an object is animated using GPU skinning
fog
, used when global fog is applied to the scene
vsm
, used when VSM shadows are enabled and the object is a shadow receiver
ssr
, used when screen-space reflections are enabled in the View
stereo
, used when stereoscopic rendering is enabled in the Viewmaterial {
name : "Invisible shadow plane",
shadingModel : unlit,
shadowMultiplier : true,
blending : transparent,
variantFilter : [ skinning ]
}
boolean
true
or false
. Defaults to true
.
When set to true
(default value), the Y coordinate of UV attributes will be flipped when
read by this material's vertex shader. Flipping is equivalent to y = 1.0 - y
. When set
to false
, flipping is disabled and the UV attributes are read as is.
material {
flipUV : false
}
string
Any of low
, normal
, high
, default
. Defaults to default
.
Set some global quality parameters of the material. low
enables optimizations that can
slightly affect correctness and is the default on mobile platforms. normal
does not affect
correctness and is otherwise similar to low
. high
enables quality settings that can
adversely affect performance and is the default on desktop platforms.
material {
quality : default
}
boolean
true
or false
. Defaults to false
.
Allows a material to access the instance index (i.e.: gl_InstanceIndex
) of instanced
primitives using getInstanceIndex()
in the material's shader code. Never use
gl_InstanceIndex
directly. This is typically used with
RenderableManager::Builder::instances()
. getInstanceIndex()
is available in both the
vertex and fragment shader.
material {
instanced : true
}
boolean
true
or false
. Defaults to false
.
Only meaningful for vertexDomain:Device
materials, this parameter specifies whether the
filament clip-space transforms need to be applied or not, which affects TAA and guard bands.
Generally it needs to be applied because by definition vertexDomain:Device
materials
vertices are not transformed and used as is.
However, if the vertex shader uses for instance getViewFromClipMatrix()
(or other
matrices based on the projection), the clip-space transform is already applied.
Setting this parameter incorrectly can prevent TAA or the guard bands to work correctly.
material {
vertexDomainDeviceJittered : true
}
array of string
Each entry must be any of uv0
, uv1
, color
, position
, tangents
, custom0
through custom7
.
Lists the vertex attributes required by the material. The position
attribute is always
required and does not need to be specified. The tangents
attribute is automatically required
when selecting any shading model that is not unlit
. See the shader sections of this document
for more information on how to access these attributes from the shaders.
material {
parameters : [
{
type : sampler2d,
name : texture
},
],
requires : [
uv0,
custom0
],
shadingModel : lit,
}
fragment {
void material(inout MaterialInputs material) {
prepareMaterial(material);
material.baseColor = texture(materialParams_texture, getUV0());
material.baseColor.rgb *= getCustom0().rgb;
}
}
array of string
Up to 4 strings, each must be a valid GLSL identifier.
Defines custom interpolants (or variables) that are output by the material's vertex shader.
Each entry of the array defines the name of an interpolant. The full name in the fragment
shader is the name of the interpolant with the variable_
prefix. For instance, if you
declare a variable called eyeDirection
you can access it in the fragment shader using
variable_eyeDirection
. In the vertex shader, the interpolant name is simply a member of
the MaterialVertexInputs
structure (material.eyeDirection
in your example). Each
interpolant is of type float4
(vec4
) in the shaders. By default the precision of the
interpolant is highp
in both the vertex and fragment shaders.
An alternate syntax can be used to specify both the name and precision of the interpolant.
In this case the specified precision is used as-is in both fragment and vertex stages, in
particular if default
is specified the default precision is used is the fragment shader
(mediump
) and in the vertex shader (highp
).
material {
name : Skybox,
parameters : [
{
type : samplerCubemap,
name : skybox
}
],
variables : [
eyeDirection,
{
name : eyeColor,
precision : medium
}
],
vertexDomain : device,
depthWrite : false,
shadingModel : unlit
}
fragment {
void material(inout MaterialInputs material) {
prepareMaterial(material);
float3 sky = texture(materialParams_skybox, variable_eyeDirection.xyz).rgb;
material.baseColor = vec4(sky, 1.0);
}
}
vertex {
void materialVertex(inout MaterialVertexInputs material) {
float3 p = getPosition().xyz;
float3 u = mulMat4x4Float3(getViewFromClipMatrix(), p).xyz;
material.eyeDirection.xyz = mulMat3x3Float3(getWorldFromViewMatrix(), u);
}
}
string
Any of object
, world
, view
, device
. Defaults to object
.
Defines the domain (or coordinate space) of the rendered mesh. The domain influences how the vertices are transformed in the vertex shader. The possible domains are:
material {
vertexDomain : device
}
string
Any of smooth
, flat
. Defaults to smooth
.
Defines how interpolants (or variables) are interpolated between vertices. When this property
is set to smooth
, a perspective correct interpolation is performed on each interpolant.
When set to flat
, no interpolation is performed and all the fragments within a given
triangle will be shaded the same.
material {
interpolation : flat
}
string
Any of opaque
, transparent
, fade
, add
, masked
, multiply
, screen
, custom
. Defaults to opaque
.
Defines how/if the rendered object is blended with the content of the render target. The possible blending modes are:
source over
rule. This blending mode assumes
pre-multiplied alpha.
transparent
but transparency is also applied to specular lighting. In
transparent
mode, the material's alpha values only applies to diffuse lighting. This
blending mode is useful to fade lit objects in and out.
multiply
, the content of the
render target is brightened.
blendFunction
.blending
is set to masked
, alpha to coverage is automatically enabled for the material.
If this behavior is undesirable, refer to the Rasterization: alphaToCoverage section to turn
alpha to coverage off using the alphaToCoverage
property.material {
blending : transparent
}
object
srcRGB
, srcA
, dstRGB
, dstA
- srcRGB: source function applied to the RGB channels
- srcA: source function applied to the alpha channel
- srcRGB: destination function applied to the RGB channels
- srcRGB: destination function applied to the alpha channel
The values possible for each functions are one of zero
, one
, srcColor
, oneMinusSrcColor
,
dstColor
, oneMinusDstColor
, srcAlpha
, oneMinusSrcAlpha
, dstAlpha
,
oneMinusDstAlpha
, srcAlphaSaturate
material {
blending : custom,
blendFunction :
{
srcRGB: one,
srcA: one,
dstRGB: oneMinusSrcColor,
dstA: oneMinusSrcAlpha
}
}
string
Any of opaque
, transparent
, add
. Defaults to transparent
.
Defines how the postLightingColor
material property is blended with the result of the
lighting computations. The possible blending modes are:
postLightingColor
directly.
postLightingColor
, using Porter-Duff's source over
rule. This blending mode assumes
pre-multiplied alpha.
postLightingColor
.
postLightingColor
.
postLightingColor
,
and the result is added to the material's computed color.material {
postLightingBlending : add
}
string
Any of default
, twoPassesOneSide
or twoPassesTwoSides
. Defaults to default
.
Controls how transparent objects are rendered. It is only valid when the blending
mode is
not opaque
and refractionMode
is none
. None of these methods can accurately render
concave geometry, but in practice they are often good enough.
default
: the transparent object is rendered normally (as seen in figure 33),
honoring the culling
mode, etc.
twoPassesOneSide
: the transparent object is first rendered in the depth buffer, then again in
the color buffer, honoring the culling
mode. This effectively renders only half of the
transparent object as shown in figure 34.
twoPassesTwoSides
: the transparent object is rendered twice in the color buffer: first with its
back faces, then with its front faces. This mode lets you render both set of faces while reducing
or eliminating sorting issues, as shown in figure 35.
twoPassesTwoSides
can be combined with doubleSided
for better effect.material {
transparency : twoPassesOneSide
}
number
A value between 0.0
and 1.0
. Defaults to 0.4
.
Sets the minimum alpha value a fragment must have to not be discarded when the blending
mode
is set to masked
. If the fragment is not discarded, its source alpha is set to 1. When the
blending mode is not masked
, this value is ignored. This value can be used to controlled the
appearance of alpha-masked objects.
material {
blending : masked,
maskThreshold : 0.5
}
string
Any of none
, cubemap
, screenspace
. Defaults to none
.
Activates refraction when set to anything but none
. A value of cubemap
will only use the
IBL cubemap as source of refraction, while this is significantly more efficient, no scene
objects will be refracted, only the distant environment encoded in the cubemap. This mode is
adequate for an object viewer for instance. A value of screenspace
will employ the more
advanced screen-space refraction algorithm which allows opaque objects in the scene to be
refracted. In cubemap
mode, refracted rays are assumed to emerge from the center of the
object and the thickness
parameter is only used for computing the absorption, but has no
impact on the refraction itself. In screenspace
mode, refracted rays are assumed to travel
parallel to the view direction when they exit the refractive medium.
material {
refractionMode : cubemap,
}
string
Any of solid
, thin
. Defaults to solid
.
This is only meaningful when refractionMode
is set to anything but none
. refractionType
defines the refraction model used. solid
is used for thick objects such as a crystal ball,
an ice cube or as sculpture. thin
is used for thin objects such as a window, an ornament
ball or a soap bubble. In solid
mode all refracive objects are assumed to be a sphere
tangent to the entry point and of radius thickness
. In thin
mode, all refractive objects
are assumed to be flat and thin and of thickness thickness
.
material {
refractionMode : cubemap,
refractionType : thin,
}
| |
Any of | |
Defines which triangles should be culled: none, front-facing triangles, back-facing triangles or all. |
material {
culling : none
}
| |
| |
Enables or disables writes to the color buffer. |
material {
colorWrite : false
}
| |
| |
Enables or disables writes to the depth buffer. |
material {
depthWrite : false
}
boolean
true
or false
. Defaults to true
.
Enables or disables depth testing. When depth testing is disabled, an object rendered with this material will always appear on top of other opaque objects.
material {
depthCulling : false
}
boolean
true
or false
. Defaults to false
.
Enables two-sided rendering and its capability to be toggled at run time. When set to true
,
culling
is automatically set to none
; if the triangle is back-facing, the triangle's
normal is flipped to become front-facing. When explicitly set to false
, this allows the
double-sidedness to be toggled at run time.
material {
name : "Double sided material",
shadingModel : lit,
doubleSided : true
}
fragment {
void material(inout MaterialInputs material) {
prepareMaterial(material);
material.baseColor = materialParams.albedo;
}
}
boolean
true
or false
. Defaults to false
.
Enables or disables alpha to coverage. When alpha to coverage is enabled, the coverage of
fragment is derived from its alpha. This property is only meaningful when MSAA is enabled.
Note: setting blending
to masked
automatically enables alpha to coverage. If this is not
desired, you can override this behavior by setting alpha to coverage to false as in the
example below.
material {
name : "Alpha to coverage",
shadingModel : lit,
blending : masked,
alphaToCoverage : false
}
fragment {
void material(inout MaterialInputs material) {
prepareMaterial(material);
material.baseColor = materialParams.albedo;
}
}
string
default
or screenspace
. Defaults to default
.
Controls the source of specular reflections for this material. When this property is set to
default
, reflections only come image-based lights. When this property is set to
screenspace
, reflections come from the screen space's color buffer in addition to
image-based lights.
material {
name : "Glossy metal",
reflections : screenspace
}
boolean
true
or false
. Defaults to false
.
Only available in the unlit
shading model. If this property is enabled, the final color
computed by the material is multiplied by the shadowing factor (or visibility). This allows to
create transparent shadow-receiving objects (for instance an invisible ground plane in AR).
This is only supported with shadows from directional lights.
material {
name : "Invisible shadow plane",
shadingModel : unlit,
shadowMultiplier : true,
blending : transparent
}
fragment {
void material(inout MaterialInputs material) {
prepareMaterial(material);
// baseColor defines the color and opacity of the final shadow
material.baseColor = vec4(0.0, 0.0, 0.0, 0.7);
}
}
boolean
true
or false
. Defaults to false
.
Enables transparent shadows on this material. When this feature is enabled, Filament emulates
transparent shadows using a dithering pattern: they work best with variance shadow maps (VSM)
and blurring enabled. The opacity of the shadow derives directly from the alpha channel of
the material's baseColor
property. Transparent shadows can be enabled on opaque objects,
making them compatible with refractive/transmissive objects that are otherwise considered
opaque.
material {
name : "Clear plastic with stickers",
transparentShadow : true,
blending : transparent,
// ...
}
fragment {
void material(inout MaterialInputs material) {
prepareMaterial(material);
material.baseColor = texture(materialParams_baseColor, getUV0());
}
}
boolean
true
or false
. Defaults to true
.
When adding a clear coat layer, the change in index of refraction (IoR) is taken into account
to modify the specular color of the base layer. This appears to darken baseColor
. When this
effect is disabled, baseColor
is left unmodified. See figure 37 for an
example of how this property can affect a red metallic base layer.
material {
clearCoatIorChange : false
}
boolean
true
or false
. Defaults to false
on mobile, true
on desktop.
Multi-bounce ambient occlusion takes into account interreflections when applying ambient occlusion to image-based lighting. Turning this feature on avoids over-darkening occluded areas. It also takes the surface color into account to generate colored ambient occlusion. Figure 38 compares the ambient occlusion term of a surface with and without multi-bounce ambient occlusion. Notice how multi-bounce ambient occlusion introduces color in the occluded areas. Figure 39 toggles between multi-bounce ambient occlusion on and off on a lit brick material to highlight the effects of this property.
material {
multiBounceAmbientOcclusion : true
}
string
none
, simple
or bentNormals
. Defaults to none
on mobile, simple
on desktop. For
compatibility reasons, true
and false
are also accepted and map respectively to simple
and none
.
Static ambient occlusion maps and dynamic ambient occlusion (SSAO, etc.) apply to diffuse
indirect lighting. When setting this property to other than none
, a new ambient occlusion
term is derived from the surface roughness and applied to specular indirect lighting.
This effect helps remove unwanted specular reflections as shown in figure 40.
When this value is set to simple
, Filament uses a cheap but approximate method of computing
the specular ambient occlusion term. If this value is set to bentNormals
, Filament will use
a much more accurate but much more expensive method.
material {
specularAmbientOcclusion : simple
}
boolean
true
or false
. Defaults to false
.
Reduces specular aliasing and preserves the shape of specular highlights as an object moves
away from the camera. This anti-aliasing solution is particularly effective on glossy materials
(low roughness) but increases the cost of the material. The strength of the anti-aliasing
effect can be controlled using two other properties: specularAntiAliasingVariance
and
specularAntiAliasingThreshold
.
material {
specularAntiAliasing : true
}
float
A value between 0 and 1, set to 0.15 by default.
Sets the screen space variance of the filter kernel used when applying specular anti-aliasing. Higher values will increase the effect of the filter but may increase roughness in unwanted areas.
material {
specularAntiAliasingVariance : 0.2
}
float
A value between 0 and 1, set to 0.2 by default.
Sets the clamping threshold used to suppress estimation errors when applying specular anti-aliasing. When set to 0, specular anti-aliasing is disabled.
material {
specularAntiAliasingThreshold : 0.1
}
bool
true
or false
. Defaults to false
.
Enables custom surface shading when set to true. When surface shading is enabled, the fragment shader must provide an extra function that will be invoked for every light in the scene that may influence the current fragment. Please refer to the Custom surface shading section below for more information.
material {
customSurfaceShading : true
}
The vertex block is optional and can be used to control the vertex shading stage of the material.
The vertex block must contain valid
ESSL 3.0 code
(the version of GLSL supported in OpenGL ES 3.0). You are free to create multiple functions inside
the vertex block but you must declare the materialVertex
function:
vertex {
void materialVertex(inout MaterialVertexInputs material) {
// vertex shading code
}
}
This function will be invoked automatically at runtime by the shading system and gives you the
ability to read and modify material properties using the MaterialVertexInputs
structure. This full
definition of the structure can be found in the Material vertex inputs section.
You can use this structure to compute your custom variables/interpolants or to modify the value of the attributes. For instance, the following vertex blocks modifies both the color and the UV coordinates of the vertex over time:
material {
requires : [uv0, color]
}
vertex {
void materialVertex(inout MaterialVertexInputs material) {
material.color *= sin(getUserTime().x);
material.uv0 *= sin(getUserTime().x);
}
}
In addition to the MaterialVertexInputs
structure, your vertex shading code can use all the public
APIs listed in the Shader public APIs section.
struct MaterialVertexInputs {
float4 color; // if the color attribute is required
float2 uv0; // if the uv0 attribute is required
float2 uv1; // if the uv1 attribute is required
float3 worldNormal; // only if the shading model is not unlit
float4 worldPosition; // always available (see note below about world-space)
mat4 clipSpaceTransform; // default: identity, transforms the clip-space position, only available for `vertexDomain:device`
// variable* names are replaced with actual names
float4 variable0; // if 1 or more variables is defined
float4 variable1; // if 2 or more variables is defined
float4 variable2; // if 3 or more variables is defined
float4 variable3; // if 4 or more variables is defined
};
To achieve good precision, the worldPosition
coordinate in the vertex shader is shifted by the
camera position. To get the true world-space position, users can use
getUserWorldPosition()
, however be aware that the true world-position might not
be able to fit in a float
or might be represented with severely reduced precision.
By default the vertex shader of a material will flip the Y coordinate of the UV attributes
of the current mesh: material.uv0 = vec2(mesh_uv0.x, 1.0 - mesh_uv0.y)
. You can control
this behavior using the flipUV
property and setting it to false
.
You can use up to 8 custom vertex attributes, all of type float4
. These attributes can be accessed
using the vertex block shader functions getCustom0()
to getCustom7()
. However, before using
custom attributes, you must declare those attributes as required in the requires
property of
the material:
material {
requires : [
custom0,
custom1,
custom2
]
}
The fragment block must be used to control the fragment shading stage of the material. The fragment
block must contain valid
ESSL 3.0
code (the version of GLSL supported in OpenGL ES 3.0). You are free to create multiple functions
inside the fragment block but you must declare the material
function:
fragment {
void material(inout MaterialInputs material) {
prepareMaterial(material);
// fragment shading code
}
}
This function will be invoked automatically at runtime by the shading system and gives you the
ability to read and modify material properties using the MaterialInputs
structure. This full
definition of the structure can be found in the Material fragment inputs section. The full
definition of the various members of the structure can be found in the Material models section
of this document.
The goal of the material()
function is to compute the material properties specific to the selected
shading model. For instance, here is a fragment block that creates a glossy red metal using the
standard lit shading model:
fragment {
void material(inout MaterialInputs material) {
prepareMaterial(material);
material.baseColor.rgb = vec3(1.0, 0.0, 0.0);
material.metallic = 1.0;
material.roughness = 0.0;
}
}
Note that you must call prepareMaterial(material)
before exiting the material()
function.
This prepareMaterial
function sets up the internal state of the material model. Some of the APIs
described in the Fragment APIs section - like shading_normal
for instance - can only be accessed
after invoking prepareMaterial()
.
It is also important to remember that the normal
property - as described in the Material fragment
inputs section - only has an effect when modified before calling prepareMaterial()
. Here is an
example of a fragment shader that properly modifies the normal
property to implement a glossy red
plastic with bump mapping:
fragment {
void material(inout MaterialInputs material) {
// fetch the normal in tangent space
vec3 normal = texture(materialParams_normalMap, getUV0()).xyz;
material.normal = normal * 2.0 - 1.0;
// prepare the material
prepareMaterial(material);
// from now on, shading_normal, etc. can be accessed
material.baseColor.rgb = vec3(1.0, 0.0, 0.0);
material.metallic = 0.0;
material.roughness = 1.0;
}
}
struct MaterialInputs {
float4 baseColor; // default: float4(1.0)
float4 emissive; // default: float4(0.0, 0.0, 0.0, 1.0)
float4 postLightingColor; // default: float4(0.0)
// no other field is available with the unlit shading model
float roughness; // default: 1.0
float metallic; // default: 0.0, not available with cloth or specularGlossiness
float reflectance; // default: 0.5, not available with cloth or specularGlossiness
float ambientOcclusion; // default: 0.0
// not available when the shading model is subsurface or cloth
float3 sheenColor; // default: float3(0.0)
float sheenRoughness; // default: 0.0
float clearCoat; // default: 1.0
float clearCoatRoughness; // default: 0.0
float3 clearCoatNormal; // default: float3(0.0, 0.0, 1.0)
float anisotropy; // default: 0.0
float3 anisotropyDirection; // default: float3(1.0, 0.0, 0.0)
// only available when the shading model is subsurface or refraction is enabled
float thickness; // default: 0.5
// only available when the shading model is subsurface
float subsurfacePower; // default: 12.234
float3 subsurfaceColor; // default: float3(1.0)
// only available when the shading model is cloth
float3 sheenColor; // default: sqrt(baseColor)
float3 subsurfaceColor; // default: float3(0.0)
// only available when the shading model is specularGlossiness
float3 specularColor; // default: float3(0.0)
float glossiness; // default: 0.0
// not available when the shading model is unlit
// must be set before calling prepareMaterial()
float3 normal; // default: float3(0.0, 0.0, 1.0)
// only available when refraction is enabled
float transmission; // default: 1.0
float3 absorption; // default float3(0.0, 0.0, 0.0)
float ior; // default: 1.5
float microThickness; // default: 0.0, not available with refractionType "solid"
}
When customSurfaceShading
is set to true
in the material block, the fragment block must
declare and implement the surfaceShading
function:
fragment {
void material(inout MaterialInputs material) {
prepareMaterial(material);
// prepare material inputs
}
vec3 surfaceShading(
const MaterialInputs materialInputs,
const ShadingData shadingData,
const LightData lightData
) {
return vec3(1.0); // output of custom lighting
}
}
This function will be invoked for every light (directional, spot or point) in the scene that may
influence the current fragment. The surfaceShading
is invoked with 3 sets of data:
MaterialInputs
, as described in the Material fragment inputs section and prepared in the
material
function explained above
ShadingData
, a structure containing values derived from MaterialInputs
(see below)
LightData
, a structure containing values specific to the light being currently
evaluated (see below)
The surfaceShading
function must return an RGB color in linear sRGB. Alpha blending and alpha
masking are handled outside of this function and must therefore be ignored.
The surfaceShading
function is invoked even when a fragment is known to be fully in the shadow
of the current light (lightData.NdotL <= 0.0
or lightData.visibility <= 0.0
). This gives
more flexibility to the surfaceShading
function as it provides a simple way to handle constant
ambient lighting for instance.
Custom surface shading only works with the lit
shading model. Attempting to use any other
model will result in an error.
struct ShadingData {
// The material's diffuse color, as derived from baseColor and metallic.
// This color is pre-multiplied by alpha and in the linear sRGB color space.
vec3 diffuseColor;
// The material's specular color, as derived from baseColor and metallic.
// This color is pre-multiplied by alpha and in the linear sRGB color space.
vec3 f0;
// The perceptual roughness is the roughness value set in MaterialInputs,
// with extra processing:
// - Clamped to safe values
// - Filtered if specularAntiAliasing is enabled
// This value is between 0.0 and 1.0.
float perceptualRoughness;
// The roughness value expected by BRDFs. This value is the square of
// perceptualRoughness. This value is between 0.0 and 1.0.
float roughness;
};
struct LightData {
// The color (.rgb) and pre-exposed intensity (.w) of the light.
// The color is an RGB value in the linear sRGB color space.
// The pre-exposed intensity is the intensity of the light multiplied by
// the camera's exposure value.
vec4 colorIntensity;
// The normalized light vector, in world space (direction from the
// current fragment's position to the light).
vec3 l;
// The dot product of the shading normal (with normal mapping applied)
// and the light vector. This value is equal to the result of
// saturate(dot(getWorldSpaceNormal(), lightData.l)).
// This value is always between 0.0 and 1.0. When the value is <= 0.0,
// the current fragment is not visible from the light and lighting
// computations can be skipped.
float NdotL;
// The position of the light in world space.
vec3 worldPosition;
// Attenuation of the light based on the distance from the current
// fragment to the light in world space. This value between 0.0 and 1.0
// is computed differently for each type of light (it's always 1.0 for
// directional lights).
float attenuation;
// Visibility factor computed from shadow maps or other occlusion data
// specific to the light being evaluated. This value is between 0.0 and
// 1.0.
float visibility;
};
The material below shows how to use custom surface shading to implement a simplified toon shader:
material {
name : Toon,
shadingModel : lit,
parameters : [
{
type : float3,
name : baseColor
}
],
customSurfaceShading : true
}
fragment {
void material(inout MaterialInputs material) {
prepareMaterial(material);
material.baseColor.rgb = materialParams.baseColor;
}
vec3 surfaceShading(
const MaterialInputs materialInputs,
const ShadingData shadingData,
const LightData lightData
) {
// Number of visible shade transitions
const float shades = 5.0;
// Ambient intensity
const float ambient = 0.1;
float toon = max(ceil(lightData.NdotL * shades) / shades, ambient);
// Shadowing and attenuation
toon *= lightData.visibility * lightData.attenuation;
// Color and intensity
vec3 light = lightData.colorIntensity.rgb * lightData.colorIntensity.w;
return shadingData.diffuseColor * light * toon;
}
}
The result can be seen in figure 41.
While GLSL types can be used directly (vec4
or mat4
) we recommend the use of the following
type aliases:
Name | GLSL type | Description |
---|---|---|
bool2 | bvec2 | A vector of 2 booleans |
bool3 | bvec3 | A vector of 3 booleans |
bool4 | bvec4 | A vector of 4 booleans |
int2 | ivec2 | A vector of 2 integers |
int3 | ivec3 | A vector of 3 integers |
int4 | ivec4 | A vector of 4 integers |
uint2 | uvec2 | A vector of 2 unsigned integers |
uint3 | uvec3 | A vector of 3 unsigned integers |
uint4 | uvec4 | A vector of 4 unsigned integers |
float2 | float2 | A vector of 2 floats |
float3 | float3 | A vector of 3 floats |
float4 | float4 | A vector of 4 floats |
float4×4 | mat4 | A 4×4 float matrix |
float3×3 | mat3 | A 3×3 float matrix |
Name | Type | Description |
---|---|---|
PI | float | A constant that represent \(\pi\) |
HALF_PI | float | A constant that represent \(\frac{\pi}{2}\) |
saturate(float x) | float | Clamps the specified value between 0.0 and 1.0 |
pow5(float x) | float | Computes \(x^5\) |
sq(float x) | float | Computes \(x^2\) |
max3(float3 v) | float | Returns the maximum value of the specified float3 |
mulMat4×4Float3(float4×4 m, float3 v) | float4 | Returns \(m * v\) |
mulMat3×3Float3(float4×4 m, float3 v) | float4 | Returns \(m * v\) |
Name | Type | Description |
---|---|---|
getViewFromWorldMatrix() | float4×4 | Matrix that converts from world space to view/eye space |
getWorldFromViewMatrix() | float4×4 | Matrix that converts from view/eye space to world space |
getClipFromViewMatrix() | float4×4 | Matrix that converts from view/eye space to clip (NDC) space |
getViewFromClipMatrix() | float4×4 | Matrix that converts from clip (NDC) space to view/eye space |
getClipFromWorldMatrix() | float4×4 | Matrix that converts from world to clip (NDC) space |
getWorldFromClipMatrix() | float4×4 | Matrix that converts from clip (NDC) space to world space |
Name | Type | Description |
---|---|---|
getResolution() | float4 | Dimensions of the view's effective (physical) viewport in pixels: width , height , 1 / width , 1 / height . This might be different from View::getViewport() for instance because of added rendering guard-bands. |
getWorldCameraPosition() | float3 | Position of the camera/eye in world space (see note below) |
getWorldOffset() | float3 | [deprecated] The shift required to obtain API-level world space. Use getUserWorldPosition() instead |
getUserWorldFromWorldMatrix() | float4×4 | Matrix that converts from world space to API-level (user) world space. |
getTime() | float | Current time as a remainder of 1 second. Yields a value between 0 and 1 |
getUserTime() | float4 | Current time in seconds: time , (double)time - time , 0 , 0 |
getUserTimeMod(float m) | float | Current time modulo m in seconds |
getExposure() | float | Photometric exposure of the camera |
getEV100() | float | Exposure value at ISO 100 of the camera |
To achieve good precision, the “world space” in Filament's shading system does not necessarily
match the API-level world space. To obtain the position of the API-level camera, custom
materials can use getUserWorldFromWorldMatrix()
to transform getWorldCameraPosition()
.
Name | Type | Description |
---|---|---|
getMaterialGlobal0() | float4 | A vec4 visible by all materials, its value is set by View::setMaterialGlobal(0, float4) . Its default value is {0,0,0,1}. |
getMaterialGlobal1() | float4 | A vec4 visible by all materials, its value is set by View::setMaterialGlobal(1, float4) . Its default value is {0,0,0,1}. |
getMaterialGlobal2() | float4 | A vec4 visible by all materials, its value is set by View::setMaterialGlobal(2, float4) . Its default value is {0,0,0,1}. |
getMaterialGlobal3() | float4 | A vec4 visible by all materials, its value is set by View::setMaterialGlobal(3, float4) . Its default value is {0,0,0,1}. |
The following APIs are only available from the vertex block:
Name | Type | Description |
---|---|---|
getPosition() | float4 | Vertex position in the domain defined by the material (default: object/model space) |
getCustom0() to getCustom7() | float4 | Custom vertex attribute |
getWorldFromModelMatrix() | float4×4 | Matrix that converts from model (object) space to world space |
getWorldFromModelNormalMatrix() | float3×3 | Matrix that converts normals from model (object) space to world space |
getVertexIndex() | int | Index of the current vertex |
getEyeIndex() | int | Index of the eye being rendered, starting at 0 |
The following APIs are only available from the fragment block:
Name | Type | Description |
---|---|---|
getWorldTangentFrame() | float3×3 | Matrix containing in each column the tangent (frame[0] ), bi-tangent (frame[1] ) and normal (frame[2] ) of the vertex in world space. If the material does not compute a tangent space normal for bump mapping or if the shading is not anisotropic, only the normal is valid in this matrix. |
getWorldPosition() | float3 | Position of the fragment in world space (see note below about world-space) |
getUserWorldPosition() | float3 | Position of the fragment in API-level (user) world-space (see note below about world-space) |
getWorldViewVector() | float3 | Normalized vector in world space from the fragment position to the eye |
getWorldNormalVector() | float3 | Normalized normal in world space, after bump mapping (must be used after prepareMaterial() ) |
getWorldGeometricNormalVector() | float3 | Normalized normal in world space, before bump mapping (can be used before prepareMaterial() ) |
getWorldReflectedVector() | float3 | Reflection of the view vector about the normal (must be used after prepareMaterial() ) |
getNormalizedViewportCoord() | float3 | Normalized user viewport position (i.e. NDC coordinates normalized to [0, 1] for the position, [1, 0] for the depth), can be used before prepareMaterial() ). Because the user viewport is smaller than the actual physical viewport, these coordinates can be negative or superior to 1 in the non-visible area of the physical viewport. |
getNdotV() | float | The result of dot(normal, view) , always strictly greater than 0 (must be used after prepareMaterial() ) |
getColor() | float4 | Interpolated color of the fragment, if the color attribute is required |
getUV0() | float2 | First interpolated set of UV coordinates, only available if the uv0 attribute is required |
getUV1() | float2 | First interpolated set of UV coordinates, only available if the uv1 attribute is required |
getMaskThreshold() | float | Returns the mask threshold, only available when blending is set to masked |
inverseTonemap(float3) | float3 | Applies the inverse tone mapping operator to the specified linear sRGB color and returns a linear sRGB color. This operation may be an approximation and works best with the “Filmic” tone mapping operator |
inverseTonemapSRGB(float3) | float3 | Applies the inverse tone mapping operator to the specified non-linear sRGB color and returns a linear sRGB color. This operation may be an approximation and works best with the “Filmic” tone mapping operator |
luminance(float3) | float | Computes the luminance of the specified linear sRGB color |
ycbcrToRgb(float, float2) | float3 | Converts a luminance and CbCr pair to a sRGB color |
uvToRenderTargetUV(float2) | float2 | Transforms a UV coordinate to allow sampling from a RenderTarget attachment |
To obtain API-level world-space coordinates, custom materials should use getUserWorldPosition()
or use getUserWorldFromWorldMatrix()
. Note that API-level world-space coordinates should
never or rarely be used because they may not fit in a float3 or have severely reduced precision.
When sampling from a filament::Texture
that is attached to a filament::RenderTarget
for
materials in the surface domain, please use uvToRenderTargetUV
to transform the texture
coordinate. This will flip the coordinate depending on which backend is being used.
Material packages can be compiled from material definitions using the command line tool called
matc
. The simplest way to use matc
is to specify an input material definition (car_paint.mat
in the example below) and an output material package (car_paint.filamat
in the example below):
$ matc -o ./materials/bin/car_paint.filamat ./materials/src/car_paint.mat
matc
attempts to validate shaders when compiling a material package. The example below shows an
example of an error message generated when compiling a material definition containing a typo in the
fragment shader (metalic
instead of metallic
). The reported line numbers are line numbers in the
source material definition file.
ERROR: 0:13: 'metalic' : no such field in structure
ERROR: 0:13: '' : compilation terminated
ERROR: 2 compilation errors. No code generated.
Could not compile material metal.mat
The command line flags relevant to application development are described in table 16.
Flag | Value | Usage |
---|---|---|
-o, —output | [path] | Specify the output file path |
-p, —platform | desktop/mobile/all | Select the target platform(s) |
-a, —api | opengl/vulkan/all | Specify the target graphics API |
-S, —optimize-size | N/A | Optimize compiled material for size instead of just performance |
-r, —reflect | parameters | Outputs the specified metadata as JSON |
-v, —variant-filter | [variant] | Filters out the specified, comma-separated variants |
matc
offers a few other flags that are irrelevant to application developers and for internal
use only.
By default, matc
generates material packages containing shaders for all supported platforms. If
you wish to reduce the size of your material packages, it is recommended to select only the
appropriate target platform. For instance, to compile a material package for Android only, run
the following command:
$ matc -p mobile -o ./materials/bin/car_paint.filamat ./materials/src/car_paint.mat
By default, matc
generates material packages containing shaders for the OpenGL API. You can choose
to generate shaders for the Vulkan API in addition to the OpenGL shaders. If you intend on targeting
only Vulkan capable devices, you can reduce the size of the material packages by generating only
the set of Vulkan shaders:
$ matc -a vulkan -o ./materials/bin/car_paint.filamat ./materials/src/car_paint.mat
This flag applies fewer optimization techniques to try and keep the final material as small as possible. If the compiled material is deemed too large by default, using this flag might be a good compromise between runtime performance and size.
This flag was designed to help build tools around matc
. It allows you to print out specific
metadata in JSON format. The example below prints out the list of parameters defined in Filament's
standard skybox material. It produces a list of 2 parameters, named showSun
and skybox
,
respectively a boolean and a cubemap texture.
$ matc --reflect parameters filament/src/materials/skybox.mat
{
"parameters": [
{
"name": "showSun",
"type": "bool",
"size": "1"
},
{
"name": "skybox",
"type": "samplerCubemap",
"format": "float",
"precision": "default"
}
]
}
This flag can be used to further reduce the size of a compiled material. It is used to specify a
list of shader variants that the application guarantees will never be needed. These shader variants
are skipped during the code generation phase of matc
, thus reducing the overall size of the
material.
The variants must be specified as a comma-separated list, using one of the following available variants:
directionalLighting
, used when a directional light is present in the scene
dynamicLighting
, used when a non-directional light (point, spot, etc.) is present in the scene
shadowReceiver
, used when an object can receive shadows
skinning
, used when an object is animated using GPU skinning or vertex morphing
fog
, used when global fog is applied to the scene
vsm
, used when VSM shadows are enabled and the object is a shadow receiver
ssr
, used when screen-space reflections are enabled in the ViewExample:
--variant-filter=skinning,shadowReceiver
Note that some variants may automatically be filtered out. For instance, all lighting related
variants (directionalLighting
, etc.) are filtered out when compiling an unlit
material.
When this flag is used, the specified variant filters are merged with the variant filters specified in the material itself.
Use this flag with caution, filtering out a variant required at runtime may lead to crashes.
If the color data comes from a texture, simply make sure you use an sRGB texture to benefit from automatic hardware conversion from sRGB to linear. If the color data is passed as a parameter to the material you can convert from sRGB to linear by running the following algorithm on each color channel:
float sRGB_to_linear(float color) {
return color <= 0.04045 ? color / 12.92 : pow((color + 0.055) / 1.055, 2.4);
}
Alternatively you can use one of the two cheaper but less accurate versions shown below:
// Cheaper
linearColor = pow(color, 2.2);
// Cheapest
linearColor = color * color;
A color uses pre-multiplied alpha if its RGB components are multiplied by the alpha channel:
// Compute pre-multiplied color
color.rgb *= color.a;
If the color is sampled from a texture, you can simply ensure that the texture data is pre-multiplied ahead of time. On Android, any texture uploaded from a Bitmap will be pre-multiplied by default.
The number of usable sampler parameters (e.g.: type is sampler2d
) in materials is limited and
depends on the material properties, shading model, feature level and variant filter.
unlit
materials can use up to 12 samplers by default.
lit
materials can use up to 9 samplers by default, however if refractionMode
or reflectionMode
is set to screenspace
that number is reduced to 8.
Finally if variantFilter
contains the fog
filter, an extra sampler is made available, such that
unlit
materials can use up to 13 and lit
materials up to 10 samplers by default.
16 samplers are available.
Be aware that external
samplers account for 2 regular samplers.