Skip to content
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

Best practices for .gltf / .glb #1117

Open
pjcozzi opened this issue Oct 11, 2017 · 54 comments
Open

Best practices for .gltf / .glb #1117

pjcozzi opened this issue Oct 11, 2017 · 54 comments
Assignees

Comments

@pjcozzi
Copy link
Member

pjcozzi commented Oct 11, 2017

Documentation for when to export/import .gltf / .glb for different types of apps, e.g., desktop vs. web, consumer vs. developer, etc.

Not 100% sure where this should live but it should be easily discoverable. Perhaps an implementation section in the spec. Or maybe also a glTF Tutorial.

CC @sbtron

@emackey
Copy link
Member

emackey commented Oct 12, 2017

Thanks for starting this thread. Lately I've started to think that end users should generally always prefer .glb over .gltf. There are a few reasons: .glb is a single file, or a single network request, and much smaller than .gltf's base64-encoded "embedded" version. An individual file (or server asset) is easier to copy and transport (should I say transmit?) than multiples, and there's a lot less to worry about with broken relative URIs and such.

That said, a few apps still prefer .gltf: Substance Painter (since the app is primarily concerned with exporting separate textures, not finished models) and my own VSCode extension (since VSCode is a text editor). Are there any others? It seems like the whole rest of the ecosystem is likely to embrace .glb primarily.

It's too late to change now, but .glb isn't the greatest extension name, when you think about it. It doesn't stand for anything on its own (Graphics Library... Binary?) and isn't the name of this repo or the format. Had I figured this out a year ago I would have lobbied for .gtf, Graphics Transmission Format. Still, COLLADA became known as .dae, so, I guess we can keep .glb and it will do fine.

Anyway, regardless of the name, I think the self-contained binary form of glTF is going to win over the ecosystem in the long run.

@xelatihy
Copy link
Contributor

For desktop use and if you are concerned about editing and versioning, .gltf is a must. In fact, we found it to be bext used with one binary blog per mesh, so meshes are like textures. Obviously other apps will likely do it differently.

@emackey
Copy link
Member

emackey commented Oct 12, 2017

Yes, exactly: During creation, editing (and possibly debugging) of the glTF, the .gltf file with separate textures is best, and I hadn't even thought of separate re-usable bin files, that's an interesting twist. The examples I gave (Substance Painter and VSCode) are both on the editing side, which makes sense in this light.

Once the final, deliverable asset is finished though, the self-contained .glb file is best for end users who don't need to edit it.

Certainly we need an easy path for users to convert one to the other.

@jtorresfabra
Copy link

For 3D Tiles is also a must in my opinion. And in general where bandwith is a priority.

@sakrist
Copy link

sakrist commented Oct 12, 2017

I think .gltf more like for web and development stage. And .glb for final model which is for use in desktop and mobile apps. Basically the same as above.

@zellski
Copy link
Contributor

zellski commented Oct 12, 2017

I think the .glb format is clearly superior for any kind of final delivery. There are only upsides, right? It's not as if you give up the ability to externally load additional buffers. You just get one conveniently available as part of the initial payload, without need for secondary HTTP round-trips. You can use it for a little bit of data (maybe enough to render a low-fidelity preview) or for all of it. Most of the time, it's likely to be the latter, because as @emackey suggested up there, the distinction between "here is a single atomic deliverable that you can toss at any loader" and "here's a starting point from which to make further relative URL requests" is pretty huge.

@javagl
Copy link
Contributor

javagl commented Oct 12, 2017

Although I cannot speak for real users, there are several aspect that might be relevant for dicussing the "best practices", and maybe even affect of further development or recommendations.

One of them is the "JSON refer other JSON?" issue, #37 , which has occasionally brought up. Somewhat related to that: I think there also was a discussion about the guidelines for external references in GLB in general, although the question is only relevant for images and buffers now, and not for other assets.

