Skip to content

feat(gltf): material features Tier 2 — texture filter, unlit, alpha cutout, emissive#1506

Open
obiot wants to merge 5 commits into
masterfrom
feat/gltf-materials
Open

feat(gltf): material features Tier 2 — texture filter, unlit, alpha cutout, emissive#1506
obiot wants to merge 5 commits into
masterfrom
feat/gltf-materials

Conversation

@obiot

@obiot obiot commented Jun 20, 2026

Copy link
Copy Markdown
Member

glTF / OBJ material features (Tier 2)

Builds on the Tier‑1 glTF/GLB loader (#1504) with the four day‑1 material features an indie dev expects from a 3D loader, even at melonJS scope. Each is a generic Mesh setting the loaders drive — usable directly from raw‑geometry meshes too, not just glTF.

Added

  • Mesh.textureFilter + glTF sampler filtering"nearest" / "linear" magnification, read from each material's sampler magFilter, so pixel‑art‑textured 3D models render crisp instead of blurred by the global antiAlias default. Omitted → keeps the engine default.
  • KHR_materials_unlit — materials that bake their own lighting render fullbright (not shaded again) even in a lit scene, avoiding double‑lighting. Per‑primitive.
  • Mesh.alphaCutoff + glTF alpha cutout — a hard cutout: fragments below the threshold are discarded in the mesh shader, for crisp foliage / fences / chain‑link / decals with no blending or sorting. Set from a material's alphaMode: "MASK" (alphaCutoff, default 0.5); OPAQUE / BLEND unaffected. 0 (default) disables.
  • Mesh.emissive + glTF/OBJ emissive — a self‑illumination color added on top of the lit/unlit result so a surface glows regardless of scene lights (neon, lava, screens, glowing eyes). From glTF emissiveFactorKHR_materials_emissive_strength for HDR glow) and MTL Ke. Emissive textures (emissiveTexture / map_Ke) remain out of scope.

Implementation notes

  • Both new uniforms (uAlphaCutoff, uEmissive) follow the existing uSampler pattern in MeshBatcher.addMesh: set flush‑free per mesh, only when the value changes, and only when the bound shader actually declares the uniform — so custom mesh shaders are untouched. Each drawMesh flushes individually, so no cross‑mesh batch is broken.
  • Emissive is added after lighting in the lit shader so it glows at full strength; the no‑emission path stays lean (undefined, shared zero vector).

Tests

  • glTF parser: unlit (3), filter (5), alpha cutout (6), emissive (5); MTL Ke (1); Mesh consumer wiring (1).
  • WebGL end‑to‑end pixel tests in webgl_mesh_depth.spec.js: alpha cutout discard/keep/zero‑cutoff (3) and emissive glow + uniform‑reset‑doesn't‑leak (2) — these also prove both shaders still compile with the new uniforms.
  • Full suite: 4265 passed / 15 skipped / 0 failed.

🤖 Generated with Claude Code

obiot and others added 5 commits June 20, 2026 11:23
Materials flagged KHR_materials_unlit bake their own lighting and must not
be shaded again. The parser detects the extension per material; GLTFScene /
GLTFModel set `lit = sceneLit && !unlit` so an unlit prim renders fullbright
even in a lit scene (no double-lighting).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…support)

New `Mesh` `textureFilter` setting ("nearest" / "linear", WebGL) applied to the
resolved texture. The glTF loader reads each material's sampler `magFilter`, so
pixel-art-textured 3D models render crisp instead of blurred by the global
antiAlias default. Same image-global caveat as `textureRepeat` (#1503).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Hard alpha cutout for the WebGL mesh path: fragments whose final alpha
falls below a per-mesh threshold are discarded in the mesh / lit-mesh
shaders, for crisp foliage / fences / chain-link / decals with no
blending or back-to-front sorting.

- mesh.frag / mesh-lit.frag: new uAlphaCutoff uniform + discard guard
- MeshBatcher.addMesh: set the uniform (flush-free, per-mesh) when it
  changes, guarded behind the shader actually declaring it so custom
  mesh shaders are untouched
- Mesh.alphaCutoff setting (default 0 = disabled)
- glTF parser materialAlphaCutoff: alphaMode MASK -> alphaCutoff (def
  0.5); OPAQUE/BLEND -> 0. Wired through GLTFScene + GLTFModel
- tests: parser (6) + Mesh consumer (1) + WebGL end-to-end pixel (3,
  proving both shaders compile with the uniform and the discard works)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Self-illumination for the WebGL mesh path: a color added on top of the
lit/unlit result so a surface glows regardless of scene lights (neon,
lava, screens, glowing eyes).

- mesh.frag / mesh-lit.frag: new uEmissive vec3 uniform, added to the
  final color (after lighting in the lit path so it glows full strength)
- MeshBatcher.addMesh: set uEmissive flush-free per mesh with a per-
  channel redundant-set guard, behind the shader declaring the uniform;
  shared zero vector when a mesh has no emission
- Mesh.emissive (Float32Array(3) or undefined) + toEmissive() helper
  (all-zero collapses to undefined → lean path)
- glTF parser materialEmissive: emissiveFactor x
  KHR_materials_emissive_strength; wired through GLTFScene + GLTFModel
- MTL parser: Ke (Blender emission export); wired in Mesh single-
  material path
- tests: glTF parser (5) + MTL Ke (1) + WebGL end-to-end pixel (2,
  incl. uniform-reset-doesn't-leak)

Emissive textures (emissiveTexture / map_Ke) remain out of scope.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…emissive) to JSDoc

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant