Open pjcozzi opened 7 years ago
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.
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.
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.
For 3D Tiles is also a must in my opinion. And in general where bandwith is a priority.
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.
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.
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, https://github.com/KhronosGroup/glTF/issues/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...)
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.
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.
@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 ?
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).
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.
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?
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:
(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...
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.
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. :)
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.
π 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. π
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 :) )
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 )
@AurL That animated Swiss Army Knife from your second link is amazing.
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?
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
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!
@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
.
(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
.
It sounds like @Fabrice3D expected a GLB chunk
for each image. Instead, as @lexaknyazev says, just slam the encoded image bytes into a dedicated BufferView
and point to that. It's not much more work, presumably your exporter already has some infrastructure in place to do similar operations.
thx for the fast reply @lexaknyazev I was afraid It would not be the expected... why do simple when you can do complicated :)
Any specifics props on the buffers and views objects I need to specify? or it is similar to the bin one?
why do simple when you can do complicated
Some people wanted to keep text-based JSON as a separate file while putting all binary data into one blob.
Oh wait I see edits... you mean all the images become one chunk ref, and the buffer views are then defining the offsets just like for the bin data... so its in fact seen as a 3 pieces file, json, bin, and depenciesbytes. I see the attempt to be consistent, but really overkill, again, this may be my import/export strategies..
Also, its not necessary to add padding per image then no?
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.
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.
Really! Huh. My apologies. (Though I think I'll continue to do it in my own code.)
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.
Ok, followed @zellski advise, and added the images to the bin vs add a new buffer. I ran the validator and looks ok.
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" }
@Fabrice3D
Looks like your buffer
has buffer.uri
defined, so GLB binary chunk is unused.
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
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...
Validation report above shows that an asset refers to full.bin
- an external file.
you mean this? where uri should be something else? "buffers": [ { "byteLength": 3949602, "uri": "full.bin" } ]
@Fabrice3D see this section: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#glb-stored-buffer
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...
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?
From 0
, so 0
means the first byte of BIN
chunk.
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.
Probably, that's because you didn't validate .bin
file before. You can drag-n-drop it with .gltf
into the validator.
if I drop the gltf , no errors. The glb file only gives the errors.
Are you dropping both .gltf
and .bin
files (simultaneously)?
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??
If it's helpful, here is a glTF chatroom: https://gitter.im/KhronosGroup/glTF π
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