The main point is: A GLB file is not necessarily self-contained. From just having a GLB, you never know whether it contains references to external data or not.

Or should one of the "best practices" be exactly this recommendation - namely, to not have external references in GLB? (That would sound like a considerable restriction of use-cases for me...)

@donmccurdy
Copy link
Contributor

donmccurdy commented Oct 12, 2017

It seems easiest and perhaps most important to suggest a best practice on what should be the default output of consumer applications, e.g. future tools like Sketchfab, Substance Painter, and Minecraft's exporter. For this case, I think a self-contained file is the essential thing, whether it's .gltf or .glb. More likely .glb.

As a recommendation for developer workflows and final delivery, we should probably do some real-world tests before proposing something here. .glb seems preferable in theory, but for delivery on the web there may be some advantage to being able to load images in parallel. There's some interest in three.js to support asynchronous texture request and GPU upload, in which case textures shouldn't block geometry. In a couple very non-scientific tests of the Boombox sample, the metal/rough .glb loaded marginally (~5-15%) slower than than the (non-embedded) .glTF metal/rough and spec/gloss samples.

boombox_profile

@lexaknyazev
Copy link
Member

Servers could use transport-level compression on pure JSON .gltf files. It may be less efficient with .glb.

We may also evaluate extending GLB with LZ (brotli?) compression of JSON chunk.

@cg-cnu
Copy link

cg-cnu commented Oct 13, 2017

@donmccurdy Valid points there. For developer/artist sanity a single .glb make sense but when it comes to delivering to the end user I don't see the single network request provided by .glb as a huge advantage, especially with the upcoming http2 protocol.

Wondering how does texture re-usability works 🤔 If i have two models using the same texture, exporting the models as a separate .glb files includes the texture in both the .glb files ?

@lexaknyazev
Copy link
Member

If i have two models using the same texture, exporting the models as a separate .glb files includes the texture in both the .glb files ?

Yes, unless common texture is stored separately (as an image file or as a part of shared binary buffer).

@jbouny
Copy link

jbouny commented Oct 13, 2017

In Substance Painter, as said, we are currently only exporting a .gltf with an external buffer and png files (I am one of the developer of this software). It is not really a spoiler, but it is just our first step to support gltf ecosystem.

Even if we are only exporting static objects, I think being able to directly export an 'engine ready' asset as a .glb self-contained file is also important to provide to our users.
With gltf, there are many export options, which is great as they all can have their benefits (network issues, re-edition, reuse, etc.). I am personally seduced by the asynchronous texture request possibility in http2 (as example: glb + external textures).

We should soon expose full control as .gltf/.glb and external/contained textures; but it could indeed be dependent of the format evolution and usage.

@zellski
Copy link
Contributor

zellski commented Oct 13, 2017

Pardon the partial digression, but it seems to me that the importance of exporting on just the right format would diminish significantly if there were a dependable, free, swiss-army-knife type utility that could convert omnidirectionally between format variations. It could be npm-installed locally for lightning fast execution, or in the cloud (for tiny amounts of money) for convenience. The most obvious candidate would seem to be https://github.com/AnalyticalGraphicsInc/gltf-pipeline, but I'm not sure a web-based GUI is on their roadmap? Anyone else?

@javagl
Copy link
Contributor

javagl commented Oct 14, 2017

A big +1 @zellski . Af course, such a tool should definitely exist. But still, the questions about file sizes, number of requests etc. are still relevant, and the answers not obvious. There should probably be an overview about the pros/cons of each representation for each application case.


Somehow off-topic, regarding the conversion tool:

One important question regarding this conversion utility is the one that you already referred to, namely the form of delivery and availability. I think some sort of standalone executable would be nice, to be used in at the command line or for batch processing. But it could also expose the command-line functionality via a trivial interface so that it could be used as a library.

