OBJ File Documentation


Summary

A file with the .obj extension is usually a Wavefront OBJ file: a plain-text definition of 3D geometry — vertices, texture coordinates, normals and faces — and one of the most widely supported 3D interchange formats. It has no binary magic number; lines begin with tokens such as v, vt, vn and f. Its MIME type is model/obj. An OBJ stores geometry only; colours and textures live in a companion .mtl file, so a textured model is really a small bundle of files that must stay together.

Technical details

FeatureValue
Full nameWavefront OBJ (3D geometry)
File extension.obj
MIME typemodel/obj
Format typePlain-text 3D geometry (vertices, texture coords, normals, faces)
DeveloperWavefront Technologies
IntroducedLate 1980s (for The Advanced Visualizer)
Standard / specDe facto (Wavefront OBJ specification)
Open standardYes (openly documented)
EncodingASCII text; no byte signature
Line tokensv, vt, vn, f, mtllib, usemtl, g, o, #
Geometry typePolygon / triangle mesh (surface approximation, not CAD B-rep)
Coordinate systemRight-handed; indices are 1-based
MaterialsExternal .mtl file referenced by mtllib
Also used byCOFF compiled object file (.obj) — an unrelated build artefact
Category3D Image Files
Related extensions.mtl, .stl, .ply, .fbx, .gltf, .glb, .dae
Specification URLhttps://paulbourke.net/dataformats/obj/
Syntax at a glance

OBJ is ASCII text with no magic number, so it is identified by its line tokens, not a byte signature. Each line is a keyword followed by whitespace-separated values: v x y z is a vertex position, vt u v a texture coordinate, vn x y z a vertex normal, and f a face that references those by index. A leading # is a comment (often naming the authoring tool), and mtllib file.mtl points at the companion material file. All indices are 1-based, and a face index can be written v, v/vt, v//vn or v/vt/vn.

What is an OBJ file?

OBJ is the Wavefront OBJ format, a 3D geometry definition created by Wavefront Technologies in the late 1980s for its Advanced Visualizer animation software. It became one of the most universally supported 3D interchange formats for three reasons: it is simple, openly documented, and plain text. An OBJ stores a mesh as lists of vertices, texture coordinates, vertex normals, and the faces that connect them, and nearly every 3D application — Blender, Maya, 3ds Max, Cinema 4D, ZBrush, SketchUp, CAD tools — can import and export it. That ubiquity is why OBJ is the common lowest-common-denominator for moving models between programs. Its MIME type is model/obj.

An OBJ defines geometry only. Surface appearance — colour, shininess, texture images — lives in a separate companion .mtl file. And because the format describes a triangle/polygon mesh (an approximation of a surface), it is a graphics, visualization and 3D-printing format, not exact engineering CAD like STEP. Everything below is about how those plain-text records actually encode a mesh.

Vertex data: v, vt and vn

Three record types define the raw geometric data, each on its own line, keyword first:

# positions: x y z  (a fourth value w defaults to 1.0)
v  1.000000 -1.000000 -1.000000
v  1.000000 -1.000000  1.000000

# texture coordinates: u v  (v optional, defaults 0; range typically 0..1)
vt 0.625000 0.500000

# vertex normals: x y z  (need not be unit length)
vn 0.0000 1.0000 0.0000

A v line is a point in 3D space; the collection of all v lines is the model’s vertex list. A vt line is a texture coordinate that maps a location in an image onto the surface. A vn line is a vertex normal, the surface direction used for shading. These three lists are independent and are stitched together by faces. A critical detail is that OBJ uses a right-handed coordinate system and stores these as human-readable decimal numbers, which is what makes an OBJ diffable and editable in a text editor but also larger and slower to parse than a binary mesh format.

Faces and 1-based indexing

The f record builds the actual surface by referencing the vertex, texture and normal lists by index. OBJ indices are 1-based and count in the order the elements appear in the file, so the first v line is vertex 1, not 0 — a frequent source of off-by-one bugs when writing a parser. A face lists three or more index groups, and each group can take four forms:

