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.
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.
- Three.js PR #33485 — MaterialX upgrade: near-perfect fidelity across all 400+ samples, new
open_pbr_surface/gltf_pbrsupport, 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,MxUnifiedNoisein both 2D and 3D variants.
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 packagesthird_party/MaterialX- custom MaterialX branch used by the MaterialXView GLSL/Metal and OSL reference renderersthird_party/OpenShadingLanguage- Open Shading Language sources used to provideoslcandtestrenderfor thematerialx-oslrenderer (build/install instructions:docs/building-openshadinglanguage.md)third_party/blender- patched Blender branch with custom MaterialX nodes used byblender-nodesandblender-eevee-nodesthird_party/blender-materialx-importer- standalone Blender MaterialX importer used by the Blender fidelity renderersthird_party/three.js- custom Three.js branch used only by thethreejs-newrenderer
This repository includes step-by-step build instructions for compiling the vendored MaterialX and Blender trees locally (artifacts live under git-ignored build/):
- docs/building-materialx.md — MaterialXView (GLSL/Metal) and
materialx-osl - docs/building-openshadinglanguage.md — Open Shading Language
oslc/testrenderformaterialx-osl(sources inthird_party/OpenShadingLanguage; install e.g. underbuild/osl-dist/) - docs/BUILDING_BLENDER.md — patched Blender for
blender-nodesandblender-eevee-nodes
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.
- Node.js 24+
- pnpm 10+
- MaterialX reference executables built under
build/materialx-*(seedocs/building-materialx.md) or available onPATH; formaterialx-osl, also an Open Shading Language install withoslcandtestrender(seedocs/building-openshadinglanguage.md) - Blender 4.0+ available as
blenderon yourPATHor viaBLENDER_EXECUTABLE;blender-nodesandblender-eevee-nodesrequire the patched Blender executable frombuild/blender(seedocs/BUILDING_BLENDER.md) orBLENDER_NODES_EXECUTABLE
# pull in samples, custom renderer dependencies, and add-ons
git submodule update --init --recursivepnpm installpnpm build
pnpm tsc
pnpm lint
pnpm format
pnpm testpnpm 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.
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_pbrThis 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_pbrThis 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 asthreejsorblender--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 of1--skip-existingonly render renderer/material pairs whose<renderer-name>.pngoutput does not already exist
The metrics command supports the same --renderers, --materials, and --concurrency filters.
Samples are organized by purpose:
third_party/material-samples/materials/nodes- canonical per-node teststhird_party/material-samples/materials/surfaces/<surface_type>- focused surface-attribute/debug samples grouped by shader familythird_party/material-samples/materials/showcase/<surface_type>- complex, transferable showcase materials grouped by shader family
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
# 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# 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 samples now follow an input-driven naming convention:
input_<primary_input>for single-input isolatesgraph_<target_input>_<source>for graph-driven isolatesshowcase_<stack_or_look>for intentionally multi-feature examples
Coverage and rename artifacts:
docs/surface-input-coverage-baseline.jsondocs/surface-input-coverage-baseline.mddocs/surface-sample-rename-map.md
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, near0.05, eye(0,0,5), look target(0,0,0) - model normalization: center the loaded
ShaderBall.glbat the origin, then scale it so the bounding-box sphere radius is2.0(matchingMaterialXView'sIDEAL_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
-90degrees 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.
Run the MaterialX Fidelity Viewer:
pnpm viewerThe 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.
MIT. See LICENSE.