A broader question would be about the form or feature set of this tool. I think that there may be different levels of granularity to answer the question of what should be exported how:

  1. Just convert a whole glTF+BIN+Images into a GLB
  2. Convert a glTF+BIN into a GLB, but keep the images as external references
  3. Convert a glTF+BIN+Images into a GLB, but keep some specific (user-selected) images as external references

(just examples, different combinations may be possible). Some of these conversion options are already covered in the gltf-pipeline, but others are not yet direclty supported (by any tool, as far as I know). The most fine-grained export options might look a bit complicated, though...

exporting

I already considered to wrap a small GUI like this around some of the https://github.com/javagl/JglTF libraries, and will probably give it a try when I have some more time. But the gltf-pipeline would be the better candidate for an official solution, due to its broader community support.

@donmccurdy
Copy link
Contributor

donmccurdy commented Oct 14, 2017

Once glTF-pipeline fully supports glTF 2.0 (progress), it should be very possible to make a drag-and-drop web-based tool, or an Electron desktop GUI, for doing the conversions. Could be very handy. :)

@javagl
Copy link
Contributor

javagl commented Oct 15, 2017

Just a short cross-reference that I just stumbled over - the Best Practices section of the pbrSpecularGlossiness Extension :

To get the best of both worlds a glTF asset can include both metallic-roughness and specular-glossiness in a single glTF asset. [ ... ] Since such an approach requires including both material models it is best suited for a web scenario where a client can choose to download the appropriate material model from a server hosting the glTF asset.

We should be careful to not make toooo contradicting statements here: Including both in a GLB may cause it to become prohibitively large.

@emackey
Copy link
Member

emackey commented Oct 28, 2017

🎉 UPDATE 🎉

The VSCode extension got an external contribution in AnalyticalGraphicsInc/gltf-vscode#35, and now includes a command to export the glTF file you're editing to GLB format. Currently, this export includes an implicit bundling of all external references to embedded GLB chunks, which I think is not unreasonable for most use cases. I'm exited about this one if you can't tell. 😃

@sbtron
Copy link
Contributor

sbtron commented Nov 6, 2017

I wanted to give a big +1 to @emackey 's comment on “end users” (not developers or technical artists) only seeing a self-contained .glb file especially when interacting with it as a file on disk.
We are seeing feedback from end users being confused by the .gltf and associated files. Specifically there have been many instances of trying to share a .gltf json file over email or cloud share without the associated geometry or textures resulting in the recipient not being able to view the asset.

I would strongly encourage any tool or service exporting .gltf also provide a self-contained .glb export option. It will be lot easier for end users to learn to view and share a single .glb file.

If your tool is already writing out a .gltf it should be quite straightforward to package that out into a self- contained glb. The .glb export by @najadojo for VSCode extension is a very good starting point if you are looking for examples on how this can be done- https://github.com/AnalyticalGraphicsInc/gltf-vscode/blob/master/src/exportProvider.ts
(Hint @jbouny , @AurL :) )

@AurL
Copy link

AurL commented Nov 7, 2017

On our side, it will obviously make sense to export .glb instead of the classic .gltf+ dependencies. It was more convenient for us to keep a human readable version to easily debug and spot any issue, and it will probably be the case while we include glTF more and more in our pipelines, but once we have something stable, we will obviously switch to the binary packed version everywhere.
(BTW, we recently added animation in our glTF export , or here )

@emackey
Copy link
Member

emackey commented Nov 7, 2017

@AurL That animated Swiss Army Knife from your second link is amazing.

@sbtron
Copy link
Contributor

sbtron commented Nov 7, 2017

It was more convenient for us to keep a human readable version to easily debug and spot any issue

Good point we need more debugging tools around glb :). The recent vscode extension update is quite useful in breaking apart a glb for editing/debugging. Curious to know if there are any other requirements?

@AurL
Copy link

AurL commented Nov 8, 2017

Nice!
This tool is great (thanks @emackey for this btw) and I guess it now answers all the requirement, I don't have any other reason in mind to keep the current .gltf layout, I'll make a note.
Thanks

