Skip to content
← Back to Blog 3d-modeling

ORM Textures in Godot 4: A Complete PBR Workflow with StandardMaterial3D

By BitSoul3D6 min read163 views

Godot 4's physically based rendering pipeline is genuinely excellent — but most tutorials stop at dragging an albedo texture into StandardMaterial3D and calling it done. That leaves serious performance and quality on the table. The real unlock is understanding ORM textures: a single packed image that encodes Occlusion, Roughness, and Metallic data into three separate channels. One texture slot. Three properties. Zero wasted samples.

ORM Textures in Godot 4: A Complete PBR Workflow with StandardMaterial3D

This guide walks through the complete ORM workflow: what each channel does, how to author ORM maps in Blender and Substance Painter, how to wire them correctly inside Godot 4, and which common mistakes will silently destroy your material quality.

What Are ORM Textures and Why They Matter

ORM stands for Occlusion (R channel), Roughness (G channel), Metallic (B channel). Instead of sampling three separate grayscale textures at runtime, you pack all three values into the RGB channels of a single texture. The GPU fetches one texture instead of three — reducing memory bandwidth and texture cache misses simultaneously.

Godot 4's StandardMaterial3D has native support for this format through the ORM texture input under the Occlusion, Roughness, and Metallic combined workflow. When you assign a texture to the orm_texture property and enable texture_channel flags correctly, Godot reads R for occlusion, G for roughness, and B for metallic automatically.

What Are ORM Textures and Why They Matter — illustrated

Here is what each channel controls:

ChannelProperty0 Value1 Value
R (Red)Ambient OcclusionNo occlusion (full light)Full occlusion (shadowed)
G (Green)RoughnessMirror-smoothFully diffuse
B (Blue)MetallicDielectric (plastic/stone)Conductor (metal)

The green channel is most visually impactful — roughness controls how tightly the specular highlight focuses. A roughness of 0.0 gives you a perfect mirror. At 1.0 you get completely diffuse scattering with no visible specular. Most real-world surfaces live between 0.3 and 0.8.

Why not separate textures? Three 1024x1024 grayscale textures at 8-bit consume 3 MB uncompressed and three texture unit binds per draw call. One 1024x1024 RGB texture consumes 3 MB — but only one bind. On mobile GPUs with limited texture units, this distinction can mean the difference between a shader that compiles and one that doesn't.

Understanding StandardMaterial3D Properties

Godot 4's StandardMaterial3D exposes PBR parameters across four property groups: Albedo, Metallic, Roughness, and Emission. For ORM workflows you are working primarily in the Metallic and Roughness groups.

Key properties to configure:

When setting up your ORM texture:

  1. Under Metallic, set Texture Channel to Blue.
  2. Under Roughness, set Texture Channel to Green.
  3. Under Ambient Occlusion, enable it and set Texture Channel to Red.
  4. Assign your ORM image to all three texture slots — Godot deduplicates the GPU resource automatically.
# Assign ORM map in GDScript
var mat = StandardMaterial3D.new()
var orm_tex = load("res://textures/my_asset_orm.png")

mat.ao_enabled = true
mat.ao_texture = orm_tex
mat.ao_texture_channel = BaseMaterial3D.TEXTURE_CHANNEL_RED

mat.roughness_texture = orm_tex
mat.roughness_texture_channel = BaseMaterial3D.TEXTURE_CHANNEL_GREEN

mat.metallic_texture = orm_tex
mat.metallic_texture_channel = BaseMaterial3D.TEXTURE_CHANNEL_BLUE

# Scalar multipliers should be 1.0 to let texture drive values
mat.metallic = 1.0
mat.roughness = 1.0

Creating ORM Maps in Blender and Substance Painter

The authoring step is where most developers make mistakes. Both tools output the right data, but neither outputs a pre-packed ORM file by default — you have to configure the export correctly.

Creating ORM Maps in Blender and Substance Painter — illustrated

Blender (via Cycles baking)

Blender bakes occlusion, roughness, and metallic as separate images. To pack them:

  1. Bake Ambient Occlusion to save as ao.png (grayscale).
  2. Bake Roughness from your material's roughness output to save as rough.png.
  3. Plug your metallic node into the Emission bake output to save as metal.png.

Then pack in Blender's Compositor using the Combine RGBA node:

Save the result as asset_orm.png. Use 8-bit PNG unless you need 16-bit precision on roughness (rare).

Substance Painter

