Toon Shader in Unity Using a Shader Graph with Custom Lighting! ✔️ 2020.3 | Game Dev Tutorial

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hi i'm ned and i make games today i'm making a tune shader with unity's shader graph along the way i'll set up custom lighting something useful for a wide variety of shaders the complete toon shader will support textures shading ram textures shadows specular highlights and multiple light sources toon shaders also known as cell shaders break shading into distinct segments creating a cartoony look they're a good way to add character to any game to build this shader yourself you'll need unity 2019.3 or later and some basic knowledge on how to use materials and the shader graph a script editor like visual studio community or visual studio code and an image editor like photoshop or the one more thing before we get started i made this shader and unity 2019 313 f1 if you encounter any problems with a later version look in the comments i might have a solution there i highly recommend creating a fresh project to follow along with me it will be simple to integrate your new shader into an existing project when you've got it working open the unity hub and create a new project using the 3d template download the universal render pipeline package through the package manager this enables the shader graph in your project to have unity use the universal render pipeline create a universal render pipeline settings asset assign the pipeline asset in project settings [Music] create a shaders folder and an unlit graph asset name it tune a shader by itself is not enough to draw a mesh it needs a material create a materials folder and a material called test tune assign it to use our newly created tune shader in order to test the shader while working create a test scene import some models and set up the camera and lights give the objects the test-tuned material they'll turn into gray silhouettes but that's okay the toon shader does nothing at the moment [Music] it's time to program the shader we need to calculate lighting from the main sunlight the directional light in the scene to calculate the amount of light a surface receives you need its normal vector which is the vector that points directly outwards from the surface the closer to parallel the normal vector and direction of light are the more light the surface receives a function called the dot product can calculate this it returns one if two vectors are parallel and zero if they're perpendicular the final value which i call the diffuse lighting attenuation is a multiplier for light color giving the final color for the surface okay so let's program that open the tune shader which is almost empty right now unfortunately it's not simple to get light information in a shader graph so we have to write some shader code that's where the custom function node comes in right click on empty space and search for custom function to create one go back to the main unity window and create an includes folder open that folder in your operating system and create a text file named lighting.hlsl open that file in your preferred script editor and write this calculate main light function you can look in the video description for text to copy and paste this is the function name the float suffix tells unity which type of numbers to use the function arguments appear next both inputs and outputs world pose or position is an input while direction and color are outputs you can tell this because of the out keyword all arguments are float3 type which is a 3d vector made out of float type numbers look at the function body this is a preprocessor directive which tells the compiler to use different code under different circumstances in this case if the shader is running inside the shader graph preview window it will set predefined values for direction and color if it's outside the preview window we'll call this get main light function which is defined by the universal render pipeline get main light returns a light structure containing values for direction and color you might notice more preprocessor directives at the top of the file put simply these prevent unity from including multiple copies of this function in the final shader return to the shader graph click the gear on the custom function node to edit its settings add one vector3 input called world pose and two vector3 outputs called direction and color set the type to file the name to calculate main light and the source to lighting.hlsl these fields are case sensitive the function node should display a yellowish circle in its preview if there's an error first check for typos create a position node and set the mode to absolute world position drag the out field to world post that finally gives us the light direction with this normals node we also have the model's normal vectors hook both values to a dot product node for lighting we want to discard any negative results so we can use a saturate node it clamps values between 0 and 1. the final result is the value for diffuse lighting attenuation multiply that with light color and you have the final color click save asset in the top left corner to compile the shader and check out the scene the meshes are 3d once again ramp textures are textures containing a grayscale gradient they're a good tool to achieve a tune shader segmented lighting style use the diffuse lighting attenuation value to read a pixel from the ramp and multiply that with the light color this method gives complete control over the lighting gradient when graphs get complicated it's a good idea to separate different parts into subgraphs create a folder to hold these sub-graphs open tune select these nodes right-click select convert to sub-graph and then save as calculate lights in your new folder subgraphs can have outputs and calculate lights should have two a vector one called diffuse and a vector three called color click the gear in the output node to set this up and save the asset return to the main unity window and create a textures folder if you haven't already create or import a ramp texture here's one i'll use make sure it's oriented horizontally open the tune graph again to access the ramp texture add a property this is a variable passed to the shader from the material click the plus icon in the properties panel to add a texture 2d property and name it tune ramp to sample or read from the ramp texture we'll need a sample texture 2d lod node create a ramp texture property node and feed it into the sample node use a sampler state node to tell the shader how to read the texture specifically set the ramp mode to clamp graphics programmers call texture coordinates uvs where u is the column and v is the row when sampling the ramp set the u chord to the diffuse attenuation the v chord doesn't matter right now so set it to zero use a vector two node to create this coordinate the output from the texture sample is the final multiplier to the light color hook everything up and save the asset return to the main unity window set your ramp texture in the tune material and see your test scene in all its toony goodness so we have toony shading but it's lacking color let's add support for diffuse textures first off create or import some textures to use in the project open the tune graph select all these nodes and create a subgraph called calculate lighting ramp unity automatically defines some properties which act as input fields and sub graphs rename the properties to attenuation color and tune ramp by right-clicking them also rename the output value to out save the asset and return to tune's graph reconnect any nodes which got disconnected add another texture 2d property and name it diffuse texture create another sample texture 2d node and a uv node to get the model's uvs notice i left the sampler state unconnected unity will use the settings specified in the texture importer multiply the texture sample by the light color from calculate lighting ramp save the asset set your textures in the materials and watch your models come to life [Music] one feature conspicuously missing so far is shadows unfortunately unity makes it difficult to implement them in a shader graph when you're using custom lighting but i'll walk you through it open the shader code file lighting.hlsl we need to add some shadow logic to the calculate main light function add two additional float outputs distance a 10 and shadow attend these hold the attenuation of light due to distance and shadows in the shader graph preview block set these new variables to 1. notice a hash if block with shadow screen put simply there are two types of shadows unity can compute and you must call different built-in functions depending on the type regardless pass the shadow chord value to get main light and set the values for distance attend and shadow attend from the light structure okay so we're done with that file open the calculate lights graph and click the gear in the custom function node to add two new vector one outputs distance 10 and shadow attend combine the attenuation values by multiplying them together and then multiply that with the diffuse attenuation we're all done here so save the asset it might seem like that's all that's needed to support shadows unfortunately not in the universal render pipeline code there are several keywords like the shadow screen we saw before which control how shadows work for good looking shadows it's important to enable three different keywords most are turned off by default so no shadows will display even with our modified code as of now the process to turn on keywords is kind of complicated but here's how to do it open the tune graph in the properties panel add a new keyword of boolean type and then two more for three in total on each set the default to on the scope to local and the definition to multi-compile in the reference fields enter the three keywords we need to enable one in each property capital letters and underscores do matter okay so save the asset return to the main unity window and select the tune material in the upper right of the inspector there is a three dot menu button click that and switch to debug mode you should see a shader keywords field in that field fill in the three keywords we want to enable making sure to leave a space between each again capital letters and underscores matter press enter and then disable the inspector debug mode that might be all you need to do to see shadows if not there are a couple things to look at in the universal render pipeline settings edit shadow settings specifically set lighting main light cast shadows to on set shadows cascades to at least two and shadows soft shadows to on also check that the camera has render shadows enabled keep in mind you'll need to add these three shadow keywords to any material using the toon shader hopefully unity will make this simpler in the future the little glint of light you see in eyes or on glass marbles is called a specular highlight specular lighting adds a lot of character to a model so let's add it the algorithm to calculate specular lighting is similar to that for diffuse lighting but a little more complex highlights come from light reflecting off a surface and hitting the camera we can approximate this by taking the average of the light direction and the camera direction and then calculating how close to parallel that vector is to the surface normal open the calculate lights graph create a view direction node and set the mode to world space calculate the average of two vectors by adding them and then normalizing the result that will bring the vector's length back to 1. finish the algorithm with the normals.product and saturation nodes if you looked at the highlight right now it would be very large so let's shrink it by adding a new vector1 property called smoothness plug smoothness into this formula and use it as an exponent on the dot product result since the dot product is always less than 1 taking it to a higher power will lower the value shrinking the specular highlight you could stop here and use this value as the specular attenuation i found that it creates some weird highlights and shadowed areas as is to eliminate these artifacts multiply by the diffuse attenuation add a specular vector1 field to the output node and then save the asset let's add a specular lighting ramp it needs to be different from the diffuse ramp but it's more efficient to load as few textures as possible just draw the specular ramp above the diffuse ramp on our image file combining them into one file satisfies both problems now it's important to change the v chord when reading from the ramp texture it needs to be set to 1 for the specular ramp and 0 for the diffuse ramp in the calculate lighting ramps graph add a new vector1 property called ramp sample v route that value into the y input of the vector2 node it's time to wrap everything together open tune graph create another calculate lighting ramps node and set specular as the attenuation set the appropriate ramp sample v values and connect properties together don't forget to create a smoothness property in this graph as well finally add the specular color and the diffuse color together save the asset and check out the scene fine-tune smoothness and materials until everything looks just right wouldn't things look even better with more lights let's implement them we need to dig into shader code some more but first a little background on how unity handles lights unity stores the main light that we've been using separately from other lights such additional lights must be handled specially by the shader additional lights can be of two types point lights and spotlights also sometimes called cone lights with that out of the way let's tidy up the graph and get ready for more complexity open calculate lights select just about everything and create a new subgraph called calculate main light edit it and verify that smoothness was carried over and the outputs are named correctly [Music] we'll need the algorithm to adjust smoothness elsewhere so select these nodes and create a subgraph called adjust smoothness edit it and rename the property to n and the output to out make sure everything is still hooked up and save all graphs return to the main unity window create a new shader subgraph called add additional lights and open it our goal with this graph is to combine together the attenuation and light colors from all lights diffuse and specular attenuation from each light can simply add together light color on the other hand must use the weighted average color between all lights where the weight is that light's attenuation since we don't know how many additional lights there are we need to loop through them loops are not possible purely in nodes so we need to handle this in the hlsl file in the add additional lights graph add these properties which give smoothness and main light information [Music] next add three outputs for the two types of attenuation and light color create a custom function node open the lighting.hlsl file and add the following new function add additional lights note this function has seven inputs and three outputs there is a familiar preprocessor directive this time meaning the code inside will not execute if in the graph preview window get additional lights count is a built-in function in the universal render pipeline it returns the number of additional lights currently affecting the model get additional light gets a light structure just like get main light did these lines calculate the diffuse and specular attenuation they're the same algorithms we built and graph earlier but in code form add the results to the running totals and add the weighted light color to the total outside the loop divide the total light color by the total attenuation to calculate the final light color there's only one caveat if the total attenuation is zero set the light color to the main light color always avoid dividing by zero back in the calculate additional lights graph fill in the arguments outputs and setting information corresponding to the function we just wrote like before double check for typos now create nodes to hook up all the needed inputs including an adjust smoothness node [Music] finally connect the diffuse and specular outputs to the output node and save the asset next edit calculate lights and add the new add additional lights node route nodes together and save that asset okay so that's it create some point and spotlights and watch things light up make sure the light intensity is high enough to be visible if they're still not showing up check the universal render pipeline settings and set lighting additional lights to per pixel and the per pixel object limit to at least one and with that we're done the shader is in a good spot but there's always more features you could add here are a few ideas if you're feeling inspired ambient lighting rim lighting texture tents specular tents specular maps and normal maps try to implement a few thank you all for watching if you like this video please leave a like it lets youtube know they should recommend this video to others searching for toon shader tutorials if you have any questions don't hesitate to leave a comment i have two questions for you how are you planning to use this toon shader in your project and are there any topics you'd like to see a video about thanks so much for watching and make games you
Info
Channel: Ned Makes Games
Views: 80,617
Rating: undefined out of 5
Keywords: gamedev, game development, development, unity, unity3d, madewithunity, programming, game design, csharp, nedmakesgames, nedmakesgames dev log, twitch, twitchcreative, twitch creative, indiedev, indie game, dev log, shaders, 3d modeling, blender, tutorial, walkthrough, shader, toonshader, celshader, cel, toon, cel-shader, toon-shader
Id: RC91uxRTId8
Channel Id: undefined
Length: 20min 40sec (1240 seconds)
Published: Thu Aug 06 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.