How to add materials and textures to a GLB file: a step-by-step guide
Introduction
The GLB format (or its text analog glTF) is widely used for 3D models due to its lightweight nature and support for physically based materials (PBR — Physically Based Rendering). It allows developers and designers to store complex 3D models, complete with geometry, textures, materials, animations, and scenes, in a single binary file. This makes GLB an ideal format for applications in gaming, AR/VR, and web-based 3D content. High-quality materials and textures are critical for achieving realistic and visually appealing 3D models. They not only add depth and detail to the model but also enhance the overall user experience. In this guide, we will look at how to programmatically add materials and textures to a GLB file, enhancing its visual quality and realism. We will also provide tips on creating high-quality materials to ensure your 3D models are ready for any platform or use case.
PBR Basics and GLB Structure
A GLB file consists of:
Meshes: the geometry of the model.
Materials: a description of the visual properties (color, metallicity, roughness, etc.).
Textures: images used to add details (albedo, normals, metallicity, etc.).
PBR materials use physically correct lighting, which allows you to create realistic scenes. The main parameters of PBR:
Base Color (Albedo): the main color of the surface.
Metallic: The degree of light reflection typical of metals.
Roughness: Adjusts the smoothness of the surface (higher values = more matte surface).
Normal Map: Used to add fine details such as bumps or dents.
Initial Step
Launch Blender and Pycharm (or any other IDE, where python is installed).
Make an export of this file to glTF format.
Save it in any directory and name it as you wish.
Adding base colors
To add base colors to the GLB model, you can use the pygltflib library. Install this library with command:
pip install pygltflib
Launch this code in your IDE.
from pygltflib import GLTF2, Material
# Loading the GLB file
file_path = "path/to/model.glb"
glb = GLTF2().load(file_path)
# Creating a new material with a base color
new_material = Material(
name="CustomMaterial",
pbrMetallicRoughness={
"baseColorFactor": [1.0, 0.0, 0.0, 1.0], # Red color with full opacity
"metallicFactor": 0.0, # Non-metallic
"roughnessFactor": 0.8 # Matte surface
}
)
# Adding the material to the model
glb.materials.append(new_material)
# Assigning the new material to the first primitive mesh
for mesh in glb.meshes:
for primitive in mesh.primitives:
primitive.material = len(glb.materials) - 1 # Index of the new material
# Saving the updated file
glb.save("model_with_material.glb")
Explanations:
baseColorFactor defines the color of the material. In this case, it is red.
MetalFactor controls the metallicity of the material. A value of 0.1 makes the surface slightly metallic.
RoughnessFactor controls the roughness. A value of 0.8 makes the surface matte.
As a result, the mesh models will use red color, low metallicity and high roughness.
Now you have to import this file from your working directory back to Blender.
You have to receive this red cube:
Loading and assigning textures
Textures greatly improve the appearance of the model. Let's consider adding an albedo texture (Base Color).
from pygltflib import GLTF2, Image, Texture, Sampler, Material
# Loading the GLB file
file_path = "path/to/model.glb"
glb = GLTF2().load(file_path)
# Loading the texture image
texture_path = "path/to/texture.png"
with open(texture_path, "rb") as f:
image_data = f.read()
# Creating an Image object
image = Image(uri="path/to/texture.png")
glb.images.append(image)
# Creating a Sampler object
sampler = Sampler()
glb.samplers.append(sampler)
# Creating a Texture object
texture = Texture(sampler=len(glb.samplers) - 1, source=len(glb.images) - 1)
glb.textures.append(texture)
# Assigning the texture to the material
new_material = Material(
name="TexturedMaterial",
pbrMetallicRoughness={
"baseColorTexture": {"index": len(glb.textures) - 1}
}
)
glb.materials.append(new_material)
# Assigning the material to the mesh
for mesh in glb.meshes:
for primitive in mesh.primitives:
primitive.material = len(glb.materials) - 1
# Saving the file
glb.save("model_with_texture.glb")
Explanations:
Image: Represents the texture image file (e.g., texture.png) that you want to apply.
Sampler: Controls how the texture is sampled, such as wrapping or filtering.
Texture: Combines the image and sampler for use in the material.
baseColorTexture: Links the texture to the base color of the material.
As a result, the albedo texture (e.g., a detailed color image) is applied to the model, enhancing its appearance.
Import "model_with_texture.glb" as in the upper section.
Adding normal maps and other textures
To improve quality, you can add normal, metallic and roughness maps. These maps work similarly to albedo:
Load the map image.
Create an Image object, then a Texture.
Assign the texture to the corresponding material parameter (e.g. normalTexture, metallicRoughnessTexture).
Example of adding a normal map:
from pygltflib import GLTF2, Image, Texture, Sampler, Material
# Loading the GLB file
file_path = "path/to/model.glb"
glb = GLTF2().load(file_path)
# Loading the texture image
texture_path = "path/to/texture.png"
with open(texture_path, "rb") as f:
image_data = f.read()
# Creating an Image object
image = Image(uri="path/to/texture.png")
glb.images.append(image)
# Creating a Sampler object
sampler = Sampler()
glb.samplers.append(sampler)
# Creating a Texture object
texture = Texture(sampler=len(glb.samplers) - 1, source=len(glb.images) - 1)
glb.textures.append(texture)
# Assigning the texture to the material
new_material = Material(
name="TexturedMaterial",
pbrMetallicRoughness={
"baseColorTexture": {"index": len(glb.textures) - 1}
}
)
glb.materials.append(new_material)
# Assigning the material to the mesh
for mesh in glb.meshes:
for primitive in mesh.primitives:
primitive.material = len(glb.materials) - 1
# Loading normal map
normal_map_path = "path/to/normal_map.png"
with open(normal_map_path, "rb") as f:
normal_data = f.read()
image = Image(uri="path/to/normal_map.png")
glb.images.append(image)
texture = Texture(source=len(glb.images) - 1)
glb.textures.append(texture)
new_material.normalTexture = {"index": len(glb.textures) - 1}
# Saving the file
glb.save("model_with_texture_and_map.glb")
Explanations:
Material Assignment: Creates a new material (TexturedMaterial) and assigns the texture as the base color. Then, this material is linked to the mesh's primitives so it applies to the object.
Loading a Normal Map: Normal maps add depth and detail to the model without changing its geometry by simulating lighting variations.
The normal_map.png file is loaded as an additional Image and linked to a Texture object.
The normalTexture property of the material is set to reference this texture, enabling normal mapping.
Result: The model now includes realistic surface details, such as grooves or ridges, based on the normal map.
Tips for Creating High-Quality Materials
- Use textures with a resolution that is appropriate for your project. Avoid images that are too large, as this can slow down rendering.
- materials look best when using physically based lighting.
- Adjust the roughness and metallic parameters to achieve the desired effect.
- Test the model in environments with different lighting to ensure that it looks good in all conditions.
The Final Result
Full Code
from pygltflib import GLTF2, Image, Texture, Sampler, Material
# Loading the GLB file
file_path = "path/to/your/model.glb"
glb = GLTF2().load(file_path)
# Loading the albedo texture image
texture_path = "path/to/your/texture.png"
with open(texture_path, "rb") as f:
image_data = f.read()
# Creating an Image object for the albedo texture
image = Image(uri="path/to/your/texture.png"
glb.images.append(image)
# Creating a Sampler object
sampler = Sampler()
glb.samplers.append(sampler)
# Creating a Texture object for the albedo texture
texture = Texture(sampler=len(glb.samplers) - 1, source=len(glb.images) - 1)
glb.textures.append(texture)
# Creating a new material with a base color texture
new_material = Material(
name="TexturedMaterial",
pbrMetallicRoughness={
"baseColorTexture": {"index": len(glb.textures) - 1},
"metallicFactor": 0.0, # Non-metallic
"roughnessFactor": 0.8 # Matte surface
}
)
glb.materials.append(new_material)
# Assigning the material to the first primitive mesh
for mesh in glb.meshes:
for primitive in mesh.primitives:
primitive.material = len(glb.materials) - 1 # Index of the new material
# Loading the normal map texture
normal_map_path = "path/to/your/normal_map.png"
with open(normal_map_path, "rb") as f:
normal_data = f.read()
# Creating an Image object for the normal map
normal_image = Image(uri="path/to/your/normal_map.png")
glb.images.append(normal_image)
# Creating a Texture object for the normal map
normal_texture = Texture(source=len(glb.images) - 1)
glb.textures.append(normal_texture)
# Assigning the normal map texture to the material
new_material.normalTexture = {"index": len(glb.textures) - 1}
# Saving the updated GLB file
glb.save("full_model.glb")
Conclusion
Adding materials and textures to GLB files with Python opens up endless possibilities for automating workflows and creating realistic 3D models. By programmatically applying materials, textures, and maps, you can ensure consistency across projects and maintain precision in your 3D assets. Experiment with different texture maps, such as normal or roughness maps, to achieve the desired level of realism and visual quality. Always test your models after making changes to ensure compatibility with your target platform, whether it's a game engine, AR application, or web viewer. The combination of creativity and scripting opens up opportunities to bring your ideas to life efficiently. Let’s bring more stunning models to the virtual world together.

