So this is not a code bug to patch; it is an out-of-distribution material-decomposition failure. The lead explanation is a domain gap we measured a month ago and never closed: every conditioning render in training is flat-lit (no specular, no environment reflection), while every real photograph is not.
The deployed demo (image → SS 64³ → shape SLAT-512 → tex SLAT-512 → GLB) produced results that read as wet plastic / polished chrome. Reading the material of one generated GLB:
| channel | value | what it means |
|---|---|---|
| metallic (glTF MR texture, B) | mean 0.996, p10 0.988 | the entire object is treated as metal |
| roughness (G) | 0.394 | semi-glossy |
| baseColor | (0.43, 0.48, 0.52) | grey-blue |
metallic ≈ 1 with roughness ≈ 0.4 under an environment map is exactly the "oily film" look: every surface mirrors the environment.
Rather than guess, each stage was measured against ground truth on real assets.
| # | layer | measurement | metallic | verdict |
|---|---|---|---|---|
| 1 | Training data (GT pbr_latent_512 decoded, 6.0M voxels, 5 assets) | decode GT latents through the deployed tex decoder | 0.016 (p50 0.000, only 1.5% of voxels > 0.5) | ✅ clean — GT is non-metallic, roughness 0.906 |
| 2 | Export path (build_mw → to_glb → glTF) | feed GT latents through the exact demo export, read the GLB back | 0.002 (roughness 0.929) | ✅ correct — no channel swap, no MR mix-up |
| 3 | Tex flow (s3_tex_40k/ckpt-37000, teacher-forced GT coords+shape) | sample, unnormalize, decode | 0.000; latent stats mean +0.374 / std 2.534 vs GT +0.314 / 2.830 | ✅ on-distribution and faithful |
| 4 | Full cascade (generated occupancy + generated shape + generated tex → GLB) | instrumented every step | decode 0.000 → attrs 0.000 → GLB B 0.004 | ✅ end-to-end correct on a render input |
Eight assets, teacher-forced (GT coords + GT shape so the geometry stages cannot be blamed), predicted material vs each asset's own GT:
| asset | GT metallic | pred metallic | GT rough | pred rough | Δ metallic |
|---|---|---|---|---|---|
| 418d858d | 0.000 | 0.000 | 0.921 | 1.000 | −0.000 |
| 3f45107c | 0.000 | 0.003 | 0.993 | 0.668 | +0.003 |
| ed751084 | 0.000 | 0.033 | 0.980 | 0.752 | +0.033 |
| 68dd18f3 | 0.084 | 0.099 | 0.683 | 0.918 | +0.015 |
| 38a5b1fb | 0.016 | 0.437 | 0.866 | 0.617 | +0.421 |
| b9812039 | 0.001 | 0.004 | 0.360 | 0.342 | +0.003 |
| 511bde43 | 0.009 | 0.019 | 0.943 | 0.861 | +0.009 |
| 03dc0b5a | 0.018 | 0.025 | 0.591 | 0.519 | +0.007 |
| mean GT 0.016 · mean pred 0.078 · bias +0.061 · only 1/8 exceeds GT+0.1 | |||||
Seven of eight assets are faithful to within 0.03. One asset (38a5b1fb) flips to 0.437 — so the failure mode exists in-distribution but is rare. Note the roughness column is noisier than metallic (e.g. 0.993 → 0.668), i.e. the model is less certain about gloss than about metalness.
The intuitive explanation — real photos carry baked-in lighting and specular highlights, which get misread as metal — is exactly the failure Seed3D 2.0 names ("specular highlights on non-metal surfaces misclassified as metallic responses"). We tested it directly: one training render, one property perturbed at a time, same seed, teacher-forced.
| variant | metallic | roughness | albedo |
|---|---|---|---|
| 0 · original render | 0.000 | 1.000 | 0.394 |
| 1 · exposure ×1.5 | 0.000 | 1.000 | 0.428 |
| 2 · exposure ×2.0 | 0.000 | 1.000 | 0.354 |
| 3 · added specular highlight | 0.000 | 0.715 | 0.565 |
| 4 · contrast ×1.6 | 0.000 | 1.000 | 0.413 |
| 5 · saturation ×1.8 | 0.000 | 1.000 | 0.374 |
| 6 · saturation ×0.3 | 0.000 | 1.000 | 0.405 |
Grouping every GLB the demo has produced by input type:
| input | metallic (GLB, B channel) |
|---|---|
| training-style renders (examples, 3 runs) | 0.288 / 0.012 / 0.004 |
| controlled end-to-end on a render (this audit) | 0.004 |
| real photographs (7 user uploads) | 0.514 / 0.530 / 0.623 / 0.488 / 0.687 / 0.516 / 0.996 |
The split is clean and it is not a code-version effect: one of the high-metallic runs predates the most recent demo rebuild, i.e. the same binary produces ≈0.00 on renders and ≈0.5–1.0 on photos.
Reading the three current systems, material realism is a main axis of competition, and one of them states our exact failure mode and its fix.
| system | relevant technique |
|---|---|
| Seed3D 2.0 (ByteDance, 2026-04) | Replaces 1.0's cascaded RGB→material with a unified PBR model emitting multi-view albedo + metallic-roughness directly (MMDiT two-stream + MoE), and adds VLM-generated descriptions of material type and surface characteristics as conditioning tokens injected into the DiT blocks, explicitly to fix "specular highlights on non-metal surfaces misclassified as metallic". Geometry stays a separate coarse-to-fine stage (VecSet latents → Dual Marching Cubes up to 1536³). |
| Tripo P1.0 / H3.1 (GDC 2026) | "Native 3D diffusion" — resolves global structure holistically in a unified probabilistic space rather than sequential token prediction; clean topology, up to 2M polygons. PBR described only as "optimized"; no architecture disclosed. |
| Meshy 6 (2026-01) | Multi-view conditioning (front/side/back) to stop the model hallucinating unseen angles; refined geometry, low-poly mode, multi-colour print export. |
Mining the project history surfaced a measurement from 2026-06-25 that reframes this whole audit. Checking renders_cond against a known-metal asset (an ABO step ladder) showed the conditioning renders carry only soft grey gradients — no sharp specular, no environment reflection. Metal is rendered as matte grey; a captioner calls it "metal" from shape and commonsense, not from material cues. The conclusion recorded at the time was that these renders are unsuitable for material work — and the rig was never changed.
This also explains the asymmetry in Seed3D 2.0's setup: they render training views under random HDRI so highlights and reflections are visible, and then add VLM material tokens to disambiguate what those highlights mean. We have neither half. Two concrete levers follow, in dependency order:
Material decomposition from a single photo is ill-posed — a white highlight can be metal or a lit dielectric, and pixels alone cannot separate them. The disambiguating signal is semantic: knowing the object is a wooden chair fixes metallic ≈ 0. Seed3D 2.0 validates this route with a light touch (a few VLM description tokens). Our architecture conditions on a VLM throughout, so the same idea can go further:
global_material{metalness, reflectivity, transparency}, per-part {material, metalness, roughness, finish}). Correction to an earlier draft of this report: those captions are qualitative and produced from the same flat renders — the GT-grounded variant (decode pbr_latent → k-means material regions → feed measured metallic/roughness to the captioner) was prototyped on 9 assets and never run at scale. So the (material words ↔ real PBR values) supervision does not exist yet; it is a ~2-day 8-GPU pass on top of an already-validated prototype.