Update: I hope the VS Code 5MB size limitation will be configurable soon, since it's not possible to inspect most of the .glb here

@Fabrice3D
Copy link

Back to the gltf fun after few months client work. I've started work on the .glb exports.
So far so goed, bin+json are in and validator says he's happy.

Now, next is to embed the images... on the doc I could find, for images embed, its a bit unclear how I need to define the pointer that replaces the previous "uri" to the alternative that points to the images chunks...

Me naive, I was expecting something logic and simple like {chunk:2, mimeType:"image/jpeg"}
as I do know from json its an image, and that its being stored, I even expected
it would keep the uri value, so I could even save locally the image using the same name/path.
This also would prevent to have multiple exporters/variations, as @javagl commented on oct 14 to select the export type. Basically I was expecting same json, only the addition of the integer to pick the right image to read it. As its parsed from glb extension, you know in image that there would be either uri or chunk integer...

Short story, as its not the expected to me, could someone tell me how do I replace the uri to point to the chunk and/or a ref where this is described? thx!

@lexaknyazev
Copy link
Member

@Fabrice3D
There's a bit more indirection with storing images in GLB. For each image an asset needs to have a bufferView object. These views will point to some buffer object. This allows flexible packing of data — you can use one .bin file for geometry and the other .bin file for all images.

BIN chunk from GLB is represented by buffer object with undefined uri.

@lexaknyazev
Copy link
Member

(Cont.)
Images accessed via bufferViews have image.bufferView instead of image.uri.

For one-file delivery, all bufferViews (both with geometry and with image data) must point to the same buffer.

@Fabrice3D
Copy link

Also, its not necessary to add padding per image then no?

@zellski
Copy link
Contributor

zellski commented Mar 6, 2018

I think you're confusing some interrelated concepts (while criticising them, which is a weird). You can use one or more buffers, although if you are building a GLB you are best off sticking with the "freebie" one you get from there. I would stop talking about 'chunks' entirely, as that's specific to GLB creation and you will almost always just have two: JSON and the binary one.

For GLB creation you typically just slam everything into your one buffer. It's a complete jumble of data. That's why there are BufferViews. They are just views into the Buffer jumble that start at a given offset and have a certain length. Once you've copied your binary image data into the main Buffer one, and configured your new BufferView to point at the right position, you're good to go -- just reference the view it much as you yourself suggested above, except not with chunks -- more something like {bufferView: 3, mimeType: "image/jpeg"}.

All BufferViews need to be 4-byte aligned within the Buffer, yes.

@lexaknyazev
Copy link
Member

All BufferViews need to be 4-byte aligned within the Buffer, yes.

Not necessarily. If one puts all images after vertex buffers then each image don't have to be aligned.

@zellski
Copy link
Contributor

zellski commented Mar 6, 2018

