Shader Graph Custom Lighting Sample

The Shader Graph team is excited to announce the new Custom Lighting Sample set - available in Unity 6.3. This set of samples shows how you can use Shader Graph to customize the lighting model in your project. By customizing the lighting model, you can:

  1. Improve the performance of the project by skipping some elements of the lighting. On low-end Android devices or XR platforms, you may want to leave out fog, reflections, specular, or other lighting elements to speed up rendering performance.
  2. Stylize the lighting to give your project a unique look. If you want your project to use toon shading, painterly rendering, or to change the appearance in other ways, you can use Shader Graph to control how light interacts with surfaces.

All of these things were possible to do in URP before, but now with this sample content, artists can achieve these things directly in Shader Graph without writing any code.

Getting Started

After importing the Custom Lighting Sample content (see below for instructions), open the Shader Graph Template Browser (also new in Unity 6.3!) by right clicking the Project window and selecting Create → Shader Graph → From Template…

In the Template Browser, scroll down to the section called URP Custom Lighting. Here you’ll find 4 new templates:

  • Custom Lighting Basic - a very simple lighting model. It does not support specular, reflectance, fog, or SSAO, but it does improve performance significantly over the existing URP lighting model and may be ideal for lower-end mobile devices and XR platforms.
  • Custom Lighting Simple - a simplified lighting model that still provides most lighting features, but uses a cheaper Blinn formula to improve performance.
  • Custom Lighting Toon - an example of how a toon shader can be created in Shader Graph. You can follow the example of what this shader is doing to achieve all kinds of unique lighting variations.
  • Custom Lighting URP - this shader is a direct imitation of what the URP lighting model is already doing - but it’s re-created in Shader Graph so you can take it apart, use just the pieces you need, or add your own elements to what URP is already doing.

Select the Custom Lighting URP template and hit the Create button at the bottom of the window to use this template and create a new shader. This new shader you’ve created can be used as a replacement for the existing code version of the Lit shader - so if your project is already using that, you can select your materials and point them to this shader instead. This will enable you to go in and make changes.

How It Works

So double-click on the shader you just made to open it up in Shader Graph. First, open the Graph Inspector and notice that the shader is using the Unlit Material Target. In order to make the Unlit Target work better with Custom Lighting, we’ve added three new checkbox settings:

  • Keep Lighting Variants - When this setting is on, it instructs Unity to include all of the Lighting keywords in the shader. Normally, these would not be included since it’s an Unlit shader, but this setting allows us to keep the keywords active - enabling the use of lighting subgraphs in the graph.
  • Default Decal Blending - If your project is using the decal renderer component, decals are typically added to the shader under the hood. In the Unlit target, decals are added with no lighting. But this setting instructs Unity to not do any decal blending. This allows you to handle the decal blending yourself in the graph.
  • Default SSAO - If your project is using the Screen Space Ambient Occlusion renderer component, SSAO is added to the shader under the hood. But this settings prevents that from happening - which allows you to add SSAO yourself in the graph instead if you choose. You can use it how you’d like (using the included Screen Space Ambient Occlusion subgraph) instead of depending on Unity to do it for you.

By turning on Keep Lighting Variants, and turning off Default Decal Blending and Default SSAO, we have an Unlit target that will work correctly with custom lighting.

Now notice that the Master Stack only has a Base Color as an input, but all of the standard material properties - Base Color, Normal, Metallic, Smoothness, etc, are connected to the Lit URP subgraph instead. This subgraph is doing all of the lighting directly in the graph and then passing the result to the Unlit Master Stack. So if you want to change the type of lighting that’s being used, you can just swap out this subgraph. It’s that easy! We’ll get to that in a minute.

