Skip to content

Roughness to Smoothness Converter for Unity (Invert Channel)

Unity's metallic workflow reads smoothness, most texture tools write roughness. Inverting is simple arithmetic, but only if the bytes survive: the Texture Lab reads the PNG itself, so the saved plane is exactly 255 − roughness.

Unity's metallic workflow reads smoothness, while glTF, Godot and most PBR texture sources give roughness. Unity's own shader library defines perceptual roughness as 1 − smoothness, so the conversion is an exact inversion: smoothness = 1 − roughness, which is 255 − value in an 8-bit PNG (roughness 80 → smoothness 175). Do it on linear data and put the result where Unity reads it — the alpha of the URP / Built-in metallic map or of the HDRP mask map. Nerulio inverts the channel byte-exactly in the browser (measured); Unity import was not run.

Drop texture maps (PNG keeps exact channel bytes)
Runs in your browser. Files are not uploaded.
Texture Lab: a normal map generated from a CC0 texture, with the OpenGL/DirectX convention shown
The real Texture Lab with a CC0 asset (ambientCG).
Outputs
  • Channel PNGsMeasured· numpy
  • Normal mapMeasured· numpy
  • Packed mask / ORMMeasured· Pillow
  • Engine importUNVERIFIED

Roughness, smoothness and why gamma matters

Both maps describe the same thing from opposite ends: roughness 0 is a mirror, roughness 1 fully rough; smoothness 1 is a mirror. In Unity's render-pipeline shader library, PerceptualSmoothnessToPerceptualRoughness returns 1 − smoothness, and only afterwards is the value squared (PerceptualRoughnessToRoughness). Both maps store the perceptual value an artist paints, so you invert — you do not square or take a square root.

Inversion is exact only on linear data. The bytes of a roughness map are numbers, not colours. If a map is treated as sRGB anywhere — a colour-managed editor or an importer with sRGB on — the values move: a smoothness byte of 175 means 0.686 as linear data but about 0.429 when decoded as sRGB. So import the result with sRGB (Color Texture) off, and do not "correct" it with a gamma curve.

Where the smoothness goes. URP Lit takes it from the alpha of the Metallic Map (Smoothness › Source Metallic Alpha, the default) or from the base map's alpha; URP's channel-packed texture and the Built-in Standard shader's metallic map both hold it in A; HDRP's Mask Map holds it in A. A lone grey smoothness PNG is only useful for a custom shader or for packing.

A gloss or glossiness map already is smoothness. Nerulio's name rules classify gloss as smoothness for that reason; inverting it would give roughness back.

Roughness
0 = mirror-smooth, 1 (255) = fully rough; the glTF, Godot and Unreal convention.
Smoothness
Unity's inverse: 1 − roughness.
Perceptual roughness
The painted value; engines square it before lighting.
Linear (non-colour) data
Values used as stored, without sRGB decoding.

Example: the numbers

Four roughness bytes, inverted exactly as the Lab writes them:

roughness byte   roughness   smoothness = 1 − r   smoothness byte (255 − v)
  0              0.000       1.000                255
 80              0.314       0.686                175
128              0.502       0.498                127
255              1.000       0.000                  0

Same byte 175 imported with sRGB (Color Texture) on:
175 / 255 = 0.686 → sRGB decode → 0.429   (a noticeably rougher surface)

Inverting twice returns the original bytes, so the conversion loses nothing. The Lab measured it on the downloaded file: the saved plane equals 255 − roughness at every texel.

From your roughness to Unity's smoothness

You haveIn NerulioIn Unity
A grey roughness PNGChannels: Invert the channel labelled roughness, save itA grey smoothness map (for packing or a custom shader)
Roughness + metallic PNGsMask packer, preset "Unity metallic + smoothness": R ← metallic, A ← roughness invertedURP / Built-in: Metallic Map, smoothness from Metallic Alpha
Roughness + metallic + AO (+ detail)Mask packer, preset "Unity HDRP mask": R metallic, G AO, B detail or 0, A roughness invertedHDRP Lit: Mask Map
An ORM texture (roughness in G)Channels, layout ORM: Invert G, save; then pack it into AAlpha of the metallic map or mask map
A gloss / glossiness mapNothing to invert: it is already smoothnessPack it into A as it is

What it does

  • Channel invert that writes exactly 255 − v (checked with numpy on the downloaded file).
  • Bytes under fully transparent texels kept, which a browser canvas would lose.
  • Channel preview with its meaning in Unity HDRP (mask map) or URP (metallic + smoothness in alpha).
  • Continue in the packer: smoothness goes into alpha of the URP metallic map or the HDRP mask map.

How to do it

  1. Drop the roughness map (PNG).
  2. Invert the channel.
  3. Download the smoothness map, or open the mask packer.
  4. Pack it with metallic (and AO for HDRP) using the Unity preset.

Files you get

FileWhat it is for
rock_roughness-g-inverted-roughness.pngThe inverted plane as an 8-bit grey PNG. The name keeps the source role (roughness) with inverted- in front, so its content is smoothness.
rock_metallic-mask.pngFrom the mask packer: metallic in R and the inverted roughness in A (plus AO and detail for HDRP), ready for the Unity material.

Put the smoothness where Unity reads it

