What an MTL File Is, and Why Your Textures Vanished Without It

Published 2026-09-20

An MTL file is the part of an OBJ asset that says what the surfaces look like. It arrives alongside the OBJ, it is plain text, and most people first hear of it when their model shows up grey.

We exported one textured cube and looked at what the exporter actually produced, then sent the model on the way people really send models.

What is an MTL file?

MTL stands for Material Template Library, and it is a plain text companion to a Wavefront OBJ file. Where the OBJ stores geometry, the vertex positions, the faces built from them and the UV coordinates that say how a flat image wraps onto them, the MTL stores appearance: colours, how shiny a surface is, how transparent, and the names of any image files used as textures.

Here is the entire material file our exporter wrote, unedited:

# https://github.com/mikedh/trimesh

newmtl material_0
Ka 0.40000000 0.40000000 0.40000000
Kd 0.40000000 0.40000000 0.40000000
Ks 0.40000000 0.40000000 0.40000000
Ns 1.00000000
map_Kd material_0.png

newmtl opens a named material. Ka, Kd and Ks are ambient, diffuse and specular colour as red, green and blue values from 0 to 1. Ns is the specular exponent, which controls how tight a highlight looks. And map_Kd is the line that matters here: it names an image file to use as the diffuse texture, the picture you actually see on the surface.

Note what map_Kd is not. It is not the image. It is the string material_0.png, and nothing else.

Why does the texture disappear when you send just the model?

Because the picture was never in the model file, and the reference that points to it is resolved against the folder the file happens to be sitting in. Move the file without the folder and the reference has nothing to resolve against.

Our export produced three files from one model. Searching each for the PNG signature, the byte marker that starts every PNG image, gives a clear answer about where the picture lives:

FileSizeContains image data
model.obj759 bytesNo
material.mtl198 bytesNo
material_0.png184 bytesYes

And the chain between them is two links long, each one a bare filename with no path and no fallback:

model.obj      ->  mtllib material.mtl
material.mtl   ->  map_Kd material_0.png

So we did what people actually do. We copied model.obj on its own into an empty folder, as though attaching “the model” to a message, and loaded it from there.

The geometry arrived intact. The texture did not: instead of the 64 by 64 pixel image we authored, the reader produced a two pixel placeholder. No error was raised, no warning was printed, and the load reported success.

Why is the model grey rather than untextured?

Because the MTL always carried a fallback colour, and losing the image simply reveals it. Look again at the material above: Kd 0.40000000 0.40000000 0.40000000 is a flat mid grey, and our exporter wrote it without being asked.

That is the answer to the most common version of this question. The model did not lose its material. It found the material, failed to find the picture the material named, and fell back to the plain colour sitting on the same line. A different exporter writing Kd 1.0 1.0 1.0 produces a white model from exactly the same failure.

What does each program draw instead?

Each one substitutes something different, and knowing which is which turns a vague “it looks wrong” into a diagnosis. The following is collected from vendor documentation and from reports by experienced users rather than from our own measurement, so treat it as a field guide rather than as data.

ProgramWhat you see insteadWhere it tells you
BlenderBright magentaSystem console; the Image Texture node shows the missing file
UnityBright magentaConsole, though in Unity magenta more often means a render pipeline mismatch than a missing image
Unreal EngineGrey and white checkerboard, its default WorldGridMaterialOutput log
3ds MaxNothing yet, a dialog blocks the load until paths are resolvedThe “Missing External Files” dialog
MayaDefault Lambert greyScript Editor warning naming the path
RhinoThe expected file path drawn as text across the surfaceThe surface itself
Web viewersBlack, or the poster imageBrowser console

The Rhino row is worth knowing because it looks like a bug rather than a diagnostic. Experienced users on the McNeel forum describe it plainly: when Rhino cannot find the image, it renders the path it expected directly onto the model. Students hitting it for the first time report it as “strange text appearing on model”. It is the most useful behaviour in the table, since the software tells you exactly which file it went looking for.

Which formats carry the image inside the file?

One of the five we tested. We exported the same textured cube to OBJ, GLB, PLY, STL and 3MF, then loaded each one twice: once in place, and once after copying only the model file to an empty folder.

FormatFiles writtenImage inside the model fileOpened in placeOpened after being sent alone
OBJ3NoTexture presentTexture gone
GLB1YesTexture presentTexture present
PLY1NoAlready no textureNo texture
STL1NoNo material at allNo material at all
3MF1NoNo material at allNo material at all

GLB is the only row that survives the journey, and it survives for one reason: the image bytes are inside the file. This is the same property that makes GLB awkward to edit and easy to send, which we go through in more detail in what a GLB file is.

Two caveats on the bottom rows, because the table is narrower than it looks. Plain STL has no material system at all, which is why converting GLB to STL throws away everything except the triangles. PLY and 3MF are different: both have mechanisms that can carry a texture, a de facto comment convention in PLY and a materials extension in 3MF, and our exporter simply did not write either. Those two rows say the file we produced arrived without a texture, not that the format is incapable of one.

