POLYMASON

The OBJ file format, explained for game engines

OBJ is a plain-text 3D format from 1990 that's still in everyday use. Every modeling tool writes it and every engine reads it. You can also open one in a text editor and understand it, and that's worth doing once, because most import problems come from the things an OBJ doesn't say.

This guide covers what's in the file and its material library, what isn't and what each engine assumes in its place, how OBJ compares with the formats you'd use instead of it, and how to get one into Unity, Godot and Unreal at the right size, the right way up and in color. The articles under it go into full detail on the comparison with FBX, GLB and STL and on importing into Unity, Godot and Unreal. What happens to the shading after import is in flat shading in Unity.

  • OBJ vs FBX vs GLB vs STL: which format, and whenThe four formats a game or a printer will ask for, what each one can and can't carry, how Unity, Godot and Unreal read each, and which to ask a pack for. There's one table, then a section on each format.
  • Import an OBJ into Unity: size, color, shadingThe steps for getting an OBJ and its materials into Unity, the Scale Factor and Convert Units settings that make a model the right size, what to do when a model arrives a hundred times too big, the Materials tab, normals for flat shading, and colliders.
  • Import an OBJ into Godot 4 as a mesh or a sceneThe steps for putting an OBJ and its MTL into a Godot 4 project, the difference between importing it as a mesh and as a scene, scale, materials, reimporting after a change, generating LODs, and adding collision.
  • Import an OBJ into Unreal at the right scaleThe steps for importing an OBJ as a Static Mesh in Unreal Engine 5, the Import Uniform Scale of 100 that a file in meters needs, Convert Scene for the up axis, Normal Import Method for flat shading, what happens to the MTL's materials, collision, LODs and Nanite.

What is in the file

An OBJ is lines of text, and each line starts with a keyword. These are the ones you'll meet:

# a comment
mtllib tree.mtl        the material library, a second file
o oak                  an object, and its name
v 0.12 0.0 -0.08       a vertex position: x y z
vt 0.5 0.5             a texture coordinate: u v
vn 0.0 1.0 0.0         a normal: x y z
usemtl palette         the material for the faces that follow
s off                  smoothing group: off means flat
f 1/1/1 2/2/2 3/3/3    a face: vertex/texture/normal per corner

Faces refer to vertices by number, counting from one in the order the v lines appear. Texture coordinates and normals are numbered the same way. f 1/1/1 2/2/2 3/3/3 is a triangle whose corners are vertices one, two and three, each with its own texture coordinate and normal. The slashes separate the three kinds of index. f 1//1 is a corner with a normal but no texture coordinate, and f 1 is a bare vertex. A negative index counts back from the most recent vertex, and some exporters write those. A face can have four or more corners, and engines triangulate it on import, so quads and n-gons in a file aren't a problem unless they aren't flat.

Two other keywords group things. o starts a named object and g starts a named group inside one. Each engine does something different with them. Unity makes a child GameObject per object and per material, Godot makes a surface per material, and Unreal makes a material slot per material. s sets a smoothing group, either a number or off. Older tools used it to decide which faces to blend across when the file has no normals. Modern engines mostly ignore it and go by their own normal settings.

The material library, the .mtl file, is written the same way:

newmtl palette         a material, and its name
Kd 1.0 1.0 1.0         diffuse color, red green blue in 0 to 1
Ka 0 0 0               ambient color
Ks 0 0 0               specular color
Ns 10                  specular exponent
d 1.0                  opacity
illum 1                lighting model
map_Kd tree.png        a texture for the diffuse color, by file name

Kd is the color, and map_Kd is a texture used in its place. A file with several newmtl blocks gives the model several materials, and each one is used by whatever faces follow its usemtl.

Games don't use any more of the format than that. The full specification goes on to curves and surfaces, which no engine reads. OBJ has no rig, no animation, no hierarchy beyond names, and no compression. A text file with every number written out is large for what it holds, though a low poly model still only comes to kilobytes.

What is not in the file

Units. An OBJ has no unit. The numbers are bare numbers, and each tool decides what they mean:

  • Unity reads an OBJ at one unit to one meter. A model made in meters arrives at the right size.
  • Godot does the same. One unit is one meter.
  • Unreal works in centimeters. A model made in meters arrives a hundred times too small. On the import dialog, set Import Uniform Scale to 100.
  • Blender exports at one unit to one meter unless told otherwise, so a model made there in meters is right for Unity and Godot. Its FBX exporter behaves differently, and that's where the well-known "everything is a hundred times too big" problem comes from.

Which way is up. OBJ has no axis convention. Unity and Godot are Y-up. Unreal is Z-up, and so is Blender, whose exporter has a Forward and Up setting to convert. Engines convert on import when they know the source, but a file exported with the wrong axes lands lying on its side. If a tree imports flat on the ground, this is why.

