MaterialX icon indicating copy to clipboard operation
MaterialX copied to clipboard

Proposal: MaterialX Flake3d Node

Open msuzuki-nvidia opened this issue 3 months ago • 6 comments

MaterialX Flake3d Node Proposal

Summary

Flake generation provides a procedural method to create sparkling, metallic flake effects commonly seen in automotive paints, cosmetics, and other materials with embedded reflective particles. This document proposes a new MaterialX node <flake3d> based on the algorithm developed by Matthias Raab that generates procedurally distributed flake normals with controllable size, roughness, and coverage.

This node is particularly valuable for creating realistic car paint materials, glittery surfaces, and other effects that require procedural generation of micro-scale reflective particles without requiring high-resolution texture maps.

Key Features

The <flake3d> node provides the following key features:

  • Procedural generation: Creates infinite variations of flake patterns without requiring texture assets
  • Physically-based distribution: Uses microfacet theory (GGX) for realistic normal perturbation
  • Efficient evaluation: Optimized algorithm suitable for real-time rendering
  • Artistic control: Intuitive parameters for size, roughness, and coverage density

Parameters

<flake3d>

Parameter Type Range Default Description
size float [0.0, 5.0] 0.01 Size of individual flakes
roughness float [0.0, 1.0] 0.1 Roughness of the flake normal distribution
coverage float [0.0, 1.0] 0.5 Density of flake coverage (0 = no flakes, 1 = full coverage)
position vector3 Pworld 3D position for flake generation
normal vector3 Nworld Base surface normal
tangent vector3 Tworld Surface tangent vector
bitangent vector3 Bworld Surface bitangent vector

Outputs

The node returns multiple outputs to enable advanced shading workflows:

Output Type Range Description
id int [0, INT32_MAX] Unique identifier for each flake (0 for no flake)
rand float [0.0, 1.0] Random value per flake for additional variation (0 for no flake)
presence float [0.0, 1.0] Depth-based presence value per flake for additional variation (0 for no flake)
normal vector3 The computed flake normal (base normal if no flake present)

Examples

Outputs

rand presence normal

Basic Flake Material

<flake3d name="flakes" type="multioutput">
  <input name="size" type="float" value="0.01"/>
  <input name="roughness" type="float" value="0.2"/>
  <input name="coverage" type="float" value="0.5"/>
</flake3d>

<standard_surface name="surface">
  <input name="base" type="float" value="0.1"/>
  <input name="base_color" type="color3f" value="0.06, 0.424, 0.679"/>
  <input name="metalness" type="float" value="1.0"/>
  <input name="specular_roughness" type="float" value="0.2"/>
  <input name="coat" type="float" value="0.5"/>
  <input name="coat_roughness" type="float" value="0.05"/>
  <input name="normal" type="vector3" nodename="flakes" output="normal"/>
</standard_surface>
size = 0.01 size = 0.1 size = 1.0
roughness = 0.05 roughness = 0.2 roughness = 0.5
coverage = 0.1 coverage = 0.5 coverage = 1.0

msuzuki-nvidia avatar Oct 08 '25 21:10 msuzuki-nvidia

This is excellent, thanks for contributing! I love having all of those useful outputs, this is really great.

Would it make sense to call this flake3d, since it works with 3d positions? Have there been discussions on a 2d version that works from texture coordinates, using this new implementation?

crydalch avatar Oct 10 '25 15:10 crydalch

That makes sense as the current algorithm requires 3D space. Updated the description. Thanks!

We haven’t discussed 2D support yet. Sharing code with a 2D version would be difficult if we add it, so calling this “3D” seems reasonable.

msuzuki-nvidia avatar Oct 10 '25 19:10 msuzuki-nvidia

@crydalch Matthias told me that it actually gives acceptable result with texture coordinates, using (u, v, 0) for example (I tested). Maybe we could call this <flake>, without "3d"?

msuzuki-nvidia avatar Oct 13 '25 21:10 msuzuki-nvidia

My vote would be to call it flake3d anyway. It would fit more nicely with other procedural nodes, such as noise etc. It we can trivially add flake2d at the same time, but having that node use the 3D version and just pass (u, v, 0) then great. I think the naming leaves us opportunities to optimize different backends in different ways later too.

ld-kerley avatar Oct 13 '25 21:10 ld-kerley

Curious why you wouldn't consider this a shader? What are the benefits of adding it to MaterialX as opposed to writing an OSL shader? Although the general functionality of having a Scatter type node in mtlx might be useful. Where you could hook any 'shape' and then trans/rot/scale, blend, colorize a coverage distribution. Could be a set of predefined distributions(linear, ramp, radial, bevel) or texture driven. Is there anything in the Flake3D node, that you couldn't create with that type of scatter? But perhaps even the Scatter should simply be a shader?

biffpixel avatar Oct 21 '25 21:10 biffpixel

Because generalized scattering (aka “bombing”) on arbitrary shape is very slow. To achieve a metallic‑flake look, you’d need to generate a large number of instances, which would result in unacceptable performance. We need this to run at interactive speed.

msuzuki-nvidia avatar Oct 21 '25 22:10 msuzuki-nvidia