Tag Archives: PBR Workflow

Bitmap2PBR Official Release

Bitmap2PBR Offically is available for public download!

This project began when I was converting my models into PBR setups for real-time rendering. I also needed a way to have the models appear the same when converting to other 3D packages and this was the best way to achieve consistency. After using it on a few projects Ive decided that it could be a useful tool for other artists as well and so Its now officially available for download.

You can see the promo video below

Download is available at the bottom of the page in the Store location.

Substance Designer to Vray/Corona


Lets talk Physically Based Shading for Vray & Corona. I wrote this short guide mostly as future reference for myself, hopefully you find this useful as well. Authoring or using PBR textures can be a little confusing at first when you are provided many textures – and this break down should help understand whats happening and how to use them correctly. I will keep this as basic as it needs to be using template converters such as BaseColor/Metallic/Roughness Converter within Substance Designer, this is so you can output the textures you need correctly for your workflow and give you an understanding of what they are doing. I will put more technical explanations at the bottom of this article.


Lets start with my desired outcome – I want to create textures that will work in Vray and also Corona which match our Substance Designer Preview. I also want to set my scene up so it easily converts from Vray to Corona or visa versa.

Lets do this with something that seems reasonably simple: A Metal Copper Sphere (Conductor/Metal) with some light glossiness and some chunks removed to reveal the surface below (Dialectric).

Substance Designer Preview

I wont go into the details of how the Shader was made in Substance Designer as it was just a few generated maps – the important part is how we export them and bring them into 3dsmax.

Start a New Substance Graph Template

I advise you set up your initial graph with Metallic/Roughness as its easier to author for other uses.

Material Creation

Setup your material how you like. You can see below my setup is just some basic nodes which produce a base color, normal, roughness & metallic map.

Now you are ready to plug these into the converter template. From the bottom/left menu select Library tab, then Material Filters > PBR Utilities > BaseColor/Metallic/Roughness converter.

Make sure you change the Instance Paramters of this node from PBR Diffuse/Specular/Gloss to Vray(GGX).

You can then go ahead and plug in your nodes into the correct location for each input. Now right click on the BaseColor/Metallic/Roughness converter node and select create Output Nodes.

You are now ready to output your nodes to bitmaps/textures. At the top select the Spanner icon and select Export Outputs…

Lets open 3dsmax and set Vray to the render engine. Set up your HDRI for rendering (I used the one from Substance Designer so I can match the lighting/reflections).

Lets start with our basic input of texture maps and see the result. Only Diffuse & Reflection will have the Input Gamma as Automatic and the rest of your maps will require you to Override the Input to 1.0 as shown below.


  • The normal map generated is OpenGL format. So make sure you flip the green channel in your VrayNormalMap settings. (You can always author these as DirectX if you wish to bypass this step)

  • Unlock the Fresnel IOR in Reflect settings and place your IOR there.

Vray Render

Now when we right click > Corona Converter > START CONVERSION. We get the below, essentially the same result however a slight difference in the highlights on the bottom right normals which is due to the way Corona handles lighting. Never the less, result achieved with our material.

Alternatively, you can just swap the rendering engine from Vray to Corona without doing a conversion as Corona can use Vray Materials such as this without issues. Obviously if you intend on sharing files with someone who doesnt own Vray you will need to convert it first.

Corona Render

And thats it… almost.

Ok for those who have a keen eye, maybe you see something a little different between the Substance Preview and our final result… your eyes are not lying. Before I explain lets have a look at this same material authored for Vray using the Metallic approach (not available for Corona at the time of writing). You will notice that the metallic parts are a bit less saturated in the sky region, also the reflection looks different most notably at the glancing angle of the sphere. However the Metallic render looks much more like our Substance Preview.

Comparison Vray Metallic vs Vray(GGX)

This is due to energy conservation. Quoting the Vray Manual:

The Reflection should be set to white so as to get the proper reflectivity and preservation of energy; without this, the glancing angle will never be 100% reflective — which it should be.

As you can see from our Reflection Texture it contains the Copper Color so we are losing our fresnel in the metal area.

Reflection Texture

You could remedy this by using a Complex Fresnel and Mix it with the original Reflection Texture making sure the metal parts are receiving the Complex Fresnel (I used the inverted Metallic Mask as the Mix Amount). Complex Frensel is a free plugin by Siger Studios.

Complex Fresnel Material Setup

Complex Fresnel Render

However this can complicate things – and the whole point of PBR is to simplify things as well as drive your workflow towards using texture maps.

