Explainer

What it actually takes to build a PBR material automatically

Dropping a folder of textures onto a shader and getting a correct material looks like a small convenience. It is six distinct problems stacked on top of each other, and most of them fail silently when you get them wrong.

You have done this by hand hundreds of times: create the shader, add a texture node per map, set the colour spaces, route the normal through a converter, bind the displacement, connect eight sockets, rename it. Ten minutes that feels like one. This is what it takes to make that happen in a click — useful whether you are evaluating a tool or writing the script yourself.

The six problems, in order

01Group the loose files into a set

Strip the channel suffix, the resolution token and any UDIM number from each filename, and what remains is the set identifier. Files sharing an identifier are one material. This sounds trivial and is where most home-made scripts break, because the three tokens appear in different orders depending on who exported the files.

02Identify each map by channel

Match the stripped suffix against per-library vocabularies — _diff, _Albedo, _COL, _BaseColor and _D are all base colour. Normalise case and separators first. Where the name is ambiguous, fall back on the image itself: bit depth, channel count and whether the data is greyscale all narrow it down.

03Resolve conflicts and duplicates

A set may contain both a gloss and a roughness map, both normal conventions, or several resolutions. Rules decide: prefer roughness over gloss, prefer the OpenGL normal outside Unreal, prefer the highest resolution present unless told otherwise.

04Assign colour space per map

Base colour and emissive get the sRGB transform. Everything else is loaded raw. This is assigned per texture node, not per material, and it is the single highest-value thing automation does for you because it is the easiest thing for a human to get wrong silently.

05Build the graph for the target renderer

Create the surface node, the texture nodes and the conversion nodes that renderer needs — a bump-map node here, a native normal input there, displacement bound to the shading group in one and to the material output in another.

06Connect, name and assign

Wire the channels, give the material a name derived from the set rather than "Material.001", and assign it to the selection if there is one. The naming step is small and pays for itself the first time you open a scene from six months ago.

Problem two in detail: how channel detection works

This is the part people underestimate. There is no standard for PBR texture naming and there is unlikely to ever be one, because every library committed to a vocabulary early and changing it now would break their users' scripts. So a matcher has to hold several vocabularies at once.

the same channel, six ways
base colour   _diff   _Albedo   _Color   _BaseColor   _COL   _D
roughness     _rough  _Roughness          _ROUGHNESS  _R
normal (GL)   _nor_gl _Normal  _NormalGL          _NRM       _N

and the tokens around them move:
  wall_diff_4k.png          suffix, then resolution
  wall_4K_Albedo.jpg        resolution, then suffix
  wall_diff.1001.png        suffix, then UDIM

A workable algorithm normalises the filename to lowercase with single underscores, removes known resolution tokens and any four-digit UDIM number, then matches the remaining trailing token against a lookup keyed by channel. Where that fails, the image itself is evidence: a three-channel 8-bit file with saturated colour is almost certainly base colour; a single-channel 16-bit file is almost certainly height.

The two traps worth encoding explicitly

Gloss is not roughness. They are inverses. A gloss map plugged into a roughness input unchanged produces a mirror where you wanted concrete. Detect it and invert, or prefer the roughness map when a set ships both.

Normal conventions are not interchangeable. Green-up and green-down look nearly identical as thumbnails and produce lighting that is subtly inside-out. Prefer the labelled variant, and invert green when targeting a DirectX-convention renderer.

Problem five in detail: one set, six graphs

The same five files have to become a different node graph in every renderer. This is the part that cannot be generalised away, and it is why a script that works beautifully in your renderer stops being useful the moment a client puts you on another.

Target Surface node Normal handling Displacement Colour space set as
Blender Principled BSDF Normal Map node Displacement node → Material Output Non-Color on data maps
Cinema 4D · Redshift RS Standard Material Bump Map node, tangent-space Displacement node + tessellation tag Raw on data maps
Cinema 4D · Octane Universal Material Native normal input Vertex displacement Gamma 1.0 on data maps
Maya · Arnold aiStandardSurface aiNormalMap → normalCamera Shading group displacement + subdiv Raw on file nodes
Houdini · Karma mtlxstandard_surface mtlxnormalmap mtlxdisplacement output raw on mtlximage
Unreal Engine Material Instance Normal slot, DirectX green Tessellation or POM sRGB flag off on data maps

