Material definitions are text files that describe all the information required by a material, including shader code, parameters, and rendering state.
Filament uses a JSON-like format called “JSONish” for material definitions. A material definition consists of three main blocks:
material {
// material properties
}
vertex {
// vertex shader code (optional)
}
fragment {
// fragment shader code (required)
}
Differences from JSON
- Quotes around strings are optional (unless the string contains spaces)
- Single-line C++-style comments are allowed
- Keys are case-sensitive, values are not
- The
vertex and fragment blocks contain unescaped GLSL code
Basic Material Example
Here’s a simple textured material:
material {
name : "Textured material",
shadingModel : lit,
parameters : [
{
type : sampler2d,
name : texture
},
{
type : float,
name : metallic
},
{
type : float,
name : roughness
}
],
requires : [
uv0
]
}
fragment {
void material(inout MaterialInputs material) {
prepareMaterial(material);
material.baseColor = texture(materialParams_texture, getUV0());
material.metallic = materialParams.metallic;
material.roughness = materialParams.roughness;
}
}
Material Block Properties
General Properties
name
material {
name : "My Material" // or just: name : stone
}
Sets the name of the material for debugging purposes.
shadingModel
material {
shadingModel : lit // or subsurface, cloth, unlit, specularGlossiness
}
Selects the material model. Defaults to lit.
featureLevel
material {
featureLevel : 2 // 1, 2, or 3 (default: 1)
}
Sets the feature level:
- Level 1: 9 textures per material
- Level 2: 9 textures, cubemap arrays, ESSL 3.10
- Level 3: 12 textures, cubemap arrays, ESSL 3.10
Parameters
Define user-controllable parameters that can be set at runtime:
material {
parameters : [
{
type : float3,
name : baseColor
},
{
type : sampler2d,
name : albedoMap,
precision : high
},
{
type : float,
name : roughness
}
]
}
Parameter Types
Scalar and Vector Types:
bool, bool2, bool3, bool4
int, int2, int3, int4
uint, uint2, uint3, uint4
float, float2, float3, float4
Matrix Types:
Sampler Types:
sampler2d - 2D texture
sampler2dArray - Array of 2D textures
samplerExternal - External texture (platform-specific)
samplerCubemap - Cubemap texture
Arrays:
Append [size] to create arrays:
{
type : float[9],
name : coefficients
}
Accessing Parameters
In shaders:
- Samplers: Use
materialParams_ prefix
- Other types: Use
materialParams. structure
fragment {
void material(inout MaterialInputs material) {
prepareMaterial(material);
// Sampler access
material.baseColor = texture(materialParams_albedoMap, getUV0());
// Parameter access
material.roughness = materialParams.roughness;
}
}
Constants
Constants are specialized at material load time and cannot be changed:
material {
constants : [
{
name : overrideAlpha,
type : bool
},
{
name : customAlpha,
type : float,
default : 0.5
}
]
}
fragment {
void material(inout MaterialInputs material) {
prepareMaterial(material);
if (materialConstants_overrideAlpha) {
material.baseColor.a = materialConstants_customAlpha;
}
}
}
Access constants with materialConstants_ prefix.
Required Attributes
Specify which vertex attributes your material needs:
material {
requires : [
uv0, // First UV set
uv1, // Second UV set
color, // Vertex colors
tangents, // Tangents (auto-required for lit models)
custom0, // Custom attribute 0
custom1 // Custom attributes 0-7
]
}
The position attribute is always required. The tangents attribute is automatically required for all shading models except unlit.
Variables (Interpolants)
Define custom data to pass from vertex to fragment shader:
material {
variables : [
eyeDirection,
{
name : eyeColor,
precision : medium
}
]
}
vertex {
void materialVertex(inout MaterialVertexInputs material) {
material.eyeDirection.xyz = /* calculate direction */;
}
}
fragment {
void material(inout MaterialInputs material) {
prepareMaterial(material);
// Access with variable_ prefix
vec3 dir = variable_eyeDirection.xyz;
}
}
Up to 5 variables (4 if color attribute is used). Each is a float4.
Blending Modes
material {
blending : opaque // or transparent, fade, add, masked, multiply, screen
}
| Mode | Description |
|---|
opaque | No blending, alpha ignored |
transparent | Alpha compositing with pre-multiplied alpha |
fade | Transparency applied to both diffuse and specular |
add | Additive blending |
masked | Alpha masking with threshold |
multiply | Multiply blending (darkening) |
screen | Screen blending (brightening) |
Rasterization State
material {
culling : back, // none, front, back, frontAndBack
colorWrite : true, // Enable/disable color writes
depthWrite : true, // Enable/disable depth writes
depthCulling : true, // Enable/disable depth testing
doubleSided : false // Two-sided rendering
}
Transparency Options
material {
transparency : default, // default, twoPassesOneSide, twoPassesTwoSides
maskThreshold : 0.4, // Alpha threshold for masked mode
refractionMode : none, // none, cubemap, screenspace
refractionType : solid // solid, thin
}
Fragment Block
The fragment block is required and must implement the material() function:
fragment {
void material(inout MaterialInputs material) {
prepareMaterial(material);
// Set material properties
material.baseColor.rgb = vec3(1.0, 0.0, 0.0);
material.metallic = 1.0;
material.roughness = 0.3;
}
}
Important Rules
- Always call
prepareMaterial() before returning
- Set
normal before calling prepareMaterial() if using normal mapping
- Set other properties after
prepareMaterial()
Normal Mapping Example
fragment {
void material(inout MaterialInputs material) {
// Fetch and set normal BEFORE prepareMaterial
vec3 normal = texture(materialParams_normalMap, getUV0()).xyz;
material.normal = normal * 2.0 - 1.0;
// NOW call prepareMaterial
prepareMaterial(material);
// Set other properties
material.baseColor.rgb = vec3(1.0, 0.0, 0.0);
material.roughness = 0.5;
}
}
Vertex Block (Optional)
The vertex block allows custom vertex transformations:
material {
requires : [uv0, color],
variables : [customData]
}
vertex {
void materialVertex(inout MaterialVertexInputs material) {
// Modify attributes
material.color *= sin(getUserTime().x);
material.uv0 *= 2.0;
// Set custom variables
material.customData = vec4(material.worldPosition.xyz, 1.0);
}
}
Complete Examples
Lit Material with Textures
material {
name : "PBR Textured",
shadingModel : lit,
requires : [uv0],
parameters : [
{ type : sampler2d, name : albedo },
{ type : sampler2d, name : roughness },
{ type : sampler2d, name : metallic },
{ type : sampler2d, name : normal },
{ type : sampler2d, name : ao }
]
}
fragment {
void material(inout MaterialInputs material) {
vec2 uv = getUV0();
// Normal mapping before prepareMaterial
material.normal = texture(materialParams_normal, uv).xyz * 2.0 - 1.0;
prepareMaterial(material);
material.baseColor = texture(materialParams_albedo, uv);
material.roughness = texture(materialParams_roughness, uv).r;
material.metallic = texture(materialParams_metallic, uv).r;
material.ambientOcclusion = texture(materialParams_ao, uv).r;
}
}
Cloth Material
material {
name : "Fabric",
shadingModel : cloth,
parameters : [
{ type : float3, name : baseColor },
{ type : float, name : roughness },
{ type : float3, name : sheenColor },
{ type : float3, name : subsurfaceColor }
],
specularAntiAliasing : true
}
fragment {
void material(inout MaterialInputs material) {
prepareMaterial(material);
material.baseColor.rgb = materialParams.baseColor;
material.roughness = materialParams.roughness;
material.sheenColor = materialParams.sheenColor;
material.subsurfaceColor = materialParams.subsurfaceColor;
}
}
Unlit Material
material {
name : "UI Element",
shadingModel : unlit,
blending : transparent,
depthWrite : false,
parameters : [
{ type : sampler2d, name : texture },
{ type : float4, name : color }
],
requires : [uv0]
}
fragment {
void material(inout MaterialInputs material) {
prepareMaterial(material);
material.baseColor = texture(materialParams_texture, getUV0());
material.baseColor *= materialParams.color;
}
}
Next Steps