Substance Painter exports ORM natively via the Godot export preset. Go to File > Export Textures, select the Godot preset, and you will see an ORM channel in the output list. The preset packs R=AO, G=Roughness, B=Metallic — exactly what Godot 4 expects. No manual compositing needed.

# Substance Painter Godot export checklist
[x] Preset: Godot Engine (or manual)
[x] ORM output: R=AO, G=Roughness, B=Metallic
[x] Albedo output: sRGB color space
[x] Normal output: OpenGL convention (NOT DirectX)
[x] Resolution: power-of-two (512, 1024, 2048)
[x] Format: PNG (lossless for import into Godot)

Important: Substance Painter uses DirectX normal maps by default. Godot 4 expects OpenGL convention (Y-axis flipped). Either flip Y in the export preset or enable Flip Normal Map Y on your Godot material — never both.

Importing and Configuring ORM Materials in Godot 4

Once your ORM PNG is in your project, Godot's importer needs the correct settings:

  1. Select the ORM texture in the FileSystem panel.
  2. In Import settings, set Compress Mode to VRAM Compressed — Godot will compress to BC5/BC7 on desktop and ETC2 on mobile.
  3. Do not enable sRGB for ORM textures — occlusion, roughness, and metallic data is linear. Importing as sRGB applies a gamma curve that destroys material accuracy.
  4. Keep mipmaps enabled for all 3D surface materials.

After import, create a new StandardMaterial3D, assign the ORM texture to all three PBR channels as shown in the GDScript above, and connect your albedo and normal maps in their respective slots. The complete material stack for a game-ready asset:

Four texture files. All PBR properties covered. This is the asset format expected by the BitSoul marketplace for compatible GLB game-ready packs.

Performance Tips and Common Pitfalls

Mip-map bleeding on ORM boundaries. When roughness transitions sharply across a UV seam, lower mip levels blend those values and create a visible band at distance. Pad your UV islands by at least 4 texels at 1024 resolution (8 texels at 2048) to eliminate this.

Metallic fringing on edges. If your metallic mask has anti-aliased edges, partially metallic pixels produce physically impossible results. Keep metallic maps binary — fully 0 or fully 1 — except for intentional worn-metal transitions.

AO too strong on direct light. Setting ao_light_affect above 0.0 makes crevices look unnaturally dark in direct sunlight. Keep it at 0.0 and let the shadow system handle direct occlusion.

Wrong specular on non-metals. The default metallic_specular of 0.5 (4% F0 reflectance) is correct for most plastics, stone, and wood. If your dielectric material looks too dull or too shiny, tweak this value rather than faking it with metallic values above 0.0.

# Quick validation: confirm channel assignments
func validate_orm_material(mat: StandardMaterial3D) -> void:
    assert(mat.ao_texture == mat.roughness_texture, "ORM: AO and Roughness must share texture")
    assert(mat.roughness_texture == mat.metallic_texture, "ORM: All three must share texture")
    assert(mat.ao_texture_channel == BaseMaterial3D.TEXTURE_CHANNEL_RED, "ORM: AO must read Red")
    assert(mat.roughness_texture_channel == BaseMaterial3D.TEXTURE_CHANNEL_GREEN, "ORM: Roughness must read Green")
    assert(mat.metallic_texture_channel == BaseMaterial3D.TEXTURE_CHANNEL_BLUE, "ORM: Metallic must read Blue")
    print("Material validated: ORM channels correctly configured.")

ORM textures pay dividends at every scale — whether you are shipping a mobile game with aggressive memory budgets or a desktop title targeting 4K. One bind, three properties, physically accurate results. Once it clicks, you will never go back to separate channel textures.

Ready to test your ORM-ready assets in a real game engine? Browse game-ready 3D models with pre-packed ORM maps at the BitSoul marketplace — every asset is vetted for correct channel packing and Godot 4 compatibility out of the box.


Need drop-in assets for this workflow? Grab the 98-model Character Pack on the BitSoul marketplace and drop them straight into your project.

Tags: characters

Skip the modeling — download it instead

A free BitSoul3D account gets you 2 GLB downloads every month for personal use plus 25 one-time AI Engine credits, no card required. PBR-textured GLB downloads with a full 3D preview before you buy, for Unreal, Unity, Godot or Blender — OBJ and 3D-printable STL come with any purchase or paid plan.

Browse 1,051 models — from $4.99 → or start free (2 downloads a month)