Colors. An OBJ has no colors of its own. They're in the .mtl. If the .mtl is missing or wasn't put in the same folder, the model imports gray, with a material slot per usemtl for you to fill. Some tools write vertex colors as three extra numbers on each v line. That's a non-standard extension and most engines ignore it.

Normals. These are optional. If the file has vn lines, engines use them. If it doesn't, the engine works the normals out itself, and Unity's default smoothing turns a low poly model smooth. Flat shading in Unity covers that problem and its fixes. A file with flat normals in it imports correctly in every engine.

The origin. The file's zero point becomes the object's pivot. A tree whose origin is at the middle of its trunk ends up half underground when you drop it on terrain, and one whose origin is at the base stands on it. A good exporter puts a prop's origin where it touches the ground.

Scale of texture coordinates. vt values run from 0 to 1 across the texture, with v increasing upward, which is the opposite of most image formats. Engines handle the flip, but a tool that gets it wrong shows the texture upside down.

What PolyMason writes

The tree builder writes an OBJ in meters, Y-up, standing on the ground at the origin, as one mesh with one material. The .mtl beside it points at the PolyMason palette, which is one PNG of the full spectrum as blocks, shared by every model from here. Each face's texture coordinates point at the block nearest its color, and there's a normal per face corner so the model shades flat in any engine. That means a whole set, the tree at three levels of detail and in every state of its life, plus every other model built here, is one material in the engine. The builder also writes STL for printing and 3MF with the colors in it. GLB is next.

OBJ, FBX, GLB or STL

Four formats cover almost everything a game needs, and each has a job it's right for:

  • OBJ: text, simple, universal. No animation, no rig, and colors in a second file. Right for static props and environment pieces, and for anything you want to be able to read.
  • FBX: binary, and it holds rigs, animation, blend shapes and embedded textures. It's Autodesk's format, and every engine reads it through Autodesk's library. Right for characters and anything animated, and it's the format Unity and Unreal pipelines assume.
  • GLB: the binary form of glTF 2.0, compact, with PBR materials and textures in one file, and an open specification. It's the web's format and the one Godot prefers. Unity reads it with a package and Unreal reads it natively. Right for most things once you have it.
  • STL: triangles and nothing else, so no color, no units and no materials. 3D printers want it and no engine does.

The comparison article has the table of what each format carries and what each engine does with it, and says which format to ask a pack for.

Importing one, engine by engine

Unity: drop the .obj, its .mtl and any texture into the Assets folder together. Unity makes a prefab with a child per object and a material per usemtl. If it shades smooth, change the normals on the Model tab. If it's the wrong size, change Scale Factor, though a file in meters needs none. The Unity article has the steps in order, and covers the Materials tab and colliders.

Godot 4: drop the .obj into the project with the .mtl beside it. It imports as a mesh resource you can give to a MeshInstance3D, with a surface per material. If you'd rather have a scene with a node per object, change the importer in the Import dock. The Godot article covers that as well as scale, reimporting and collision.

Unreal: press Import in the Content Browser and choose Static Mesh. Set Import Uniform Scale to 100 for a file in meters, and set Normal Import Method to Import Normals to keep flat shading. Unreal makes a material slot per usemtl and, if it finds the .mtl and textures, materials to fill them. The Unreal article goes through the dialog.

When OBJ is the wrong choice

OBJ can't carry a rig or animation, so for a rigged or animated model use FBX or GLB. GLB keeps a whole scene's hierarchy of objects and their transforms, and OBJ flattens it. For a model going to the web, GLB is smaller because it's binary, and browsers load it directly. For a printer, export STL or 3MF. Many slicers will open an OBJ, but STL and 3MF are what they're built around.

The other guides

  • Flat shading in Unity for low poly modelsWhy a low poly model goes smooth when it lands in Unity, what the importer's Normals and Smoothing Angle settings do, the three places you can fix it, and how to keep the colors flat as well. Three articles underneath go into the detail.
  • LODs and billboards for low poly modelsHow levels of detail work, what triangle counts are reasonable for low poly trees, rocks, buildings and characters, how to make the levels so the swap doesn't pop, billboards and impostors for the far distance, and the LOD setup in Unity, Godot and Unreal.
  • Free low poly assets for GodotWhere to get free low poly models that work in Godot 4, what each source's license allows, and which formats to prefer. It also covers importing an OBJ or GLB with its colors and flat shading intact, adding a collider, and scattering a forest.
  • How to make low poly trees for gamesThe method behind a low poly tree that looks like its species: the silhouette that identifies it, the trunk, where the boughs leave and how the trunk divides, foliage as a few closed meshes, two greens, triangle budgets, and the export a game engine wants. The same steps work in the builder and by hand in Blender.

Or build a tree and see what comes out.