Full results, method and limitations: What happens to a texture when only the model file is sent on. The raw output is published as JSON, produced by scripts/measure-texture-survival.py.

How do you export so that it does not happen?

Decide first whether the asset is being edited or delivered, because that is the actual question. For delivery, export GLB and the problem disappears. For OBJ, the images have to travel and the paths have to be relative.

  • Blender, FBX and OBJ export. Set Path Mode to Copy and enable the adjacent “Embed Textures” toggle. Path Mode left on Auto writes references instead, which is the single most common way an export arrives empty.
  • Blender, before you export anything. File > External Data > Make All Paths Relative converts absolute paths to paths relative to the blend file. File > External Data > Pack Resources puts the images inside the blend itself, which matters if you are sending the blend rather than an export.
  • Maya. The FBX export options include an Embed Media checkbox. Unchecked, Maya writes paths based on the current project workspace, which will not exist on anybody else’s machine.
  • Any OBJ you send to a person. Zip the folder, not the file. Tell the recipient to extract the whole folder rather than dragging the OBJ out of the archive, which is the step that quietly breaks it.

What breaks a path that still looks correct?

Three things, and all of them survive a visual check because the reference reads perfectly well to a human.

Case. Windows treats Wood.PNG and wood.png as the same file. Linux and macOS do not, and most web servers run Linux. An OBJ that has worked on a desktop for months can lose its textures the first time it is served from a website, with nothing changed but the machine.

Spaces and non-ASCII characters. The MTL format separates tokens with whitespace, so a filename containing a space can be read as two things. Accented or non-Latin characters in a filename are a second version of the same problem, and web viewers add a third, since a URL needs those characters percent encoded before a server will find them.

Separators. Windows writes textures\wood.png with a backslash. On a POSIX system a backslash is not a directory separator, so that whole string is read as a single strange filename rather than as a folder and a file. Modern engines clean this up on import; older OBJ parsers and raw web loaders do not.

There is a fourth case we deliberately do not have measured numbers for. Referencing an image above the model folder with ../textures/wood.png works fine for a program reading your local disk, but a web viewer fetching over HTTP resolves that as a URL, and whether it finds anything depends entirely on how the server is laid out. That is a difference in how the path is resolved, not a security mechanism, and it is worth testing rather than assuming either way.

Where this fits

If you are choosing a format rather than fixing one, the comparison worth reading first is OBJ against STL, which covers what each one can and cannot store, and FBX against OBJ, which is the one to read if the destination is a game engine. If you just want to see what is actually inside a file before you send it, our online GLB viewer reports UVs, materials and geometry without uploading anything.

And the short version, if you remember one thing: an OBJ is not a model, it is the first file of a set. Send the folder.

Frequently asked questions

What is an MTL file?

A Material Template Library file: a small plain text file that sits next to an OBJ and describes what its surfaces look like. It holds colours, shininess and transparency, and it names the image files used as textures. It holds no image data itself. In our test the MTL came to 198 bytes while the texture it named was a separate 184 byte PNG, and the OBJ was a third file again.

How can I open an MTL file?

Any text editor, because it is plain text. That is also the fastest way to diagnose a missing texture: open it and read the map_Kd line, which names the image file the material expects. If that filename is not sitting in the folder you are looking at, you have found the problem. Do not open the MTL in your 3D program directly; open the OBJ, and the program follows the reference itself.

What is the difference between OBJ and MTL files?

The OBJ holds the shape and the MTL holds the appearance. The OBJ stores vertex positions, faces and UV coordinates, plus one line naming its material library. The MTL stores the material definitions and names the texture images. Neither contains the images. A textured OBJ asset is therefore at least three files, and all of them have to travel together.

Why did my 3D model turn grey after import?

Usually because the material was found and the texture image was not. The MTL declares a fallback diffuse colour on its Kd line, and many exporters write a neutral grey there. Our exporter wrote Kd 0.40 0.40 0.40. When the image is missing the program falls back to that flat colour, so the model is not untextured, it is wearing the placeholder the MTL always carried.

Can you put the texture inside an OBJ file?

No. The OBJ format has no container for binary image data, and neither does the MTL. A texture is always an external file referenced by name. If you need one file that carries its own images, that is GLB, which stores the image bytes inside the binary payload. In our test the GLB was the only format of five that still had its texture after being moved on its own.

Why do my textures work on my computer but not on someone else's?

Because the reference is resolved against the folder, not against the file. If the MTL names an absolute path such as C:\Users\you\textures\wood.png, that path does not exist anywhere else. Even relative references break if the recipient extracts only the OBJ from a ZIP rather than the whole folder. Case is the other common cause: Windows resolves Wood.PNG against wood.png and Linux, which is what most web servers run, does not.