Skip to content

bhouston/material-fidelity

Repository files navigation

Material Fidelity Suite

The Material Fidelity Suite (avalable here online: https://material-fidelity.ben3d.ca) is a website and toolset for generating and comparing renderer output for known MaterialX sample scenes. It is the test suite behind the work described in Pixel-Perfect MaterialX in Blender and Three.js.

MaterialX showcase — materialxview vs Three.js vs Blender

Every material is rendered through MaterialX reference backends and compared side-by-side against Three.js (rasterizer) and Blender Eevee/Cycles. The suite covers 400+ materials across standard_surface, gltf_pbr, and open_pbr_surface, including procedural noise, math nodes, compositing, coordinate transforms, and surface-model variants.

MaterialX 2D noise — reference vs Blender before and after

Related Work

  • Three.js PR #33485 — MaterialX upgrade: near-perfect fidelity across all 400+ samples, new open_pbr_surface / gltf_pbr support, archive loading, corrected noise implementations.
  • blender-materialx-importer — Python importer that compiles MaterialX graphs into Blender node graphs, supporting both Cycles and Eevee.
  • Blender PR #158054 — Custom MaterialX noise nodes for Blender (Cycles OSL/GLSL and Eevee): MxNoise, MxFractal, MxCellNoise, MxWorleyNoise, MxUnifiedNoise in both 2D and 3D variants.

Repository Layout

  • packages/core - renderer interfaces and reference-generation orchestration.
  • packages/cli - command line tool for running renders.
  • packages/viewer - TanStack Start website for browsing fidelity images.
  • packages/renderer-* - renderer packages
  • third_party/MaterialX - custom MaterialX branch used by the MaterialXView GLSL/Metal and OSL reference renderers
  • third_party/OpenShadingLanguage - Open Shading Language sources used to provide oslc and testrender for the materialx-osl renderer (build/install instructions: docs/building-openshadinglanguage.md)
  • third_party/blender - patched Blender branch with custom MaterialX nodes used by blender-nodes and blender-eevee-nodes
  • third_party/blender-materialx-importer - standalone Blender MaterialX importer used by the Blender fidelity renderers
  • third_party/three.js - custom Three.js branch used only by the threejs-new renderer

Building Blender and MaterialX

This repository includes step-by-step build instructions for compiling the vendored MaterialX and Blender trees locally (artifacts live under git-ignored build/):

The materialx-glsl and materialx-metal renderers use MaterialXView binaries built from third_party/MaterialX (typically under build/materialx-glsl / build/materialx-metal). materialx-osl uses materialx-osl from third_party/MaterialX and relies on an Open Shading Language install for oslc and testrender (typically third_party/OpenShadingLanguage built into build/osl-dist/ — see the OSL doc). The blender-nodes and blender-eevee-nodes renderers use a custom Blender build from the third_party/blender submodule (see docs/BUILDING_BLENDER.md; the patched app bundle is typically under build/blender/).

Both docs describe redirecting verbose CMake/Ninja output to log files so builds stay reviewable without flooding terminals or LLM sessions.

Requirements

  • Node.js 24+
  • pnpm 10+
  • MaterialX reference executables built under build/materialx-* (see docs/building-materialx.md) or available on PATH; for materialx-osl, also an Open Shading Language install with oslc and testrender (see docs/building-openshadinglanguage.md)
  • Blender 4.0+ available as blender on your PATH or via BLENDER_EXECUTABLE; blender-nodes and blender-eevee-nodes require the patched Blender executable from build/blender (see docs/BUILDING_BLENDER.md) or BLENDER_NODES_EXECUTABLE

Install

# pull in samples, custom renderer dependencies, and add-ons
git submodule update --init --recursive
pnpm install

Build and Validate

pnpm build
pnpm tsc
pnpm lint
pnpm format
pnpm test

pnpm build builds the custom third_party/three.js package before the remaining workspace packages so threejs-new uses a fresh vendored Three.js build.

For native C++ builds (Blender, MaterialX, Open Shading Language), see Building Blender and MaterialX above.

CLI

Generate reference images:

# all renderers, all materials
pnpm cli render
# only generate Three.js renders of open_pbr materials
pnpm cli render --renderers threejs-new --materials open_pbr

This command writes <renderer-name>.png in each directory containing a .mtlx material file.

Calculate visual similarity metrics against each material's materialx-glsl.png reference:

# all renderers, all materials
pnpm cli metrics
# only calculate metrics for selected renderers/materials
pnpm cli metrics --renderers threejs-current,threejs-new --materials open_pbr

This command writes metrics.json in each directory containing a .mtlx material file and a materialx-glsl.png reference. Each file is keyed by renderer name and contains a psnr value.

Currently supported renderers:

  • materialx-glsl (@material-fidelity/renderer-materialxview, MaterialXView OpenGL/GLSL)
  • materialx-metal (@material-fidelity/renderer-materialxview, MaterialXView Metal/MSL)
  • materialx-osl (@material-fidelity/renderer-materialxview, MaterialX OSL)
  • blender-new (@material-fidelity/renderer-blender, Blender bundled MaterialX rendered through Cycles)
  • blender-nodes (@material-fidelity/renderer-blender, patched Blender custom MaterialX nodes rendered through Cycles)
  • blender-eevee-nodes (@material-fidelity/renderer-blender, patched Blender custom MaterialX nodes rendered through Eevee)
  • threejs-current (@material-fidelity/renderer-threejs, official npm Three.js MaterialX support)
  • threejs-new (@material-fidelity/renderer-threejs, custom MaterialX support proposal)

Optional flags:

  • --renderers <selector[,selector...]> optional renderer filter; supports repeated flags, comma-separated values, and substring matches such as threejs or blender
  • --materials <selector[,selector...]> optional material filter; matches against each material directory name only (leaf directory), supports repeated flags, comma-separated values, substring matches, and regex selectors (re:... or /.../flags)
  • --concurrency <number> optional render concurrency; defaults to the recommended available parallelism, with a minimum of 1
  • --skip-existing only render renderer/material pairs whose <renderer-name>.png output does not already exist

The metrics command supports the same --renderers, --materials, and --concurrency filters.

Material Organization

Samples are organized by purpose:

  • third_party/material-samples/materials/nodes - canonical per-node tests
  • third_party/material-samples/materials/surfaces/<surface_type> - focused surface-attribute/debug samples grouped by shader family
  • third_party/material-samples/materials/showcase/<surface_type> - complex, transferable showcase materials grouped by shader family

Node Isolation Suite

The node isolation materials currently live under:

  • third_party/material-samples/materials/surfaces/gltf_pbr/node_isolation

Each node gets its own directory and <node-name>.mtlx, with phase planning documented in:

  • third_party/material-samples/materials/surfaces/gltf_pbr/node_isolation/PHASES.md

Validate Node Isolation Materials

# single material (run from repo root)
pnpm --filter @material-viewer/mtlx start check "$PWD/third_party/material-samples/materials/surfaces/gltf_pbr/node_isolation/add/add.mtlx"
# validate all node-isolation materials
python3 - <<'PY'
from pathlib import Path
import subprocess

root = Path.cwd()
files = sorted((root / "third_party/material-samples/materials/surfaces/gltf_pbr/node_isolation").glob("*/*.mtlx"))
for file in files:
    subprocess.run(
        ["pnpm", "--filter", "@material-viewer/mtlx", "start", "check", str(file)],
        check=True,
    )
print(f"Validated {len(files)} materials.")
PY

Regenerate Node Isolation References

# all node-isolation materials
pnpm cli render --materials node_isolation
# targeted node subset by regex on leaf directory names
pnpm cli render --materials "re:(image|tiledimage|transformmatrix)$"

Surface Input Coverage

Surface samples now follow an input-driven naming convention:

  • input_<primary_input> for single-input isolates
  • graph_<target_input>_<source> for graph-driven isolates
  • showcase_<stack_or_look> for intentionally multi-feature examples

Coverage and rename artifacts:

  • docs/surface-input-coverage-baseline.json
  • docs/surface-input-coverage-baseline.md
  • docs/surface-sample-rename-map.md

Reference Renderer Setup

To keep reference renders visually comparable between materialx-glsl, materialx-metal, threejs-new, and threejs-current, these renderers should follow this framing setup:

  • camera: perspective, FOV 45, near 0.05, eye (0,0,5), look target (0,0,0)
  • model normalization: center the loaded ShaderBall.glb at the origin, then scale it so the bounding-box sphere radius is 2.0 (matching MaterialXView's IDEAL_MESH_SPHERE_RADIUS)
  • lighting for capture: IBL from san_giuseppe_bridge_2k.hdr, environment background enabled, direct light disabled, shadow map disabled
  • environment orientation parity: apply a Y rotation offset of -90 degrees in the Three.js viewer (scene.environmentRotation.y) to match MaterialXView lighting orientation
  • color/output: no tone mapping, sRGB output encoding
  • visible background comes from the active environment HDR (san_giuseppe_bridge_2k.hdr)
  • fixed resolution of 512x512

These values are intentionally aligned with MaterialXView defaults and its scene normalization behavior in source/MaterialXView/Viewer.cpp. threejs-new resolves Three.js from the custom third_party/three.js submodule, including both the core WebGPU/TSL build and examples/jsm/loaders/MaterialXLoader.js; threejs-current continues to use the npm-installed three package. The materialx-glsl and materialx-metal reference renderers prefer MaterialXView binaries built from third_party/MaterialX (under build/materialx-*). materialx-osl uses the materialx-osl executable from third_party/MaterialX and, at run time, the Open Shading Language toolchain (oslc, testrender) from an install such as build/osl-dist/ (see docs/building-openshadinglanguage.md). The Blender renderers follow the same scene contract through background Python scripts. blender-new uses the third_party/blender-materialx-importer submodule with Blender's bundled MaterialX; blender-nodes and blender-eevee-nodes use the same importer but run against the custom Blender build from the third_party/blender submodule (patched MaterialX nodes).

The importer is intentionally maintained as a separate project. This repository keeps the shader-ball setup, render orchestration, image outputs, metrics, and viewer used to validate its Cycles and Eevee fidelity.

Viewer

Run the MaterialX Fidelity Viewer:

pnpm viewer

The viewer scans MaterialX materials and looks for images for the built-in renderer list (materialx-glsl, materialx-metal, materialx-osl, blender-new, blender-nodes, blender-eevee-nodes, threejs-current, threejs-new).

The page groups materials by purpose/type (showcase, nodes, open_pbr_surface, gltf_pbr, standard_surface) and displays each renderer image (<renderer>.png) side by side. Missing images render as a placeholder tile. When the URL does not include a renderers filter, the viewer defaults to showing materialx-glsl, materialx-metal, materialx-osl, blender-nodes, blender-eevee-nodes, and threejs-new; users can enable the other built-in renderers from the renderer filter UI. If a material directory contains metrics.json, the viewer displays each renderer's PSNR beneath its image. Material rows render lightweight placeholders until they are near the viewport, then load the image tiles, render reports, and metrics for smoother browsing.

License

MIT. See LICENSE.

About

MaterialX Fidelity Testing: MaterialXViewer, ThreeJS, Blender, etc.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors