Adding/modifying shading models

Hello all,
I started poking around in Unreal Engine 4 a couple of weeks ago and I am finally starting to get a better grip of things. However, since I am mostly interested in low-fidelity graphics (think Quake 1 and Unreal 1) I was thinking that it would be more efficient to cut off some fat from the Default Lit shading model and add that as another option (would also be nice to not have to drag constant value nodes to Metallic, Specular and Roughness etc to get rid of the effects every time, but I guess you can make it once and the make a duplicate).

So my questions are;

  1. Is there any help to be had when it comes to these things? I have managed to find the shaders (\Epic Games\4.7\Engine\Shaders) and started to try and understand everything, but they seem very interconnected and not many comments which make them hard to read. I guess the simplest way to start is to examine the Unlit shading model, but some pointers would be nice.
  2. Is it useless to actually add another shading model? As in; is setting a constant value on unwanted properties as fast as removing them altogether?


I have actually just been poking around with creating my own shading model, and I haven’t really found any resources with any details on the process of adding a new one. I was going to possibly write up a short tutorial of what I had once I was sure it was working and cleaned up a bit.

I added a new shading model with a new output pin so I could put arbitrary data into the “custom data” buffer. Right now it is being used by clearcoat and the subsurface models, but I wanted to be able to put my own data there so I could try doing some fancy custom post-processing. Most of the code is about where you’d expect: HLSLMaterialTranslator.h, MaterialEditor.cpp, Material.cpp, MaterialShared.cpp, MaterialTemplate.usf, DeferredShadingCommon.usf, etc.

There’s some interesting behaviour to figure out like how HLSLMaterialTranslator.h seems to do string replacement on ‘%s’ instances in MaterialTemplate.usf to insert the material editor outputs into the shader. Which makes things a bit hard to follow until you find where the insertion is happening.

Where you pick the actual surface shading code seems to happen in ‘float3 SurfaceShading(…)’ inside the DeferredLightingCommong.usf. Most of the lighting models seem to call ‘StandardShading()’ while clear coat calls its own function. Here is where I think you could define your own shading model and have your own shading model call its own custom function.

To define your own shading model, first you add it to EngineType.h, and if you follow one of the other defines (say “MSM_SubsurfaceProfile”) you should be able to find all the places you need to add your new define to the code. In “MaterialShared.cpp” you add where it defines your shader model in the shader code (as something like “MATERIAL_SHADINGMODEL_SUBSURFACE_PROFILE”). In ‘DeferredShadingModelCommon.usf’ you give it a shading model ID (like “SHADINGMODELID_SUBSURFACE_PROFILE”), and then in “MaterialTemplate.usf” you give that shading model ID a specific color. There’s a bit of work here finding all the references to the other shading model defines and make sure yours is included in all the right places. It shouldn’t be too bad though I don’t remember all of them off the top of my head.

That’s a bit of run-down on what I’ve figured out so far though my custom shading model is still a bit of a work-in-progress. I’ve got it basically working and passing through data into the custom buffer (i.e. subsurface color). I didn’t want to change the lighting model at all so I didn’t actually write any new shader functions, but if you follow the lead of the existing functions in ‘DeferredLightingCommon.usf’, it should be relatively straightforward. Right now I am working on setting up a cross-hatching post-process effect with the custom data. I might write up a bit more of a tutorial at some point to help along anyone else who wants to do something similar, but that won’t happen until if/when I get some decent results.

Good luck! It’s actually not too huge of an undertaking, but it will be a bit of experimentation and lots of “Find All”.

Thank you for your reply!
Yeah, I pretty much found the same things as you in the actual code base, does not seem to hard to find all references and add new ones so that they turn up in the editor. But the part “Most of the lighting models seem to call ‘StandardShading()’ while clear coat calls its own function. Here is where I think you could define your own shading model and have your own shading model call its own custom function” seems really helpful, will have to check this out when I come home tonight!

Have you/anyone figured out the behaviour of “unwanted” properties (Metallic, Roughness etc) and the performance impact of these when using constant values?

I would expect that you simply save a texture fetch in the material editor generated part of the shader, and the rest of the shading happens as usual. If you use “Fully Rough”, UE4 will optimize out part of the shader for you already.

A custom shading model would let you write your own StandardShading() function in “DeferredLightingCommon.usf”. There are a few different functions in the BDRF.usf file you could try out (like Blinn). If you are always using a fixed Roughness (whether it’s 0, or 1, or 0.75) for a certain shading model, you could evaluate part of the shading function in advance to speed things up. You could even do a simple one-line “return Diffuse_Lambert( GBuffer.DiffuseColor );” plus check “Fully Rough” to really cut down the instruction count and go super old-school with no specular at all.

If you look in “BasePassPixelShader.usf”, I think you can find where Metallic and Roughness hook into some of the other parts of the shader. Metallic is used right away to do a lerp and a multiply so you could do an “#if MATERIAL_SHADINGMODEL_SIMPLE_[NON]METAL” and simplify those out if you know Metallic is always going to be either 1 or 0 for a given shading model.

So, yeah, I think there are a few places you could simplify things if you are interested in low-fidelity graphics, and defining your own shading model would let you do that without clobbering the engine’s existing shaders.