Notice the displacement column in particular. In Blender it goes to the Material Output; in Arnold it goes to the shading group and needs subdivision enabled on the shape or it silently does nothing; in Redshift it needs a tessellation tag on the object. Three renderers, three places, and only one of them tells you when it is missing.

Writing your own

Genuinely worth doing if you work in one application with one renderer — you will learn a lot and the result will fit your naming conventions perfectly. A reasonable order to build it in:

  1. Start with one library's naming convention, not all of them. Get Poly Haven or Megascans working end to end before generalising.
  2. Write the grouping logic first and test it against your messiest folder. If grouping is wrong, everything downstream is wrong.
  3. Hard-code colour space rules early. It is two lines and it eliminates the most common class of error.
  4. Build the graph with your DCC's Python API rather than by loading a template file — templates rot as soon as a set has an extra map.
  5. Handle the missing-map case. Most sets do not have every channel; the material should build cleanly with what is there.
  6. Only then add a second library's vocabulary, and a second renderer.

The cross-library naming cheat sheet has the vocabularies and the packed-channel orderings if you want a head start on step one.

How Kiosk does it

This is the core of what Kiosk is, so concretely: it runs steps one to four when it indexes your folders, and steps five and six when you click an asset.

Indexing resolves texture sets and records which file is which channel, so a set of eight maps becomes a single tile in the library rather than eight loose images. That work happens once, at index time, not every time you use the asset.

Export builds the graph for whichever renderer is active in your live session — Cycles or EEVEE in Blender, Redshift or Octane in Cinema 4D, Arnold, Redshift, RenderMan or V-Ray in Maya, Karma, Arnold, Redshift or RenderMan in Houdini, and OpenPBR master materials in Unreal. Colour space is assigned per texture node, the normal goes through whatever converter that renderer expects, and displacement is bound where that renderer looks for it.

HDRIs are treated as their own case: sending one creates the dome or sky light for your renderer, and sending a second with a light selected retargets that light rather than stacking another.

The quickest way to judge this is to point it at a texture folder you already have and click one surface with your DCC open. Either the material comes out right, or it does not.

Download the free edition

windows 10 / 11 · free edition · how the DCC plugins work

Questions

What is an auto material creator?

Anything that takes a set of texture files and produces a finished shader without you wiring nodes. The name covers a wide range of quality: at the simple end it is a template material with filenames swapped in, and at the serious end it is a system that identifies each map by channel, assigns the right colour space, converts normals correctly and builds a graph appropriate to your active renderer.

How does software know which texture is the roughness map?

By matching the filename suffix against a table of known conventions, then falling back on heuristics. Poly Haven writes _rough, Megascans writes _Roughness, Poliigon writes _ROUGHNESS and a game pipeline might write _R. A good matcher normalises case and separators, strips resolution and UDIM tokens, and keeps per-library vocabularies rather than one global guess. Where naming is ambiguous, image properties help — a single-channel greyscale image is not a base colour map.

Why does colour space matter so much?

Because only some texture maps are pictures. Base colour and emissive describe colours a human perceives, so they are stored with an sRGB transfer curve that must be removed on load. Roughness, metalness, normal, height and occlusion are numeric data — a roughness of 0.5 means 0.5. Running data through a colour transform changes the numbers, and nothing errors. The render is simply wrong in a way that is hard to trace.

What is the difference between OpenGL and DirectX normal maps?

The green channel is inverted. OpenGL convention has green pointing up and is what Blender, Maya, Houdini, Arnold, Redshift and V-Ray expect. DirectX has green pointing down and is what Unreal and many real-time pipelines expect. Automatic systems handle this either by preferring the correctly-labelled variant when a library ships both, or by inverting the green channel for the target renderer.

Can one material setup work for every renderer?

MaterialX is the closest thing, and it is genuinely portable between MaterialX-aware renderers such as Karma. Outside that, no — a Principled BSDF, a RedshiftStandardMaterial, an aiStandardSurface and an Unreal Material Instance want different node graphs, different displacement plumbing and different normal handling for the same set of files. Automating across renderers means writing the graph builder once per renderer.

What about packed ORM textures?

A packed map carries three greyscale channels in one RGB image — typically occlusion, roughness and metallic. Handling it automatically means recognising the packing convention from the filename, inserting a channel-split node, and routing each channel to its own input. The ordering is not standardised across vendors, so ORM, ARM, RMA and MRAO all need their own mapping.