Unity steps follow the Unity 6 / HDRP 17.1 documentation; Nerulio did not run the import.

  1. Open the mask packer, drop the metallic map first and the roughness map second, and choose "Unity metallic + smoothness" (URP / Built-in) or "Unity HDRP mask" (then map AO to G and set B to 0 if you have no detail mask).
  2. Tick Invert on the A channel and download the packed PNG. For URP with ambient occlusion, switch to Custom and put the AO file in G.
  3. In Unity, select the texture and turn off sRGB (Color Texture); for HDRP also set Texture Type to Default.
  4. URP Lit: Workflow Mode Metallic, assign the texture to Metallic Map (and Occlusion Map if G holds AO), keep Smoothness › Source on Metallic Alpha.
  5. HDRP Lit: assign it to Mask Map under Surface Inputs; the Smoothness Remapping slider rescales the range if the result needs tuning.

Check the result

  • Unpack the packed PNG in the channel unpacker: A should read 255 − roughness everywhere (a texel of roughness 80 reads 175).
  • In Unity, compare a known rough area (dull, broad highlight) with a polished one (sharp highlight). Reversed means the channel was not inverted, or was inverted twice.
  • Check the texture's Inspector: sRGB (Color Texture) must be off for the packed map.

Troubleshooting

SymptomLikely causeHow to checkFix
Everything looks wet or mirror-likeRoughness went into alpha without inversionUnpack A and compare it with the roughness map: identical instead of invertedTick Invert on A and pack again
Surfaces look rougher than in the texturing appThe packed map is imported as sRGB (175 is read as about 0.429)Unity Inspector: sRGB (Color Texture) is onTurn it off; do not apply a gamma curve to compensate
The smoothness map has no effectSmoothness › Source is Albedo Alpha, or a tool saved the PNG without its alpha channelMaterial Inspector; unpack the PNG and check that A is not constant 255Set Source to Metallic Alpha, and save packed maps as RGBA PNG
A gloss map ended up rough where it was shinyA gloss map is already smoothness and was invertedThe source file name contains gloss or glossinessPack it without Invert
URP material darkens in ambient lightThe packed texture is also assigned to Occlusion but G was written as 0Unpack G: constant 0Put the AO file in G with Custom mapping, or leave Occlusion Map empty

Other ways to do it

OptionWhen it is the better choice
Invert in an image editorA grey 8-bit roughness map in an editor that does not colour-manage it: Invert gives the same 255 − v; you still have to pack it into alpha yourself.
Shader Graph (Unity)You would rather keep the roughness texture and invert in the shader; Unity's URP page points to Shader Graph for layouts other than the default packing.
Channel unpackerThe roughness is inside another packed texture (an ORM's G) and has to come out first.

Outputs and how each was checked

"Measured" means the downloaded file was opened again outside the page (Pillow, numpy or an independent parser) and the property was measured. Loading these files in a game engine was not tested.

TargetFilesCheck
Channel PNGs<name>-r/g/b/a.png (8-bit grey)Measured numpyByte-identical per channel, including RGB under fully transparent texels.
Normal mapPNG · OpenGL (+Y) or DirectX (−Y)Measured numpyDecoded vectors are unit length (worst deviation 0.0002); OpenGL and DirectX differ in green only.
Packed mask / ORMUnity HDRP mask · URP metallic · ORM (glTF, Godot)Measured PillowPacked bytes checked: ORM preset over AO 10 / roughness 80 / metallic 220 → (10, 80, 220, 255).
Engine importUnity · Unreal · GodotUNVERIFIEDNot imported in any engine.

Evidence

Channel unpack byte-identical on all four channels (max difference 0), 2048 transparent texels keep their RGB; edge bleed changed 448 texels, all with alpha 0, and no alpha byte; OpenGL ↔ DirectX differ exactly in green (255 − g); converted normals decode to unit vectors.

Limits

  • Exact channel bytes need PNG; JPEG/WebP are decoded by the browser and marked "not byte-exact".
  • Interlaced PNG and 16-bit output are not supported; up to 33.5 million pixels on the exact path.
  • A normal map's convention cannot be detected from its pixels, only converted.
  • Nothing was imported into Unity, Unreal or Godot: channel orders rest on their documentation and engine source.

Versions and sources

Measured on the Texture Lab (docs/TEXTURE-LAB.md): inverting G of a roughness map and downloading it gave a plane equal to 255 − roughness exactly, and the stage showed the in → out pair; the packer's inverted-R check gave (245, 80, 220, 255) from an AO of 10. The 1 − smoothness relation is from Unity's render-pipeline source; channel locations and import settings are from the Unity pages below. Unity import was not run.

Official documentation used

Questions

Which channel does Unity read smoothness from?

URP metallic: alpha of the metallic map. HDRP mask map: alpha too (R metallic, G AO, B detail mask). Both orders come from Unity's documentation, quoted in the Lab.

Was it imported into Unity?

No. The bytes were checked; import into Unity was not run here.

Is it free, and do I need an account?

Everything on these pages is free and works without an account. There is no watermark and no export that is held back.

Are my sprites uploaded?

No. The Studio decodes, packs and exports in this browser tab. Projects are autosaved in the browser (IndexedDB) and can be saved as a .nerulio file.

Does it need Aseprite or an engine plugin installed?

No. .aseprite files are read and written in the browser, and each engine bundle carries what that engine needs (for Godot, the import settings and scene; for Unity and the Godot TileSet, a small editor script).