First, let’s open the Lit URP subgraph and take a look inside. From left to right we see the following subgraph nodes:

  • Apply Decals - this subgraph combines the material data of the decals in the scene with the material data in this shader. (If the decal renderer component is not present, this subgraph will not do anything and will not add to the performance cost.) This decal step needs to happen first before the lighting so that the decal data can receive the same lighting as the shader’s material data.
  • Debug Lighting - this subgraph enables the debug lighting modes that are available in the Rendering Debugger window. Using these debug modes, you can switch your scene to display just lighting, just reflections, etc. When the debug views are not in use, this subgraph adds nothing to the cost of the shader.
  • LightURP - this subgraph does the actual lighting. Inside, it contains subgraphs that do diffuse, specular, reflectance, ambient, and fog calculations.
  • Debug Materials - this subgraph is similar to the Debug Lighting subgraph. It enables the debug material modes that are available in the Rendering Debugger window. Using the debug modes, you can switch your scene to display just color, just normal, just smoothness, etc. When the debug views are not in use, this subgraph adds nothing to the cost of the shader.

All of the sample lighting models consist of these four subgraphs. Each of them switches out the core lighting model node depending on the lighting model.

Finally, hit the spacebar to open the Create Node menu. Notice that the sample includes a LOT of new subgraphs in the Lighting category. There are lighting model subgraphs, but there are also lighting component subgraphs. There are multiple types of diffuse lighting, specular lighting, ambient lighting, and reflectance. There’s a node that brings in fog, a node that brings in screen space ambient occlusion, and a node that blends decals with your material data. All of these can be mixed and matched and used in unique ways to get just the look and performance balance that your project needs.

Improving Performance

So if we want to improve the performance of our scene, we can simply swap out the lighting subgraph that is being used for one that uses simplified lighting. In our case, we’ll open the shader we created and find the Lit URP subgraph. Hit the spacebar to open the Create Node menu. Open the Lighting->Light Models category. Select the Lit Basic subgraph and add it to your graph.

Now wire it up in place of the Lit URP subgraph. This will swap out the URP lighting with the basic lighting. In our testing, the Basic lighting is about half of the cost of URP lighting and may improve the overall performance of your scene by around 20% (depending on a lot of other factors of course.)

Keep in mind that in order to get these performance gains, you’re also giving up lighting features. The Lit Basic lighting model doesn’t have any specular, reflections, SSAO, or fog. So if you need some of these things, you may consider using the Lit Simple model instead. You’ll get less of a performance gain, but you’ll also get results that are closer to URP’s lit model.

Stylizing Your Rendering

Similar to how we improved performance, we can also stylize our lighting. If we swap out the Lit URP lighting subgraph with the Lit Colorize lighting subgraph, we’ll have black and white lighting everywhere - except where the scene is lit with the point or spot light. In those areas we’ll get color. If we swap it out for the Lit Toon subgraph, we’ll have lighting that is posterized to resemble a cartoon. And you can even duplicate those individual lighting models, open up your copy and change around the way they’re working to suit your project’s art direction. Shader Graph enables you to sculpt how light reacts with surfaces and define exactly what your final rendering will look like.

LightingStyles

Limitations

There are some limitations to creating customized lighting models in Shader Graph:

  1. All of this only works in the Forward and Forward+ renderers of URP. If you’re URP’s Deferred render, or HDRP and you want to customize the lighting model, you’ll need to do that in shader code instead. This is because the lighting in deferred rendering happens in a separate pass that takes place under the hood and isn’t available to edit in Shader Graph.
  2. When supporting light sources other than the main light in your custom lighting model, you’ll need to write some HLSL code in the Custom Function node in Shader Graph to get that working. The sample already includes 5 variations of subgraph nodes that do this, so you can use these nodes directly if you don’t want to write code, or you can use these as examples to see how it’s done and then write your own based on these patterns. But code is required because the additional lights need a For Loop to process them, and it’s not possible to set up a For Loop in the node interface.

How To Import The Samples

In Unity 6.3, you can import the Custom Lighting sample by following these steps:

  1. In the Editor, open Package Manager.

  2. In the Package Manager window, select the Shader Graph package.

  3. Select the Samples tab.

  4. Finally, select the Import button to the right of the Custom Lighting sample to bring the new terrain shader sample set into your project.

With these steps completed, the Terrain Shaders sample will show up in your project under Assets/Samples/Shader Graph//Custom Lighting.

Conclusion

Using a customized lightning model - either to improve performance or to stylize the look of your project - is a great way to make it stand out from the crowd and make it memorable and unique. We hope this set of samples helps you learn how that can be done and shows you the huge amount of potential in Shader Graph. We would love to hear your feedback! Feel free to ask questions and tell us what you think here in this thread.