Face syntaxMeaning
f v1 v2 v3Position only
f v1/vt1 v2/vt2 v3/vt3Position and texture coordinate
f v1//vn1 v2//vn2 v3//vn3Position and normal (texture omitted, note the double slash)
f v1/vt1/vn1 ...Position, texture and normal
f 1/1/1 2/2/1 3/3/1        # a triangle, full v/vt/vn references
f 5//1 6//1 7//1 8//1      # a quad, positions + normal only

A face with more than three vertices is a polygon (a quad or n-gon), assumed planar and convex; many tools triangulate n-gons on import. OBJ also permits negative indices, which count backwards from the most recently defined element (-1 is the last vertex written), useful for streaming writers that do not track absolute counts.

Grouping, objects and material assignment

Beyond raw geometry, OBJ has organisational records. o name opens a named object, and g name opens a named group of faces; both let a single OBJ hold several logical parts that a modeller can select independently. s 1 / s off toggle smoothing groups, controlling whether adjacent faces are shaded smoothly or with a hard edge. Material assignment uses two records that work together: mtllib file.mtl near the top of the file names the external material library to load, and usemtl name before a run of faces says “the following faces use this material”. A model can switch materials many times by repeating usemtl.

The .mtl companion and the “grey model” problem

Because OBJ carries no appearance data, a textured OBJ is really a small bundle: the .obj geometry, its .mtl material file, and one or more image files (JPG/PNG) that the MTL references. The .mtl is itself plain text: a newmtl block per material defines ambient (Ka), diffuse (Kd) and specular (Ks) colours, shininess (Ns), opacity (d), and texture-map lines such as map_Kd texture.jpg pointing at the image used for the diffuse colour.

# in model.obj
mtllib model.mtl
usemtl BodyPaint
f 1/1/1 2/2/1 3/3/1

# in model.mtl
newmtl BodyPaint
Kd 0.80 0.10 0.10
Ns 96.0
map_Kd body_diffuse.jpg

This split explains the single most common OBJ complaint: a model that opens grey or untextured. If you move or send only the .obj, or the .mtl and its images are not in the same folder, the renderer has geometry but no materials and falls back to flat grey. Keeping all three parts together is the fix. It is also why the modern web/AR format glTF is often preferred: glTF bundles geometry, materials and textures into one file and sidesteps the companion-file problem entirely.

OBJ against STL and CAD formats

OBJ is frequently compared with two neighbours. STL, the standard 3D-printing format, stores only a raw triangle soup with per-facet normals and no vertex sharing, texture coordinates, colour or grouping. Converting OBJ to STL for slicing is common and lossless as to shape, but it drops all colour and texture — expected, because a typical printer prints geometry, not surface images. The other comparison is with CAD formats like STEP. Those store exact boundary-representation geometry (precise curved surfaces and solids you can measure and re-machine), whereas OBJ stores a tessellated mesh that only approximates such surfaces with flat triangles. You cannot faithfully turn an OBJ mesh back into clean STEP B-rep geometry; mesh-to-CAD “reverse engineering” is a manual, lossy process, not a file conversion. In short: OBJ and STL are meshes; STEP is CAD.

The .obj name collision: Wavefront versus COFF

Developers should be aware of a second, entirely unrelated meaning of the extension. C and C++ compilers (notably MSVC) produce a compiled object file named .obj as a build intermediate — a COFF binary of machine code and relocation data that the linker consumes to produce an executable. That file is binary, has nothing to do with 3D graphics, and shares only the letters. Telling them apart is trivial: a Wavefront OBJ is readable ASCII whose lines start with v, vt, vn, f or #; a COFF .obj is binary and appears as gibberish in a text editor. The presence of mtllib/usemtl tokens or a # Blender-style comment header confirms the 3D format.

FAQ

Why does my OBJ model open grey or without textures? OBJ stores only geometry. Colours and textures live in a separate .mtl file plus the image files it references. If you only have the .obj, or the .mtl and images are not in the same folder, the model renders untextured. Keep all the files together.

Why are OBJ indices 1-based? The format predates the C convention of 0-based arrays and was designed to be written and read by hand, where counting from 1 is natural. A parser must subtract 1 when indexing into a 0-based array, and must also handle negative indices that count backwards from the current element.

What is the difference between OBJ and STL? Both are meshes. OBJ can store texture coordinates, normals and material references, so it is used for rendering and games; STL stores only the raw triangle surface with no colour and is the standard for 3D printing.

References