Firstly, some references
The following text is based on the glTF tutorial written by The Khronos Group. I strongly recommend to read the tutorial, it covers many of the concepts introduced here.
This essay has to be intended as a complement to the first five chapters of the tutorial. Some arguments discussed in the tutorial are missing here, like scenes and nodes, whereas other arguments are discussed with more details. In particular, we are going to see how to get a buffer binary file storing some custom geometry data, an argument that may be difficult to grasp at first glance.
Please have a look at the Github repository for the code related to the present example, as well as for other interesting experiments with the glTF format.
Define vertices indexes and positions
We start by defining the triangle we want to draw. Let say we want the triangle with the first vertex at position
(1, 0, 0), the second vertex at position
(0, 1, 0) and the third vertex at position
(0, 0, 0). Take a paper and a pencil and draw the triangle in a right-handed cartesian coordinate system, you will see that the face direction is counter-clockwise.
The face direction is defined by the order of the three vertices that make up the triangle, as well as their apparent order on-screen, and its primary use is to allow the culling (removal) of not visible triangles on closed surfaces.
We can store the described triangle in a js object like the following:
Note that there are many possible ways to encode the wanted triangle in a data structure, the above definition is convenient because, as we will see, it resembles how data is going to be stored in buffers. The reason behind this data structure will be better understood once accessors and rendering modes will be introduced.
In the next section, we are going to store the data in a binary file.
Store data in an external binary file
Binary data is referred to by a buffer, a buffer URI may point to an external binary file or it may be a data URI that encodes the binary data directly in the glTF file.
There is not a unique way to store the data in a buffer, here we are going to use the same convention described in the tutorial. We first store indexes data as UNSIGNED_SHORT components and, with the appropriate byte offset, we later add vertices data to the buffer as FLOAT components. The code snippet below defines a function that takes
data as input and writes a binary file called
buffer.bin in the file system.
The above function is meant to be used in node.js. Create the script below and run it in order to get the binary file.
In order to use the data encoded in the binary file, we need to appropriately describe, in the glTF file, how data is stored. In the next section, we are going to do that by means of bufferViews and accessors.
Describe data with bufferViews and accessors
In the previous section, we created a binary file containing two pieces of information: the vertices indexes and the vertices positions. With bufferViews we describe these two views of data in the buffer.
Accessors add additional information about the type and layout of the data described in the bufferViews. In the first bufferView we have 3 elements of type SCALAR with components of type UNSIGNED_SHORT. In the second buffer we have 3 elements of type VEC3 with components of type FLOAT.
BufferView and accessors completely describe the data stored in the buffer, now it is time to use this data.
Define the meshes
Having the data and its description, we are ready to define meshes that use the data. A mesh describes a geometric object that appears in the scene, and it is made of smaller building blocks called mesh primitive objects.
A mesh primitive may describe individual points, lines, or triangles according to the selected rendering mode. Different rendering modes will use data in different ways, as described in the OpenGL primitive documentation.
The code below defines a single mesh, made of a single primitive, with vertices indices referenced in the first accessor (
"indices": 0) and vertices positions in the second accessor (
The final model
In the previous sections we described some of the parts componing the final glTF file. We discussed about bufferViews and accessors for the interpretation of the buffer data, but we missed out the parts related to scenes and nodes, well covedered in the tutorial.
You can see the full glTF file visualized below in the repository.
Adding a bit of complexity to our example, we can easily obtain a double-faces triangle using the TRIANGLE_STRIP rendering mode. To do that, we assign
"mode": 5 to our primitive and create the binary file for a data structure with
"indexes": [0, 1, 2, 0]. Having now different data, we need to update the first buffer view with
"byteLength": 8 and the first accessor with
The purpose of glTF is to define a format for the efficient transfer of 3D content over networks. The previous sections are based on the first five chapters of the glTF tutorial. We discussed about the production and interpretation of a buffer binary file storing a custom geometry and we touched arguments like mesh primitives and rendering modes, key concepts for the creation of any 3D model.