ShaderGraph request: Allow a "Custom Lit" MasterNode

EDIT:
After reconsideration, just allowing us to make custom master nodes without modding the RP would probably be best. My “CustomLit” suggestion would be too limited (no control over shadows, etc…)


Toon shading is something you always see a lot people asking for when in comes to shaders, and it’s very often an issue with node-based shader tools (even UE4 has this problem)

Unfortunately it’s not possible to implement proper toon shading that supports dynamic lights using only ShaderGraph. There is a toon shading ShaderGraph sample somewhere out there, but it can only be compatible with one directional light vector, so it’s really not an interesting solution. You have no choice but to write your own custom toon MasterNodes in shader code if you want the real thing.

Doing custom lighting requires only a tiny little change in the Lighting shader code, so I was wondering if you guys could just add a “CustomLit” MasterNode that basically is the same as regular Lit but allows you to do operations on the “nDotL” value after it’s been calculated (in the case of toon lighting, we would make “nDotL” be either 0 or 1 depending on a given value threshold). Maybe also operations on final light intensity too, let’s say if someone wants their toon lighting to have no light attenuation at all

Here’s an example of my own implementation of toon shading in HDRP, which I did by adding 1 or 2 lines in the light functions of the pipeline’s shader files:

It supports all available lights and probably even GI (untested) since it’s basically the same as the default Lit shader.

I think this approach of letting users implement their own custom lighting would be much better than just shipping a pre-made “ToonLit” MasterNode that has a specific implementation, because there are many different kinds of stylized lighting (some with ramps, steps, “stylized/hand-drawn” shadow edges, etc…) and it’s better to give users the power to implement whatever they want

9 Likes

I wanted to thank you, because I “stole” your idea for the custom lighting node and implemented it in my plugin (Asset store link).

I also did toon lighting as an implementation example.
It works only in LWRP at the moment (working on a separate HDRP version), so if you’re on HDRP don’t pick it up yet!

It supports correct shadow receiving, multiple lights …etc. Only vertex lights aren’t supported due to the complexity it would add to the graph.

You can see the custom lighting node in the second part of this video

I agree, Ive been asking for this since early 2018, and nothing has been done to make any of this easier for unity users in that time. Considering a vast amount of unity games use stylized graphics, its silly this still is not something that can be intuitively sorted out with shader graph.

Unity developers, this exact limitation is why in the end I have abandonded SRP entirely. Im tired of fighting against it and your constant breaking changes, just to get a simple lighting model of NdotL + stepping working via SG.

1 Like

This looks very nice, but how did you get custom MasterNodes to work with 2019.1? I thought it became impossible to make MasterNodes due to this: Custom master node, is it on the roadmap?

Can you share some details?

1 Like

Well, to give you a hint, one way of doing it would be to remove the ShaderGraph package from the package manifest, copy / paste it directly in your project and remove the “internal” modifier so you can access nodes.

Beware that by doing this, you probably won’t get much support from Unity as you’re dabbling in something that isn’t meant to be.
Every time you update ShaderGraph, you’ll also have to go through their breaking changes which aren’t documented (because the API isn’t public); the best way is to follow the ShaderGraph history on github.

1 Like

To be fair though, I think it’s in Unity’s right (and in everyone’s interest in the long run) to make constant refactors as long as they don’t declare that the SRPs/SG are “out of experimental”.

Sometimes you don’t want a bad early design decision to haunt you for the rest of the product’s life… like the decision to make the MasterNode API internal, for example :smile:

1 Like

That is a good point, I certainly dont want any unity developers reading my post to think thats my main source of concern. Im happy for them to refactor as much as they want, as long as theres a way to use shadergraph in the meantime. Currently I cant really use it for what I want so I avoid it, although I may bite the bullet and delve into how to pull apart LWRP shader to make my own by hand, but last time I did that I got most of the way and everything changed and so I did it again and everything changed and I just lost hope for a bit.

Anyone know how to change the current LWRP 5xxx lit shader to use toon based lighting, or can at least point me to where in that shader I can edit the lighting to do the calculations I need? Right now its a sea of macros and I am not sure where to begin, or how/where to remove all the PBR stuff I dont need

Im pretty proficient with writing shaders in built-in pipeline if that helps

Here are some quick notes I took last time I did it in LWRP. It doesn’t get rid of all the calculations you don’t need, but it’s a good start:

  • In ShaderLibrary\Lighting.hlsl → LightweightFragmentPBR()

  • Disable GI by commenting out the line that does: half3 color = GlobalIllumination(…)

  • (it’s probably possible to do toon GI with a few mods in the GlobalIllumination function but I didn’t bother with it)

  • In ShaderLibrary\Lighting.hlsl → LightingPhysicallyBased()

  • mod the NdotL like this: NdotL = (NdotL > 0.5) ? 1 : 0;

  • In ShaderLibrary\Lighting.hlsl → InitializeBRDFData()

  • near the beginning, make the out data diffuse be just the albedo instead of whatever is there currently: outBRDFData.diffuse = albedo ;

  • this is the part to modify: https://i.gyazo.com/c45b163b1382418d7cabab4b0acde264.png

  • In ShaderLibrary\Lighting.hlsl → DirectBDRF()

  • mod specular like this: specularTerm = (specularTerm > 0.5) ? 1 : 0;

  • In ShaderLibrary\shadows.hlsl → SampleShadowmap()

  • mod final shadow attenuation like this: attenuation = (attenuation > 0.5) ? 1 : 0;


EDIT:
Instead of the last step in shadows.hlsl, I think you could actually just do the shadow attenuation mod in Lighting.hlsl → GetMainLight() and GetAdditionalLight(). Didn’t test it but it would probably work

2 Likes

Your a legend, will try this out when I get home from work! Rock on!