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.

- 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 have | In Nerulio | In Unity |
|---|---|---|
| A grey roughness PNG | Channels: Invert the channel labelled roughness, save it | A grey smoothness map (for packing or a custom shader) |
| Roughness + metallic PNGs | Mask packer, preset "Unity metallic + smoothness": R ← metallic, A ← roughness inverted | URP / 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 inverted | HDRP Lit: Mask Map |
| An ORM texture (roughness in G) | Channels, layout ORM: Invert G, save; then pack it into A | Alpha of the metallic map or mask map |
| A gloss / glossiness map | Nothing to invert: it is already smoothness | Pack 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
- Drop the roughness map (PNG).
- Invert the channel.
- Download the smoothness map, or open the mask packer.
- Pack it with metallic (and AO for HDRP) using the Unity preset.
Files you get
| File | What it is for |
|---|---|
rock_roughness-g-inverted-roughness.png | The 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.png | From 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.
- 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).
- 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.
- In Unity, select the texture and turn off
sRGB (Color Texture); for HDRP also set Texture Type toDefault. - URP Lit: Workflow Mode
Metallic, assign the texture to Metallic Map (and Occlusion Map if G holds AO), keep Smoothness › Source onMetallic Alpha. - 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
| Symptom | Likely cause | How to check | Fix |
|---|---|---|---|
| Everything looks wet or mirror-like | Roughness went into alpha without inversion | Unpack A and compare it with the roughness map: identical instead of inverted | Tick Invert on A and pack again |
| Surfaces look rougher than in the texturing app | The packed map is imported as sRGB (175 is read as about 0.429) | Unity Inspector: sRGB (Color Texture) is on | Turn it off; do not apply a gamma curve to compensate |
| The smoothness map has no effect | Smoothness › Source is Albedo Alpha, or a tool saved the PNG without its alpha channel | Material Inspector; unpack the PNG and check that A is not constant 255 | Set Source to Metallic Alpha, and save packed maps as RGBA PNG |
| A gloss map ended up rough where it was shiny | A gloss map is already smoothness and was inverted | The source file name contains gloss or glossiness | Pack it without Invert |
| URP material darkens in ambient light | The packed texture is also assigned to Occlusion but G was written as 0 | Unpack G: constant 0 | Put the AO file in G with Custom mapping, or leave Occlusion Map empty |
Other ways to do it
| Option | When it is the better choice |
|---|---|
| Invert in an image editor | A 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 unpacker | The 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.
| Target | Files | Check |
|---|---|---|
| Channel PNGs | <name>-r/g/b/a.png (8-bit grey) | Measured numpyByte-identical per channel, including RGB under fully transparent texels. |
| Normal map | PNG · OpenGL (+Y) or DirectX (−Y) | Measured numpyDecoded vectors are unit length (worst deviation 0.0002); OpenGL and DirectX differ in green only. |
| Packed mask / ORM | Unity 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 import | Unity · Unreal · Godot | UNVERIFIEDNot 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).