Writing shaders for UE4; where do I start?

First of all I would like to specify that the node-based material editor is not what I’m looking for. I have good knowledge of how to write shaders and compute shaders and the power and flexibility that this brings over the node editor is just unmatched. Having an engine as amazing as UE4 with no way of writing shaders seems like a waste. There are shaders we cannot do with the node editor (proper toon shading, custom compute shaders), therefore we need to be able to write them.

So I’d like to know if there are any learning resources on how to write shaders for UE4. A simple hello world example of where to plug the shader into the engine and make it appear as an option for materials would suffice.

Of course I’m not expecting it to be as easy as in Unity where you just “Right click > Create Shader”. Someone from Epic on the twitch stream has already said that this would require too much work and it probably won’t happen (and that they feel really bad about that). But still, if there is a well-documented way of doing it by modifying the sources, I would be satisfied.

Lastly, I’ve tried reading this page over and over: Shader Development | Unreal Engine Documentation
But… it’s not helpful at all. It does talk about shaders, but doesn’t give any clear examples/instructions of how to create one and make it work from scratch. For instance, it says you have to create a “FLightFunctionPixelShader” class that inherits from “FShader”… but where do I put this class? Where do I call it? What do I put in it? etc, etc… A single short example would be more helpful than this entire page.

thanks

That’s a good question - there is a small amount of tutorials about shaders in Unreal Engine 4. (for example, I need simple geometric shader for screen distortion and I don’t know where to start)

I think you are looking for the custom expression node?? Custom Expressions | Unreal Engine Documentation

Do these custom expression nodes give you access to lighting information? Like if you want to make custom lighting models, for example

Nope, you can only get light information from within light functions and deferred decals. It is possible to fake it though.

//TraceScene -CMOT Float 3:
float muS = 0.0;
float3 lightPos = float3( 20.0+15.0sin(t), 15.0+12.0sin(t),-20.0);
float3 lightCol = (6000.0float3( 1.0, 0.9, 0.5));
float transmittance = 1.0;
float3 scatteredLight = float3(0.0, 0.0, 0.0);
float d = 1.0;
float material = 0.0;
float3 p = float3(0.0, 0.0, 0.0);
float phfu =1.0/(4.0
3.14);
float dd = 0.01;
for(int i=0; i<numIter;++i)
{
float3 p = rO + drD;
float mu=CustomExpression1(Parameters,p,t);
float3 S = CustomExpression0(Parameters,p,t) * mu * phfu
CustomExpression2(Parameters,p,lightPos,t);
float3 Sint = (S - S * exp(-mu * dd)) / mu;
scatteredLight += transmittance * Sint;
transmittance = exp(-mu * dd);
d += dd;
dd=dd
1.015;
if(transmittance<0.001)
break;
}
return scatteredLight;

//EvaluateLight -CMOT Float 3:
float3 lightPos = float3( 20.0+15.0sin(t), 15.0+12.0sin(t),-20.0);
float3 lightCol = (6000.0float3( 1.0, 0.9, 0.5));
float3 L = lightPos-pos;
float distanceToL = length(L);
return lightCol * 1.0/(distanceToL
distanceToL);

//ParticipatingMedia -CMOT Float 1:
float geoFog = clamp(clamp((sin(pos.x)+sin(pos.y)+sin(pos.z))/3,0,1)-(-pos.z/10),0,1)*10;
float constantFog = 0.041;
float muS = constantFog + geoFog;
return muS;

//VolumetricShadow -CMOT Float 1:
const float numStep = 30.0;
float shadow = 1.0;
float muS = 0.0;
float dd = length(to-from) / numStep;
for(float s=0.5; s<(numStep-0.1); s+=1.0)
{
float3 pos = from + (to-from)*(s/(numStep));
float mu = CustomExpression1(Parameters,pos,t);
shadow *= exp(-mu * dd);
if(shadow<0.001)
break;
}
return shadow;

@xnihil0zer0
that is really cool (and unfortunate)

Do you know if this lack of lighting info access is due to complications in the engine, or is it just something Epic could easily add but hasn’t added yet?