45 Likes

Wow! Surprised by all this new stuff. A big step in accessibility in my opinion, makes it easier to work with lighting! Keep up the good work. Shader Graph simple lit should be coming in 6.3 too right?

2 Likes

We are considering this to be Simple Lit - because with this sample content, you can achieve what Simple Lit does. Or you can make a lighting model that is even cheaper than Simple Lit, or one that’s slightly more expensive but has one extra feature that you need. Instead of just giving you one target that you can’t change, we’ve opened up the ability for you to define your own lighting model that is specific to your needs.

Please let us know how we can help you achieve what you’re looking for.

5 Likes

I will make sure to share my thoughts after I try out the 6.3 version!

This is awesome and even more so hearing about it from a Game Development LEGEND… And kinda disappointed about the custom lighting for HDRP and having to do it in the shader code.

I use Amplify Shader Editor and I’ve designed an eye shader that works in HDRP, but I need an extra pass. In the URP version, I can easily do custom lighting…

Hopefully in the future, we will be able to use Custom Lighting with HDRP, or the Unified Renderer

2 Likes

This looks great and I will have a play when it’s out in full!

Would still be useful to replace the global light falloff calculation with a custom one, which is hard coded in a package shader, as the docs say you have to customize the URP package!

I know new surface shaders are incoming and with the original built-in ones I could customize all the lighting functions, though things got a bit more complex since!

Thanks for the guidance above as this is a little complex to understand otherwise!
But the issue for me is the Core Models’ separation of main and additional lights. Rather than share common code they do the main light with nodes and then the remaining with custom code (if I understand correctly). This is unfortunate separation of the lighting calculations.

Ideally a simpler custom lighting solution would be handled by one snippet of custom code (or shader graph) that takes as input any one of the lights (main light included) and does not handle the loop itself but rather the current light (and adding to the summed diffuse/specular/color). Then the graph around it handles looping over the main light and each of the other lights.

Perhaps in some cases the main light wants to be handled differently to the others, but if the above is possible as an option it would be great.

The separation of main and additional lights in the Custom Lighting sample in Shader Graph is following the pattern and organization of URP itself. Changing it would require a significant refractor of the way the URP renderer is organized internally. It is something that has been considered by our engineers but hasn’t yet been a priority. I do understand your reasoning for wanting it to be changed and improved. Thanks for pointing it out.

3 Likes

This is a huge step in the right direction with ShaderGraph imho. Just nice. Kudos to everyone involved.
Well-Done-Applause
Having similar stuff in HDRP would be great. Having a single unified renderer (or at least a higher-level common abstraction for all pipelines) would be magnificent.

1 Like

From 6.3 or 6.4 URP and HDRP will have the same Render Graph system underneeth, slowly slowly is starting to closing the gaps.

2 Likes

These changed are good!
However, I noticed the Mainlight node does not seem to take Rendering Layers (light layers) into account. How would you approach custom lighting for 2 different directional lights. In my case I have an interior directional light and an exterior one. The URP/lit shader handles this situation correctly.

1 Like

Hi! What about GI?
Is it possible to achieve APV/SSGI sampling when using a Custom Light?
So that objects with custom shaders also receive baked GI from APV?

It would be nice to have a toggleable Normal output node in the Fragment block that could be applied to the Depth-Normals pass. Right now any texture normals don’t get output to this pass causing SSAO with Depth-Normals to give different results compared to the default URP shaders (Lit, SimpleLit, etc).

Shadergraph makes a SHADERPASS define for each pass, so you can make a “switch” custom function subgraph kinda like a keyword to filter the pass you want. So in that pass you just insert the normals in its propper slot.

In your case would be something like:

For URP this pass defines can be consulted from “com.unity.render-pipelines.universal/Editor/ShaderGraph/Includes/ShaderPass.hlsl”

#ifndef UNIVERSAL_SHADERPASS_INCLUDED
#define UNIVERSAL_SHADERPASS_INCLUDED

