Over the last few months I've been working to push my modelling and texturing skills. I've been practising on assorted projects in Blender 3D and Unity 5 (using Blender models). While searching for better tools to improve one of my weakest skills, texturing, I came across Allegorithmic's Substance Painter. It has quickly become an essential part of my workflow. I love the toolset; It's quick, it's well laid out, it has some robust generators and effects, and it feels familiar to anyone who has used Photoshop in the past. My biggest problem has been converting Substance Painter's PBR Metal/Roughness maps into materials within Blender's shader nodes. For every project I was basically creating a new node tree, playing with it until I got something that looked relatively pretty in Cycles, but was often inconsistent to what I was seeing in Substance Painter. This week, I finally made a single simple nodegroup which takes Substance Painter's PBR maps and turns them into Blender Shaders. All you have to do is append the nodegroup into your project, plug in your maps, and that's it! Let's take a look at its construction!
What is the PBR Workflow?
The first step is of course to understand the PBR workflow, and what the maps from Substance Painter's Metal/Roughness output are used for.
For a PBR Metal/Roughness workflow, there are 4 main maps that you are concerned about:
How do we implement PBR in Blender?
In order to take advantage of PBR maps (in this case specifically Substance Painter's Metal/Roughness PBR maps), we must create a material node tree which implements the maps using the above concepts. Let's break down the node tree above and explain how it works.
![]()
INPUTS
We have our 4 main input maps: our Base Colour, Roughness, Metallic, and Normal maps. We then also have value inputs for our Dielectric Reflectance, and our Normal Strength, which I will explain later. These inputs will comprise all of our shaders, their masks, and their values. Very few inputs, very nice results. Simple and clean. ![]()
NON-METAL SHADER
The first section of our shader is the Non-Metal portion. This is the section which would be black on our metallic map, and would be the sections of the map for most items: papers, plastics, rubber, wood, etc... This section of the shader is composed of 4 main parts: A Diffuse BSDF node, a Glossy BSDF node, and a Mix Shader node, the mixing of which is controlled by a Layer Weight node. Our Diffuse BSDF node (labelled in this example as "Base Colour") takes the Base Colour input for its 'color', and takes the Roughness input* for its 'roughness'. Then, we need the shiny parts of our non-metal shader, so we take a Glossy BSDF (labelled "Non-Metal Gloss"), and we give it our Roughness input (we leave this shader's 'color' as white). Finally, we're going to use a mix node to combine these shaders; Diffuse on the top input, Glossy on the bottom. However, we don't just want these to be 50-50 glossy and diffuse. We want this to be a dielectric setup, with slight gloss around the edges using the Fresnel algorithm to calculate the path of the light rays. For this, I use a Layer Weight node, and plug the 'Fresnel' output into the 'Factor' input of our Mix Shader. I give the 'Blend' value the input from our Shader "Dielectric Reflectance", this way I can change how strong the Fresnel effect is from outside the group, and per material. If you read the PBR guides above, you'd have found out that most non-metals sit around .04-.08; I set my node to default to .08 because I just happen to like shiny renders, though I do customise this value per object. And that's it! If you never planned to make a metallic object, you could technically stop there, add the normal map, and then you'd be done. But we're going to continue and add the metal shader as well, and mask between them. * You might notice that the roughness is being pushed first through a node labelled "Convert Roughness". The maps we get out of Substance Painter aren't as strong as we need for Blender, so we put the Roughness input through a math node first, to the power of 2, and then we can plug it in as needed. ![]()
METAL SHADER
Metals react differently to non-metals in the way that they reflect light and colour, so we handle them separately. You can, as always, learn more about this difference from the PBR guides above. This section of the shader is made up simply of a Glossy BSDF node and a Mix Shader node, controlled by a new Layer Weight node. Metals absorb certain frequencies of light and reflect the colour you observe them as, and are much shinier than non-metals, so our Glossy BSDF (labelled "Metallic Gloss") actually takes our Base Colour input for it's 'color', and again it takes our Roughness input for its 'roughness' (as above, run through the power of 2 conversion*). This goes into the bottom of our Mix Shader (labelled "Metallic Mix"), and our Non-Metal Shader output goes into the top. For our 'Factor' we are going to be taking another Layer Weight node with the 'Fresnel' output, but this time, the 'Blend' value is controlled by our Metallic map. You could plug the Metallic map directly into the 'Blend' but I personally felt it wasn't strong enough, so I added another Math node (labelled "Increase Metallic") and told it to multiply the value of the input map by 2. By using a multiplication operation, you maintain your dielectric regions (0*2 = 0), while strengthening your metals. You're free to play with this value as you see fit for your projects. ![]()
NORMAL MAP
The final map we need to address is our normal map. This is by far the easiest to implement, because all we need to do is add the "Normal Map" node, give it our map for its 'color' input, and give it our Normal Strength for its 'strength' input. Then we can adjust the strength per material, and from outside the shader.** ** I will note that I've found Normal Strength values of .5-.75 work best. Using the full strength of 1.0 seems to be too much in Blender, it just ends up looking distorted and messy. Play with the strength in your own projects until you're comfortable with how it looks. ![]()
OUTPUTS
We only need 2 outputs for this shader nodegroup: The shader itself, and the "displace" value which comes from the output of our normal map node. That's it! We're done! We've created the shader! Now all that's left to do is test this out on a model we've painted using Substance Painter... Testing the PBR Shader NodeGroup!Now that we've finished our shader nodegroup, let's plug in our maps, and see how this looks... If you like this set up, you can download the test scene from above. It includes my Space Hauler model, its texture maps from Substance Painter, the environment texture I used for the reflections in the previews***, and of course the shader nodegroup.
I'm quite pleased with the results myself. I've tried this on a number of models, and I plan to append this nodegroup into all my projects from now on. Once I get my maps out of Substance Painter, this shader set up means I can simply plug my maps in and focus my time in Blender more on the composition of the scene, rather than fiddling with shader nodes time and again with different innaccurate results every time. I like having an elegant and fast solution like this which covers basically every use case, with the exception of course for more niche materials like emissive or transparent objects.
If you use my nodegroup in your works, please leave me some credit (even something as simple as "Shader by @BlackhartFilms" in the description text). I hope this overview has been useful, and I look forward to seeing how people use this technique! Happy Blending! ![]() Blender PBR Shader by Jeffrey Hepburn is licensed under a Creative Commons Attribution 4.0 International License. *** HDRI Environment Maps by Greg Zaal
8 Comments
Mike
3/12/2016 08:43:23 am
Hello,
Reply
Blackhart
3/12/2016 11:07:16 am
Yes, if you're using PBR metal/rough outputs from Substance Designer, they are PBR maps, you can save them out of Substance Designer. I use Designer for most of my large environmental objects such as ground textures, walls, floors, etc...
Reply
Mike
3/12/2016 09:17:06 am
Hello again,
Reply
Blackhart
3/12/2016 11:09:29 am
Have you made sure that in the node-group the normal map node takes the normal map from the input, and plugs into the displace for the output? And that outside this nodegroup that your displace output connects to the displacement input on the shader out?
Reply
Mike
3/12/2016 11:30:50 am
Thats was the mistake. Now i have a bump-effect, but at some sides of the cube it seems, the normal is inverted. I have here three screenshots in the dropboxfolder. on picture "correct" it looks good, at the other pictures you see the "reverse" side of the cube and a comparison. why is this?
Paulodgn
4/20/2016 07:28:51 pm
Thank you so much for this its works great. Ive been trying to this for a couple of weeks with not very good results. Great job!
Reply
eknightGER
5/14/2016 09:41:40 am
Thank you for this - I just read Allegorithmics PBR guide and basically came up with the same shader group (although I am playing with the height maps in addition to the normal maps, I see where that goes :-). But seeing you play with the strengh of the maps input (eg. power 2 and multiply 2) is a very good idea, that could solve my issues of the effect's strength. One idea on the dielectric could be to assign different PBR materials to the object in Blender that plays with the fresnel value driven by a texture map or vertices based material assignment.
Reply
eknightGER
5/22/2016 05:26:22 am
I have played with the node set-up a bit as I found that using normal map input into displacement gave me a number of small micro-artifacts on the painted surface. So I tried using the exported height map from Substance painter instead and put it into a bump node and that into the normal inputs of all the shaders. I found that this delivered an improved result for me. Probably something worth exploring further.
Reply
Your comment will be posted after it is approved.
Leave a Reply. |
AuthorMy name is Jeffrey Hepburn, and I'm a young writer, graphic design artist, and aspiring filmmaker. Categories
All
Archives
June 2016
|