Pros of this workflow:

  • Easy to author for both Metallic and Vray(GGX).

  • Can easily convert from Vray to Corona and visa versa.

  • Dialectric’s work as expected in terms of fresnel.

  • Can control the fresnel IOR (more information about how at the bottom of this article).

Cons of this workflow:

  • Metals become difficult to manage fresnel due to the energy conservation issue.

Vray Render – Metallic

Make a duplicate of your graph and remove the Outputs and Converter node, name this graph differently.

Create a new node from Library > Material Filters > PBR Utilities > Base Material

You should now have a Base Material node with no inputs. To enable the inputs, on the right side panel select Instance Paramters > User-Defined Maps and enable the ones you intend on using.

Now you can plug in your nodes into the inputs of the Base Material and then right click and select Create > Output Nodes. Then export as we did before.

We can now plug in our textures into a 3dsmax material. Create a new Material in 3dsmax, this time we leave Reflect > Fresnel Reflection at default settings, change the BRDF to Use roughness, change the Reflect color to white. Just be aware of which format you created your Normal in Substance, if you created this as an OpenGL Normal then you can do as we did before and flip the green channel. If you authored it as DirectX Normal you can leave it unchecked.

Pros of this workflow:

  • Easy to author for real-time and use with render engines that support Metallic workflow.

  • Less textures required.

  • Metals are easy to manage.

Cons of this workflow:

  • Cant convert from Vray to Corona or other render engines that dont support metallic textures.

  • Fresnel IOR locked at 1.5 for dialectrics and off for metals. This however may not be such a bad thing, if you have an interest in why read down below.

Ok thats it basically, if you want to know more technical stuff, lets go down the rabbit hole.

Down the Rabbit Hole

Glossiness/Specular Workflow

Ok so we covered so far:

  • Creating a Metallic material.

  • Converting this to a Vray(GGX) material.

We are now going to look at the Glossiness/Specular workflow. The way this differs from the Vray(GGX) method is that the IOR and Reflection is created in the one texture. Duplicate your graph and rename it, we will take our previous setup and create a new BaseColor/Metallic/Roughness converter and leave the defaults as Target: Diffuse/Specular/Gloss.

Now in 3dmax we can use our previous setup for the Vray(GGX) and change a few settings. Disable Fresnel reflections, remove the IOR Texture and place our Specular Texture in the Reflection slot.

I won’t be going into pros/cons for this workflow, as its evident from our previous examples we dont have white in our reflection channel, thus our falloff/reflections will not match our reference. Its also easy for artists to accidently break energy conservation when manually adjusting the Specular Color or Level without a reference chart.

Specular Color & Specular Level

Specular Color:

This is the color that is used to determine the fresnel and color of the reflections. We can see this baked into our Specular map along with the metal color. You can change this color using a built in node within Substance Designer called PBR Dialectric f0 and set a Custom Fresnel – which for this example we will use a fresnel of 1.5. Right click on this node and create an Output.

Now we want to blend our Base Color with the Specular Color using our Metallic mask.

As you can see we have the same result as earlier, however now we can set a Custom IOR from the drop down list in our PBR Dialectric > Instance Parameters > Specular f0 > Custom IOR.

The formula to convert Fresnel IOR to Specular Color is:

IOR = 1.5
(((IOR-1.0)**2) / ((IOR+1.0)**2))**0.4545 

Specular Color = 0.231540 (rounded down)

This is what the node graph looks like in Substance Designer for the above formula. You can also inspect this graph by selecting Open Reference > PBR Dialectric f0 > Uniform Color (bottom left) > Edit Opacity (function).

Specular Level:

This is what goes into the Use SpecularLevel Input area in Substance Designer when using Glossiness/Specular workflow.

Now, as mentioned in The PBR Guide most dialectrics fall between 0.04 and 0.08 linear (4-8% Reflectance at f0). For those more familiar with Fresnel values this is between 1.5 and 1.79.

0.04 Reflectance turns out to be 0.5 Specular Level. This is useful if you want to change the dialectric fresnel value in the Glossiness/Specular workflow. When you plug a 0.5 linear Greyscale node (Uniform Color) into the Specular Level you should get the same result as you get without a Specular Level input – because the default Reflectance is 0.04 (or fresnel 1.5).

Once this gets converted using the formula within the BaseColor/MetallicRoughness converter node, it should display in your IOR map as 0.666667 for the dialectric area, metals will always be .000992.

You can now change this linear Specular Level to suit whatever fresnel you require. I found it difficult to find a chart of common Specular Levels so I built an expression node to control the Output Color of the Greyscale node to use an input Fresnel Value which will return a Specular Level. You could use the Python script I created at the bottom of this article to give you the exact number you require.

