-
Notifications
You must be signed in to change notification settings - Fork 1.2k
The KHR_materials_variants extension #1681
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 12 commits
b529179
ebe3189
9033116
ee0d8ad
b7c68e3
fd070c3
92771ff
ca97835
69b7edf
8e5d8b1
f0ab429
a9bee21
23915e9
07c109b
46b16fc
17fe9bd
910746f
b84ae8a
fc9b8d9
60be860
09a22ce
26d2629
8543426
9bc4e4e
9049ea8
8af11ea
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,98 @@ | ||
| # FB_material_variants | ||
|
|
||
| ## Contributors | ||
|
|
||
| - Pär Winzell, Facebook, [@zellski](https://twitter.com/zellski) | ||
| - Renee Rashid, Facebook, [@debuggrl](https://github.com/debuggrl) | ||
|
|
||
| ## Status | ||
|
|
||
| Draft | ||
|
|
||
| ## Dependencies | ||
|
|
||
| Written against the glTF 2.0 spec. | ||
|
|
||
| ## Overview | ||
|
|
||
| This extension allows for a compact glTF representation of multiple material variants of an asset, structured to allow low-latency switching at runtime. | ||
|
|
||
| A typical use case is digital commerce, where a user might be presented with e.g. a pair of sneakers and the ability to switch between different colours. | ||
|
|
||
| ## Tagged Variants | ||
|
|
||
| We introduce a simple tag-based extension scheme, that allows for high-level runtime swapping between which `material` is used to shade a given `mesh primitive`: the extension root contains a mandatory `mapping` property, which is an array of objects, each one associating some set of tags with a material reference. | ||
|
|
||
| Imagine a sneaker with shoelace holes that are made from materials that depend on the overall shoe colour in non-obvious ways: | ||
|
|
||
| | Tags | Material | | ||
| | ---------------------------------- | ------------------------------- | | ||
| | `sneaker_yellow`, `sneaker_orange` | `shoelace_hole_material_brown` | | ||
| | `sneaker_red` | `shoelace_hole_material_purple` | | ||
| | `sneaker_black` | `shoelace_hole_material_yellow` | | ||
|
|
||
| (_the authors of this spec are not product designer, apologies for the dubious colour choices_) | ||
|
||
|
|
||
| The currently active `material` for a `mesh primitive` is found by stepping through this array of mappings, and selecting the first one which contains any one of the currently active tag. If none match, fall back on vanilla glTF behaviour. | ||
|
||
|
|
||
| In other words, **this is not a literal mapping** in its glTF form – exporters, take note. | ||
|
|
||
| ## The variant mapping as glTF JSON | ||
|
|
||
| A snippet of a mesh implementing shoe holes which uses this extension might look like: | ||
|
|
||
| ```javascript | ||
| "meshes": [ | ||
| { | ||
| "name": "shoelace_hole", | ||
| "primitives": [ | ||
| { | ||
| "attributes": { | ||
| "POSITION": 0, | ||
| "NORMAL": 1, | ||
| }, | ||
| "indices": 2, | ||
| "material": 0, | ||
| "extensions": { | ||
| "FB_material_variants" : { | ||
| "mapping": [ | ||
| { | ||
| "tags": [ "sneaker_yellow", "sneaker_orange" ], | ||
| "material": 7, | ||
|
||
| }, | ||
| { | ||
| "tags": [ "sneaker_red" ], | ||
| "material": 8, | ||
| }, | ||
| { | ||
| "tags": [ "sneaker_black" ], | ||
| "material": 9, | ||
| }, | ||
| ], | ||
| } | ||
| } | ||
| }, | ||
| // ... more primitives ... | ||
| ] | ||
| }, | ||
| // ... more meshes ... | ||
| ] | ||
| ``` | ||
|
|
||
| The tag-based approach allows highly selective material switching, e.g. changing the colour of shoe laces only, or wholesale changes across diverse geometry, by reusing the same tags in switches across many distinct mesh primitives. | ||
|
|
||
| In the typical case, many tags will be active, e.g. `['sneaker_red', 'laces_gold']`. | ||
|
|
||
| Composition also works well with tags: if a scene were built from several different variational models, each with their own set of tags, then it's easy to imagine an API call on the entire scene that takes a set of tags and passes them on to each constituent model, recursively. | ||
|
|
||
| ## Interaction with existing glTF functionality | ||
|
||
|
|
||
| The primary purpose of this extension is to simply formalise the idea of configurability, so that members of the ecosystem can meaningfully communicate: those who produce asset and those who ingest them, and all the tooling inbetween. | ||
|
|
||
| Beyond that, it's also a compression scheme: a way to compactly represent multiple assets that have a lot in common (e.g. most geometry, some textures). In this respect, it's most meaningful for the self-contained binary format GLB, where the asset file contains all its data up-front, both that which is shared between variants and that which differs. | ||
|
|
||
| If we also allow external references, i.e. images and buffers to source from URLs,then we can additionally come up with sophisticated mechanisms where geometry and textures are loaded separately, dynamically. There's pragmatic pros and cons to going down this route, all beyond the scope of this document. Each application will make its own decisions there; meawhile, the extension proposed works well regardless. | ||
|
||
|
|
||
| ## Implications for Applications and APIs | ||
|
|
||
| How does an application communicate to a glTF engine what the initial variant state should be? How does it submit a runtime request for a different configuration? It's out of scope for this extension to constrain or mandate an engine's public API, but a useful implementation will require something of the sort. | ||
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| { | ||
| "$schema": "http://json-schema.org/draft-04/schema", | ||
| "title": "FB_material_variants mesh primitive extension", | ||
| "type": "object", | ||
| "allOf": [ | ||
| { | ||
| "$ref": "glTFProperty.schema.json" | ||
| } | ||
| ], | ||
| "properties": { | ||
| "tags": { | ||
| "type": "array", | ||
| "description": "An array of variational tags.", | ||
| "items": { | ||
| "$ref": "glTFid.schema.json" | ||
| }, | ||
| "minItems": 1, | ||
| "gltf_detailedDescription": "An array of variational tags, all of which map to the associated material." | ||
| }, | ||
| "material": { | ||
| "allOf": [{ "$ref": "glTFid.schema.json" }], | ||
| "description": "The mapped material.", | ||
| "gltf_detailedDescription": "A reference to the material associated with the given array of tags." | ||
| }, | ||
| "extensions": {}, | ||
| "extras": {} | ||
| }, | ||
| "required": ["tags", "material"] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| { | ||
| "$schema": "http://json-schema.org/draft-04/schema", | ||
| "title": "FB_material_variants mesh primitive extension", | ||
| "type": "object", | ||
| "allOf": [ | ||
| { | ||
| "$ref": "glTFProperty.schema.json" | ||
| } | ||
| ], | ||
| "properties": { | ||
| "mapping": { | ||
| "type": "array", | ||
| "description": "An array of mapping entries.", | ||
| "items": { | ||
| "$ref": "mapping.schema.json" | ||
| }, | ||
| "minItems": 0, | ||
| "gltf_detailedDescription": "An array of mapping entries, each associating a set of tags with a material." | ||
| }, | ||
| "extensions": {}, | ||
| "extras": {} | ||
| }, | ||
| "required": ["mapping"] | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Although in my customization work (athletic apparel and sports equipment) we are currently adding customization as a layer of logic around the model, I believe we should be able to replace a lot of the manipulation logic already with this declarative approach. Will try that out and report on the findings. But I have added some immediate conclusions below.