UE4 Consistency Shader in SubstancePainter/Maya
The Need
It is important for artists to work in a consistent environment no matter in the engine or DCC. Especially when the outsourced team usually doesn’t have the authority to get the project’s engine, they couldn’t test and check the final visual result in the engine after they finished the art assets. Therefore consistency between engine and DCC rendering is essential. Although DCC software usually comes with its own PBR default shader, the different engine has different render pipeline and the different project has its own shading strategy besides shading is different on mobile and PC, a custom consistency shader is needed.
To do that, it needs a well understanding of the engine shading pipeline, and I’ve already made a step by step disassembly of UE4 mobile basepass shader UE4 Mobile PBR Pipeline.
Substance Painter Unreal-like PBR Shader
Below is the output comparison of UE4 Mobile PBR and my SP PBR.
Roughness 0 to 1. Metal top, nonmetal bottom. UE4
Roughness 0 to 1. Metal top, nonmetal bottom. Substance Painter
Applied on the asset of PUBG Mobile project, in UE4 and SP.
SP Shader API
Firstly, go to Substance Painter website to see their Shader API.
As well as the PBR Metal Rough shader that usually is taken as the default PBR shader in SP.
Generally, SP makes all their lib and functions in the Shader API, and there is no any actual .glsl files in directory anymore after 2018 version.
Parameters are set in comment format, which you can find in Paramters Page.
All the functions are in Libraries Page, where you can find BRDF functions, basic PBR parameters functions, samplers, etc.
SP PBR Metal Rough
Let’s see SP’s default PBR shader first:
In this shader, it fetch material parameters from the texture input, through sampling and some manipulations*, then feed to 5 ShadingOutput. The most basic rendering equation for computing the fragment color is: emissiveColor + albedo * diffuseShading + specularShading
.
Well, I want to rewrite the shader so I’ll just put my final result into the diffuseShadingOutput
and ignore others.
*You can find what the generateDiffuseColor
generateSpecularColor
getRoughness
getBaseColor
, etc. does in this page:Lib Sampler.
The differences between SP and UE4
The asset in the standard Lookdev scene in UE4 Mobile ES3.1
The asset in SP with default PBR shader
Very different, huh? So read the PBR-metal-rough shader above, lighting of SP is from the image-based lighting by envIrradiance() function, which return the irradiance for a given direction, the computation is based on environment’s spherical harmonics projection. As well as a pbrComputeSpecular() works in the specularShadingOutput
, which compute the microfacets specular reflection to the viewer’s eye. If I remove those two functions, the output is like:
While for UE4, it has at least a directional light, SH SkyLight, and image-based lighting (IBL), as well as some approximate calculation I talked in the last post UE4 Mobile PBR Pipeline. That’s quite different.
My SP Consistency Shader
fetch basic parameters
Starting at void shade(V2F inputs)
, fetch basic parameters from the meterial textures:
I have all basic parameters I need now, let’s follow the pipeline of UE4 mobile basepass pixel shader. First I need ShadingModelContext.SpecPreEnvBrdf, ShadingModelContext.SpecularColor, ShadingModelContext.DiffuseColor. In SP glsl, they will be:
Lighting.Diffuse, Lighting.Specular
The second part we need is Lighting.Diffuse and Lighting.Specular:
Just copy CalcSpecular() function from UE4 MobileShadingModels.ush
file and MobileGGX.ush
. Implement in SP:
Next, add them on Color
:
SpecularIBL
For the sampling of IBL in SP, if I go back to see UE4’s codes of sampling the cubemap and delete all the macros but just see the actual lines:
The operation here is to sample the cubemap, RGBMDecode it and power it. There are parameters such as ProjectedCaptureVector
, AbsoluteSpecularMip
, MaxValue
, which are hardly able to get in SP, I just ignore them. It might affect the final result accuracy, but we just can’t make it 100% the same, since the environment in engine is also not absolute. In my SP code, I used SP’s function pbrComputeSpecular
from the lib but deleted the computeLOD
part, leaving the envSampleLOD
:
Add on Color: Color += SpecularIBL * SpecularColor;
So far I got:
SkyDiffuseLighting
SkyDiffuseLighting
is the indirect irradiance from the skylight. So I just use SP’s function envIrradiance() from the lib.
Add on together in addition with AO,
ToneMapping
The purpose of the Tone Mapping function is to map the wide range of high dynamic range (HDR) colors into low dynamic range (LDR) that a display can output. The Filmic tonemapper that is used in UE4 matches the industry standard set by the Academy Color Encoding System (ACES) for television and film. With Unreal Engine 4.15, the Filmic tonemapper using the ACES standard is enabled by default, you can’t shut it down. Which means, if I want the color in SP shows almost same with UE4, I need to apply an ACES tonemapping.
Where we can see that before tone mapping, the light area has no detials.
This is my ACES tonemapping function that referenced from ACES Filmic Tone Mapping Curve by Krzysztof Narkowicz, and make a change on e
which is the toe strength, to 0.53 that used in engine.
After tonemapping, the output is:
Comparison between UE4 and SP in ES3.1
Maya Unreal-like PBR Shader
With same approach, it’s easy to develop an Unreal-like Maya PBR shader as well. I’m not gonna talk it in details, as it is basically just transform the SP glsl shader above into hlsl.
NPR Consistency Shader
Cel shader cross platform. In UE4, SP, Maya, from left to right.