The formula to convert Fresnel Value to Specular Level is:

IOR = 1.5
((IOR-1.0)**2) / ((IOR+1.0)**2) / .008

Specular Level = 0.5

To show you how you can set this up for yourself in Substance Designer select your Greyscale Node > Output Color > Expose (function). You can then set a name like ior_value.

The function button will then turn blue and you can left-click on it to enter the function graph. You can then add the following nodes and set the last Divide node as Output (right click).

You now have an easy way to change the Fresnel Value and have the SpecularLevel/IOR map update automatically within Substance Designer. This ior_value you exposed before will be available in the main menu when you double click on an empty area > Input Parameters > ior_value > Default. Set the Default to 1.5 and you will see your Greyscale Linear Value change to 0.5 and your IOR dialectric texture change to 0.666667 (in the grey area). Obviously feel free to experiment with other fresnel values.

So was it all worth it? Lets have a look at some comparison renders.

This is the range where most dialectrics sit, averaged out to 3 images for simplicity. Visually there is not much of a difference which is why the PBR standards are hard coded to 1.5 Fresnel taking some of the technical work out for the artist and relying more on your other inputs such as roughness, bump, height etc – where you get the most “bang for your buck”.

Workflow Tips:

HDRI Vray Materials

Some people prefer to work with VrayHDRI instead of standard bitmaps. The process is the same however your images will default to linear (gamma 1.0) on import. This can save you a few clicks. Its also nice to see the color space of the image directly without having to check manually. You can override the gamma to 2.2 (automatic for standard 3dsmax bitmaps) for Diffuse/Reflection just by changing the color space to sRGB or leaving the default space as Inverse and change the Gamma to 0.4545 – this will have the same outcome.

Just note that you may have a difficult time converting this to other render engines if they dont support VrayHDRI in their converter.

Python Scripts

Fresnel IOR to Value:

James Vella 2020

This script takes the Fresnel value and gives you the Reflectance at f0 value,
1/IOR, Specular Level, Specular Range, and PBRDialectric f0 value for the 
Specular Color. Make sure you replace the FRESNEL value.

from math import sqrt

FRESNEL = 1.5                                                     # Replace this with the Fresnel Value

Reflectance  = ((FRESNEL-1.0)**2) / ((FRESNEL+1.0)**2)            # Formula for converting Fresnel to Reflectance at f0.
RemappedIOR = ( 2.0 / (sqrt(Reflectance)+1.0)) -1.0               # Formula for converting Reflectance to 1/IOR.                                    

SpecularLevel = Reflectance / 0.08                                # Convert Fresnel to Specular Level value
SpecularRange = (FRESNEL-1.0) / (FRESNEL+1.0)**2                  # Forumula for Specular Range Value usually between 0-8% 
PBRDialectricf0 = Reflectance ** 0.4545                           # Find The PBR Dialectric f0. This is the value for the Specular Channel.

print("The Fresnel value is: ", FRESNEL)
print("The Reflectance at f0 linear value is: ", round(Reflectance,6))
print("The 1/IOR RGB linear value is: ", round(RemappedIOR,6))
print("The Specular Level linear value is: ", round(SpecularLevel,6))
print("The Specular Range linear value is: ", round(SpecularRange,6))
print("The PBRDialectric f0 for the Specular Color Channel is: ", round(PBRDialectricf0,6))

This Prints:

The Fresnel value is:  1.5

The Reflectance at f0 linear value is:  0.04
The 1/IOR RGB linear value is:  0.666667

The Specular Level linear value is:  0.5
The Specular Range linear value is:  0.08
The PBRDialectric f0 for the Specular Color Channel is:  0.231545

Reflectance to Fresnel:

James Vella 2020

This script will take a Reflectance value at f0 (for example 0.04) and turn it into a Fresnel value (1.5). 
It also gives you the remapped 1/IOR value. 
Make sure you replace the REFLECTANCE value.

from math import sqrt

REFLECTANCE = 0.04                                          # Replace this with the Reflectance at f0.

RemappedIOR = (2.0/(sqrt(REFLECTANCE)+1.0))-1.0             # Formula for converting Reflectance to 1/IOR.
IORtoFresnel = 1/RemappedIOR                                # Calculate IOR value back to Fresnel value.

print("The Reflectance at f0 linear value is: ", REFLECTANCE)
print("The Fresnel value is: ", round(IORtoFresnel, 3))
print("The 1/IOR RGB linear value is: ", round(RemappedIOR, 6))

This Prints…

The Reflectance at f0 linear value is:  0.04

The Fresnel value is:  1.5
The 1/IOR RGB linear value is:  0.666667