I’m not super familiar with the inner workings of the engine, but I don’t see what’s preventing them from adding a “Lighting Data” node that is basically an array of light pos, rot, type, color, etc

If you get into the source code of the engine, you can create new lighting/shading models (or edit existing ones). I’m not at all familiar with that area, though, so I can’t give you any info beyond pointing you to the Engine/Shaders/ directory in the source.

Yeah, it’s because UE4 uses deferred shading.

So. I wouldn’t recommend custom material expressions , I would recomment integrating your existing shader/math knowledge (GPU gems book for example), in UE. Use custom material functions whenever possible (BP for easy stuff, C++ for more complex ones.) Mess around with already existing stuff to improve them , make your own handy shader library with stuff the engine hasn’t already got. Be sure to documentate in order to educate others about bugs and problems you face!

That certainly is my goal, but I was kinda hoping someone already did that and had info to share about it. I guess I’ll have to learn the hard, painful way.

That is a neat function above with the volumetric lighting. I was reading through it and realized that part of it could be simplified, and maybe that will help others understand it better.

Mostly these two lines:
float3 S = CustomExpression0(Parameters,p,t) * mu * phfu* CustomExpression2(Parameters,p,lightPos,t);
float3 Sint = (S - S * exp(-mu * dd)) / mu;

starting with the bottom one, if we have (S - S(x)), this can be rewritten as S(1-x). This is a bit easier to read since it implies we are applying a multiplier to an inverted density function. Also the term mu can simply be removed from both lines as it is simply being divided out. So those two lines become:

float3 S = CustomExpression0(Parameters,p,t) * phfu* CustomExpression2(Parameters,p,lightPos,t);
float3 Sint = S(1 - exp(-mu * dd));

btw this shader was a bit slow since it is doing 300 * 30 = 9000 samples of the density function. Something like this could be much faster using distance field shapes to accelerate the tracing.
Also terms like pfhu could simply be factored into your light color as another optimization to avoid more math at each sample.

, may I ask you if you have any tips for me regarding my question? You seem like you’d know something about developing shaders in UE4…

I have written modifications to the shading models but there is no straightforward place to drop in new shader code as options without editing the base engine code. It isn’t something I know enough about to write a proper tutorial on. I just kind of stumble my way from one breadcrumb to the next until things compile :slight_smile:

In general you could look at places like DeferredShadingCommon.usf or ShadingModels.usf to start to get an idea of how things are structured for the deferred rendering passes. You could for example just hijack one of the existing shading models and rewrite it as a first step since that will be fairly easy compared to finding all the places you need to hook up a new shading model from scratch, since to do that you need to have intimate knowledge of how every part of the gbuffer is packed. It is a lot to learn!

What exactly are you trying to do? For just authoring simple content shaders I would recommend sticking to material nodes, or the custom hlsl node for things that require for loops (or other execution flow that is impossible without hlsl code currently) until you are more comfortable with the engine.

Shader code extensibility is something we know we can do better at. At this time there are no plans to improve it but it is a known deficiency.

for just adding options you can find a lot of how the material stuff is set up in places like material.usf and materialtemplate.usf. Try searching for “tangent space normal” for example and you can see how that option is implemented as a branch in the material setup phase. And you can see how it simply injects some new hlsl code based on the option, which does the transform for you. Doesn’t get much simpler than that in terms of options.

Well, first of all, thanks for your reply. It’s the first time I’ve managed to get any kind of reaction from Epic staff on this subject, and you told me what I needed to know! I’ll look into the existing shaders and see what I can do.

I actually don’t really have specific shader-related plans right now. I just know that I’m a big fan of trying out new stuff with shaders, and that I use shader programming in most of my projects. So the whole shader situation with UE4 is a big turn-off for me and some of my game dev acquaintances.

I am mostly a Unity user, and Unity has a tremendous amount of problems/weaknesses, which is why I’m always looking for alternatives. But Unity does have great support for shader development. So I’ve constantly been torn between Unity and UE4 ever since it came out, and UE4’s rigidity with shaders is the main reason why I haven’t made the jump yet. With these restrictions, I’m just afraid I’ll hit a roadblock sooner or later in my project.

