Skip to content

Materials

A material is the shading half of a Mesh. It decides how a surface responds to light and what color it ends up. Flutter Scene ships two built-in materials, PhysicallyBasedMaterial and UnlitMaterial, and lets you write your own with ShaderMaterial.

PhysicallyBasedMaterial (PBR) is the default choice for realistic surfaces. It models a surface with a base color plus two scalars, metallic and roughness, and lights it with the scene’s environment.

Drag the sliders to see how those two values reshape the same sphere. Metallic moves the surface from a plastic-like dielectric to a mirror-like metal, and roughness blurs its reflections from sharp to matte.

A metal sphere reflecting an imported environment map. Drag to change metallic and roughness.

The material is just a few fields.

final material = PhysicallyBasedMaterial()
..baseColorFactor = vm.Vector4(0.85, 0.30, 0.25, 1.0) // linear RGBA
..metallicFactor = 1.0 // 0 = dielectric, 1 = metal
..roughnessFactor = 0.25; // 0 = mirror, 1 = fully diffuse
final mesh = Mesh(SphereGeometry(radius: 1.0), material);
scene.add(Node(mesh: mesh));

PBR surfaces need light to look like anything. A new Scene already comes with a procedural studio environment, so a material shows up the moment you add it. The sphere above goes a step further and loads an imported equirectangular environment map, which both lights it and appears in its reflections (most visible when it is metallic and smooth). See Lighting and environment for loading environments, drawing one as a skybox, and adding a directional light.

Every factor has a matching texture that modulates it across the surface. Assign a gpu.Texture (see Assets and loading for getting one) to any of these.

final material = PhysicallyBasedMaterial()
..baseColorTexture = albedo // multiplied by baseColorFactor
..metallicRoughnessTexture = metalRough // blue = metallic, green = roughness
..normalTexture = normal // tangent-space normals
..occlusionTexture = ao
..emissiveTexture = emissive;

The base color texture is multiplied by baseColorFactor, so leave the factor white to use the texture unchanged, or tint it. normalScale, occlusionStrength, and emissiveFactor scale their respective inputs.

emissiveFactor adds light-independent color on top of the shaded result, for surfaces that should appear to glow (screens, lava, neon). It is linear HDR, so values above 1.0 read as bright and feed into bloom when it is enabled.

material.emissiveFactor = vm.Vector4(2.0, 0.4, 0.1, 1.0);

alphaMode controls how the material’s alpha is used.

  • AlphaMode.opaque (the default) ignores alpha.
  • AlphaMode.blend routes the mesh through the depth-sorted translucent pass and alpha-blends it.
  • AlphaMode.mask discards fragments below alphaCutoff, for cutout surfaces like foliage.

UnlitMaterial ignores scene lighting and draws a flat color or texture. Use it for UI overlays, debug visualization, billboards, or a deliberately stylized look. It is also the cheapest material to draw.

final material = UnlitMaterial()
..baseColorFactor = vm.Vector4(0.1, 0.6, 0.9, 1.0);
// Or texture it:
final textured = UnlitMaterial(colorTexture: myTexture);

Like PBR, it supports alphaMode for transparency and vertexColorWeight for mixing in per-vertex colors.

When the built-ins are not enough, you write your own shader. The supported way is the .fmat format, where you declare typed parameters and a small Surface() function (plus an optional Vertex() function to displace and animate geometry) and a build hook compiles it. Your shader follows the same output contract as the built-ins, linear HDR premultiplied by alpha, so exposure and tone mapping work the same. See Custom materials for the full workflow, unlit and lit shading, vertex customization, and live demos.

Material, PhysicallyBasedMaterial, UnlitMaterial, and PreprocessedMaterial (see Custom materials).