#define SHADERPASS_FORWARD (0)
#define SHADERPASS_GBUFFER (1)
#define SHADERPASS_DEPTHONLY (2)
#define SHADERPASS_SHADOWCASTER (3)
#define SHADERPASS_META (4)
#define SHADERPASS_2D (5)
#define SHADERPASS_UNLIT (6)
#define SHADERPASS_SPRITELIT (7)
#define SHADERPASS_SPRITENORMAL (8)
#define SHADERPASS_SPRITEFORWARD (9)
#define SHADERPASS_SPRITEUNLIT (10)
#define SHADERPASS_DEPTHNORMALSONLY (11)
#define SHADERPASS_DBUFFER_PROJECTOR (12)
#define SHADERPASS_DBUFFER_MESH (13)
#define SHADERPASS_FORWARD_EMISSIVE_PROJECTOR (14)
#define SHADERPASS_FORWARD_EMISSIVE_MESH (15)
#define SHADERPASS_FORWARD_PREVIEW (16)
#define SHADERPASS_DECAL_SCREEN_SPACE_PROJECTOR (17)
#define SHADERPASS_DECAL_SCREEN_SPACE_MESH (18)
#define SHADERPASS_DECAL_GBUFFER_PROJECTOR (19)
#define SHADERPASS_DECAL_GBUFFER_MESH (20)
#define SHADERPASS_DEPTHNORMALS (21)
#define SHADERPASS_MOTION_VECTORS (22)
#define SHADERPASS_XR_MOTION_VECTORS (23)
#endif

I like that they made this custom lighting samples, especially the KeepLightingVariants option (i’ve done this workflow before, and it was a pain to track and mantain keywords), but its still miles away from propper custom lighting support.. =(.

Would love eventually to really have a Custom master node that we can tailor to our needs, AND the option to define custom passes… No Custom Master Node is hackable, but not being able to define custom passes makes you have to ditch shadergraph all together in many cases

I’m sorry, but this is incredibly funny. I just found out this by myself while investigating subgraphs in the Custom Lighting Samples, and couldn’t believe my eyes.

Additional lights support is like the main struggle for the custom lighting, especially in the Forward+ where processing loop approach changes every version.
So you have to implement the lighting model twice: in the shader graph itself and then replicate the same in the custom function code.
In that case, it seems easier to implement everything in the code to have a consistent result from all light sources, and to have everything in one place and not split among dozens of subgraphs.

And from what I understand, Custom Lighting URP (the most advanced custom lighting sample) doesn’t support rendering layers?
Yes, I just tested - rendering layers do not affect how object receives lights.

Haha, while writing this post I was investigating deeper, and the sample shader models don’t even support additional directional lights XD
Because apparently, Forward+ additional lights handling is not trivial, therefore, I don’t understand for whom these custom lighting samples are, even if Unity employees can’t get it right.

Debug Materials subgraph is the most valuable thing from these samples, but again, it doesn’t work with Rendering Debugger for Rendering Layer Masks in material override.

Hi , great tip! I’m trying working on my custom lighting, using unlit shader, and I need to get the normal map/detail to the depth normal pass. How I can use this to output the normal in the depth normal pass?

Hey, been checking and im realizing it might not be doable with Unlit target, because even if you add a “Normal” output, it will get ignored when the shader is generated for the DepthNormals pass.

It would only be doable on a Lit target using the “classic” ultra-cumbersome and unmaintanable tricks of not allowing the default lighting model to run so the compiler strips it (this is normally done by outputting all color through the emissive output and hardcoding 0 on all others so the compiler understands that they dont contribute to the math so it strips them, but also using the “pass filter” nodes like the one i showed on the other post to output your desired normals through the “normal” output only on the desired passes… purely hell, dont think ShaderGraph is really usefull for any serious customlighting workflow)

Thanks, I’ll do some experiments using the lit fragment.

The wish feature for unity would be the ability to add additional fragment nodes that are defined to a certain type of pass (or even costume pass)

I can’t figure out how to restore baked lighting support (directional shadowmask) for custom lighting. The Baked GI Node only returns the texture color, not the direction. I’m trying to write a Custom Node for this, but it would be great to have a premade example…