It also depends what kind of shaders you want to write. You can write your own custom compute shaders fairly easily (look at this thread for an example: [Tutorial] Pixel and Compute Shaders in UE4 - C++ Gameplay Programming - Unreal Engine Forums ). Custom vertex shaders are also relatively easy (look for VertexFactory). Both of these don’t require any engine source modification and can be written as C++ plugins. The main limitation is writing your own pixel shaders that would interact with the target GBuffer, but Ryan already covered that. Saying that, you can still easily write custom vertex/geometry/pixel shaders that will render stuff into a render target but the limitation is that you will need to feed all the input to these shaders manually (so no automatic frustum culling, etc…).

How can I write a function?

Hi, is it possible to use your method to integrate the famous Jorge Jimenez ssss(separable subsuface scattering) code into ue4 shader?

Sadly I have very limited c++ knowledge. But I am really into making a custom sss shader for the photo-real characters I have made.

Please give me some guidance. Thank you.

I’m having a similar problem. I read all tutorials. (let me add this link to this neat new tutorial in 2017-2018 christmas: Unreal Engine 4 Rendering Part 1: Introduction | by Matt Hoffman | Medium Honestly the best I’ve found around.

My problem is that after declaring a new global shader, or even… when you have usf global shaders loaded in the engine… how can you use them, apply them to the whole scene, instead of using a custom node? How do artists use them? are they available in the material editor somehow? When you are a newcomer you only see Tutorials made by people with 0 knowledge of c++ (so using blueprints and custom nodes, or even not hlsl code), or people with lots of c++ knowledge that do all of it by code.

Any info about this? Thanks!

The shader model will be available at the material editor (there is a combobox for shading model). I think that article series covers that, no? Materials which can be applied to the whole scene are postprocess materials, so they are a specific domain (there is a combobox for material domain). So, both Material Domain, Blend Mode and Shading Model leads to a specific material to be assembled inside the Material Editor.

Currently Material Domains: Surface, Decal, Light Function, Volume, Postprocess and User Interface
Shading Models: Lit, Unlit, Subsurface, Skin, Clear Coat, Foliage, Hair, Cloth and Eye
Blend Modes: Opaque, Masked, Translucent, Additive, Modulate and Alpha Composite

So specific nodes can be used within the combination of the 3 elements above, and the pins which will appear as material output attributes will also change.

Currently for Unreal I have only used HLSL on a very specific raymarching material and I have used a regular nodes and one Custom Node for this, I used Domain Surface, Shading Model Unlit and Blend Mode Alpha Composite, so really I have had quite some job made automatically by the engine for me. The resulting material works as it is, without any changes from my part, with deferred and forward render, works with DX11, DX12, Open GL and Vulkan, all handled by the material compiler for me when I change the packaging for the game, so up to now, there is no need for me to use my C++ knowledge, which is good, because I can produce things for people who does not understand C++.

All the remaining materials I have made, I don’t need HLSL and they also works flawless in any combination of the modes above, all handled by the engine for me, for every platform.

Approximately 95% of cases can be handled by the Material Editor without requiring any custom nodes. Even cases where you want to change how diffuse works, you can use the Cloth shading model to achieve this (0 is fully Lambert, 1 is your new lighting model. 0 takes in the Base Color, 1 takes in the Fuzz Cloth color). I also believe anisotropy can be handled in the Hair shading model. The next 4% of cases require custom nodes: iterative operations for stuff like Parallax Occlusion mapping seem to be the most common uses of custom nodes in the Material Editor. The final 1% of cases cannot be made in the Material Editor at all and require an entirely new shading model to be created. This is the case for geometry shaders for fur.

From what I heard, trying to implement all of UE4’s vertex shaders in a custom shading model is a pain. The engine really doesn’t like to break away from its foundations. We all clamor for those custom solutions, but it’s really only for the 1% of cases that absolutely require custom models to support.