Making a Zelda-style Cel Shading Effect in Unity Shader Graph

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[Music] [Applause] cell shading is the technique that drives games such as zelda breath of the wild persona 5 and okami it's usually characterized by simple colours and hard borders between brightly and dimly lit areas of an object in this video we're going to use shader graph to build a dazzling cell shading effect in unity urp if you like this video remember to subscribe to see more content like it [Music] before we jump into the effect let's go over a few lighting basics since we can't modify unity's automatic lighting in shadowgraph directly at least until they add the functionality we'll need to do it ourselves and to do that we need to understand exactly where the light comes from we'll base our lighting on the phong shading model ambient light as a base level of lighting caused by indirect light bouncing all over a room and we can model this cheaply and effectively by adding a blanket lighting value to everything ray tracing eat your heart out then there's diffuse light which you can think of as non-shiny light and this is based on the angle between the surface normal n and the light direction vector l the higher the angle the less light there is we calculate this using the dot product so it's called n n.l then the specular light which appears on shiny surfaces as bright white highlights it's based on the surface normal and what's called the half vector h which is the mean average of the view direction and the light direction specular lighting is just n dot h the final part of the puzzle is for now lighting this isn't usually part of font shading but it's a great addition to a cell shading effect and it's definitely present in breath of the wild when you have a bright light shining on the back of an object you'll see light bleeding around the edges that's what frontal lighting looks like it's based only on the view and normal vectors although as the angle increases so does the amount of lighting hence fresnel lighting is one minus n dot v the final amount of light on an object is just the sum of the ambient diffuse specular and in our case fresnel light now that we know where the light should come from let's calculate it with shader graph [Music] there's no built-in node in shader graph to grab the lighting calculated automatically by unity so we're going to code it ourselves alex lindman's fantastic article on the unity blog site has a breakdown of how to do just that don't worry it's a copy and paste job if you're not a coder i'm starting with an unlit graph and i'll add a custom function node which will let us inject our own code into shadowgraph i've created a file called lighting.hlsl and we're going to get information about the scene's directional light by writing a function called main light underscore float it takes in the wall position and then we use the transform world to shadow chord and get main light functions which exist in unity shader code api to access the directional light and output the direction color and attenuation of the light attenuation gets lower if the pixel should be in shadow then we need to set a few defaults which get used in the shader graph window since no light actually exists there to use that code just set the source file on the custom function node to lighting.hlsl and type main light into this name field ignore the underscore float bit we need to add that in the code so unity knows the precision of the variables i like to separate custom functions like this into their own shader graph and in this case i'll name it get main light and multiply the two attenuation values together make sure the inputs and outputs line up to those required by the custom code but that's just the main light what about the other lights in the scene we'll need another function called additional light which is very similar to main light except it'll take an index parameter to get a specific light shining on an object we'll set some default modes beforehand then check if the index is less than the number of lights acting on the object this time the default values are used if our index is invalid the get additional light function is provided for us to get data from the light i do the same thing with the additional lights and make a sub graph called get additional lights to wrap the whole thing up now we've got the shader code out of the way it's time to calculate some lighting inside shadowgraph [Music] now that we actually have some lighting information unity gives us all the other parts to do the lighting calculations so let's start with the diffuse light we'll pass the world position into a get main light node that's the custom node we just wrote and take the dot product of its direction output with a normal vector which returns a value between -1 and 1 where 1 is where the light shines directly onto the surface zero is on the lip between lit and not lit and -1 is where the light shines in the complete opposite direction to the surface normal multiply by the attenuation and then the light color and let's just leave it there for now we're going to use the value just after attenuation but before applying the light color in some future calculations for the specular light it's a similar story we'll calculate the half vector by adding the light direction and view vector then normalizing and then we'll take the dot product between that and the surface normal to get the specular lighting amount since specular light is based on the smoothness of an object we will add that as a property and raise the specular value to the power of the smoothness make sure you use a saturate node beforehand to bound between 0 and 1 because negatives will throw off the power calculation multiplied by the attenuation and light color and then the diffuse value because specular highlights can't appear where diffused light isn't present finally there's the phenol lighting unity helpfully provides a fresnel effect node which does the calculation for us thanks shader graph we'll add a property called final size and then take the reciprocal of it to use that in the power input but since the fresnel appears smaller with larger inputs yet again multiplied by the diffused lighting value because fresnel shouldn't appear where diffuse light is absent if we temporarily added these three sources of light together and output them to the base color on the master sac we will start to see some good lighting on our objects even if we haven't included ambient light play around with the properties to see what happens smoothness can go into the thousands for good results and fresnel size is usually very small like between 0.05 and 0.1 obviously this isn't cell shading yet to do that we're going to have to add a lighting cutoff of some kind [Music] there's several ways to do the characteristic cell shading cut off in the original article i wrote on this subject i used a texture ramp which encoded lighting amounts in a texture strip which makes it easier to customize the lighting with a few minor downsides but this time i'll just use shadow graph to add a single cut we'll start yet again with diffuse light we could use a step function to cut the light where diffuse equals zero and that would give us a hard border between light and dark but instead i'll use smooth step which gives us a tiny amount of fall off between black and white so the transition isn't quite as harsh i'll use a lighting cutoff property to set where the threshold should be and then a falloff amount of about norway note 5 for the smooth transition for the smooth step node the lighting cutoff should be edge 1 and lighting cutoff plus falloff amount should be in edge 2. then stick the diffuse light into in now we've got some cell shaded light at this point we should think about how we want ambient light to function usually we would add ambient light as a blanket value at the end like i mentioned but this might have the effect of brightening the entire object more than we want and make it difficult to decide which colors to use instead we're going to use the ambient light to modify the diffuse color value by remapping the output from smooth step we'll do that by taking the maximum between it and a new property called ambient strength in essence we're raising the floor of the darkest areas by some amount multiply by diffuse color which is another property to finish off the diffused light the specular and fresnel light are a similar story without taking ambient light into account for each take the calculation so far and use the same smooth step setup and then multiply by specular color and final color respectively which are two new properties since lighting is additive according to the lighting model we graciously adapted from fong we can take these three strands of light and add them together as part of the final lighting calculation now if we take a look at our example scene our objects look fantastic with cell shading applied again i'd encourage you to try different colors and models from all angles to see what looks best to you if we introduce a couple of extra lights though there's no effect on the objects because we're only taking the directional light into account we already wrote code to get information about additional lights so let's put together the rest in shadowgraph [Music] as we've said lighting is additive so we can add the diffuse and specular influence of other lights onto the influence from the main light it'll look really messy if we just throw everything onto the same graph so i'm going to abstract the additional light calculations into two sub graphs called calc additional diffuse and calc additional specular let's look at calc additional diffuse shadow graph can't use loops unfortunately so i'm going to recreate the nodes to calculate the diffuse light for the first additional light then copy the group three times and change the light id on the get additional light custom mode that we made early on in the tutorial i think we can support up to eight lights but i'll stick with four for now then we add the four values at the end and output a vector three called diffuse which we can set in the node settings of the output node the calc additional specular subgraph is similar except we need a smoothness input for the power calculation otherwise i use the same system of copying out the code into four rows we do the n dot h calculation like we did for the main light specular then we also need to multiply by the individual lights n dot l calculation to make sure the highlights only appear in the right place don't worry if there's fewer than four additional lights acting on some object the default values in our custom code will make it so that those lights add zero to the final light amount i've copied our main graph into a second version called cell shading additional so you'll be able to use both versions the amounts of tweaking required is minimal for the diffused lights take the multiply just before smooth step and instead saturate it then add calc additional diffuse to it the value output by saturate needs to be used in the specular calculation and the value after adding both together is used for the fresnel calculation and for the diffuse smooth step as for the specular light group the tweak is similar just before multiplying by the diffused light add everything up until that point to calc additional specular making sure you plug in smoothness into its input if you hop into the scene view and add a couple of lights you'll now see your objects react to it there's even more than one specular highlight so your objects will twinkle in the light obviously there's loads of features you could add to this originally this tutorial was only going to cover diffuse color but i quickly added the ability to read a texture by multiplying it with the diffuse color and i'm very glad i did because it makes this model of link look amazing and there's limited support for other pbr maps like normals ambient occlusion or emission so if you want the shader to really shine try to add some of those yourself thanks to everyone who supports me on patreon your names should be sparkling on screen right now if you want to support the channel then even just following my posts on patreon is super helpful or you can throw me a one-time donation on kofi i'll be back soon with new content and maybe this time i won't even be ripping off the legend of zelda until next time have fun making shaders
Info
Channel: Daniel Ilett
Views: 84,533
Rating: undefined out of 5
Keywords: unity, shader graph, shader graph tutorial, cel shading, cel shading tutorial, botw cel shading, toon shading, zelda cel shading, game development, unity3d, unity cel shading, shader, unity tutorial, shaders, indie game, gamedev, daniel ilett, daniel ilett toon shader, daniel ilett shader, urp, unity shader graph, unity shader graph tutorial, zelda breath of the wild, persona 5 cel shading, shader tutorial, unity shader tutorial, unity shader, unity toon shader
Id: lUmRJRrZfGc
Channel Id: undefined
Length: 13min 19sec (799 seconds)
Published: Mon Jul 12 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.