Search code examples
vulkangltf

Handling GLTF nodes with multiple vertex formats


I'm working on implementing support for GLTF 2.0 in a hobby renderer, using Vulkan and C++. https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html

Initially, I implemented basic support for some test GLTF files. However, after reading the spec closer, I've come across some details that are tough to deal with.

Per the spec, a Node consists of one or more Meshes, and a Mesh consists of one or more Primitives. Within the same Node, each Primitive may use different vertex formats, including:

  • Different amount of attributes (ie: Position-only vs Position+Normal)
  • Different attribute ordering (ie: Position+Normal vs Normal+Position).
  • Accessors that points to arrays of structs vs others that use arrays of elements.

It seems to me that this creates a situation which, in order to make a robust renderer that can handle general GLTF models, one needs to create a bunch of shader permutations the various vertex formats that a Primitive may use. So far, the only idea that has occurred to me on how to avoid an explosion of shader permutations is the following:

  • Define a shader with a vertex format that includes all the spec-defined attributes. When parsing a GLTF file, use default values for any missing attributes. When reading the vertex data from the file, if an attribute is present but with a different data type, convert that data to the expected type (ie: promote short to ints). Reorder the attributes to what the shader expects. Additionally, have a few other specialized shaders for when some well known vertex formats are detected (ie: if format is Position+Normal+Coord0, use shader Foo).

I tried a variation on the above, and loading models was very slow. Also, seems like a brittle way to approach this.

What is another way that I can mitigate the need to, potentially, make alot of pipeline changes for a single node? and/or needing to define many shaders for all the different situations?


Solution

  • Each primitive is intended to be a separate draw call, likely with a separate shader program.

    Notice that the primitive differs by more than just vertex attributes. The primitive includes its own material reference, so it could be a completely separate material from other primitives owned by the same mesh.

    Primitives exist to offer a mechanism to include multiple materials in a single mesh, but they require separate draw calls and separate shaders to implement. This is talked about in the Meshes Overview section.