Really! Huh. My apologies. (Though I think I'll continue to do it in my own code.)

@Fabrice3D
Copy link

Its not a critic, as said, I see the wish to be consistent. As I followed the doc to add each images, I was expecting the strategy to first isolate the json, bin and "images"/rest, as they are stored in logical order, when I load a glb, I have these "images" already separated, simply because you must first extract the json and the bin, so continuing to isolate each parts is logic to me. From this logic, I do no need again to look at buffer views, to get the offset and extract the data, I have them already. In fact, while the exporter will define this, my parser would totally skip that part.

@Fabrice3D
Copy link

Ok, followed @zellski advise, and added the images to the bin vs add a new buffer.
I ran the validator and looks ok.
screen shot 2018-03-07 at 16 35 35

As I've added the "injection" of the images bytes to the exporter routine. I wonder if need to keep the type and componenType.
{
"name": "bytes_img_4",
"bufferView": 28,
"componentType": 5120,
"count": 170698,
"type": "SCALAR"
}

@lexaknyazev
Copy link
Member

@Fabrice3D
Looks like your buffer has buffer.uri defined, so GLB binary chunk is unused.

@lexaknyazev
Copy link
Member

Also, you don't need to create accessors for images, since image.bufferView refers to bufferViews directly.
Btw, most of glTF sample models have GLB versions, e.g.,
https://github.com/KhronosGroup/glTF-Sample-Models/blob/master/2.0/BarramundiFish/glTF-Binary/BarramundiFish.glb

@Fabrice3D
Copy link

As usual thx for the fast reply @lexaknyazev !
You lost me a bit. Can you explain: "so GLB binary chunk is unused". Meanwhile will go study the link...

@lexaknyazev
Copy link
Member

Validation report above shows that an asset refers to full.bin - an external file.

@Fabrice3D
Copy link

you mean this? where uri should be something else?
"buffers": [ {
"byteLength": 3949602,
"uri": "full.bin"
} ]

@lexaknyazev
Copy link
Member

@Fabrice3D
Copy link

ah ok... now validator gives me this:
{
"pointer": "/buffers/0",
"mimeType": "application/gltf-buffer",
"storage": "glb",
"byteLength": 3949604
},
{
"pointer": "/images/0",
"mimeType": "image/jpeg",
"storage": "bufferView",
"image": {
"width": 2048,
"height": 2048,
"format": "RGB",
"bits": 8
}
}, + more images

now the "image.bufferView refers to bufferViews directly." part...

@Fabrice3D
Copy link

Just to be sure, when parsing the glb, the bufferViews.byteOffset are expected to be counted from 0 as in external .bin case or from 12 bytes header + (0x4E4F534A ) json.length?

@lexaknyazev
Copy link
Member

lexaknyazev commented Mar 7, 2018

From 0, so 0 means the first byte of BIN chunk.

@Fabrice3D
Copy link

ok thank you, so encoding both gltf and glb at same time, I do not need to offset their values for the glb version. I have a ACCESSOR_NON_UNIT error that I try figure out... all fine in regular gltf export.

@lexaknyazev
Copy link
Member

Probably, that's because you didn't validate .bin file before. You can drag-n-drop it with .gltf into the validator.

@Fabrice3D
Copy link

if I drop the gltf , no errors. The glb file only gives the errors.

@lexaknyazev
Copy link
Member

Are you dropping both .gltf and .bin files (simultaneously)?

@Fabrice3D
Copy link

ah a secret feature! :) I do get the same error now. I can load the gltf file in my app and just tried loading it with threejs, no prob either. All displays fine. What can it be??

@donmccurdy
Copy link
Contributor

If it's helpful, here is a glTF chatroom: https://gitter.im/KhronosGroup/glTF 😅

@Fabrice3D
Copy link

thx @donmccurdy. joined.

@Fabrice3D
Copy link

@lexaknyazev, was caused by length != 1 of normals. Added a routine to check them, all fine now. thx!

@MaxmaxmaximusAWS
Copy link

MaxmaxmaximusAWS commented Oct 7, 2020

we in the company use gltf + assets + webpack loader to CDN. webpack parse gltf and get links to assets. upload it to CDN and add hash to name. then we add presistent cache for this assets. if we change one small thing in our scene. all assets load from browser cache, cuz name change in only in one asset. we also use http2, and loading multiple files in parallel speeds up overall scene loading. 2020 year =)

@donmccurdy
Copy link
Contributor

I haven't been able to come up with any concrete recommendation here except that "glTF Embedded" (with Data URIs) should be avoided. Both "glTF Separate" and GLB work well, and different users seem to have fairly different preferences about the performance and ergonomics of each, e.g. for sharing textures across multiple files.

I'd like to see us more aggressively trying to discourage "glTF Embedded" (e.g. remove its output from Blender and glTF-Pipeline?), but other than that I don't think there's a clear need for more messaging.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests