Unlocking The Power Of Unity's Scriptable Render Pipeline

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
when Hi-Fi Rush was first announced and then released by Bethesda on the same day I was immediately drawn to it it's aesthetically striking and appeals to my sense of nostalgia for the era of artistic colorful games we would see on the Dreamcast and PlayStation on a technical level it's not doing anything super groundbreaking but it's delivered with a sense of cohesion and style the flat yet vibrantly shaded colors striking Line work and clever post-processing create a package that's absolutely gorgeous to look at every frame and it's a breath of fresh air in an industry that's moved closer and closer to realistic and expensive visuals and one where most cartoony art Styles look like they're straight out of a Disney movie Persona 5 is one of my all-time favorite games and while it also has a visually striking aesthetic the more painterly style means that it still relies on a lot of custom texture work in the environments and on characters to some degree what makes HiFi rush so striking is how visually simple it is and given the fact that it's been developed in Unreal Engine makes it even more interesting when we consider that most modern game engines are built with a PBR workflow in mind out of the box most modern engines like Unity are unreal want you to use realistic materials so a lot of work has to be done against the engine to get it to look more stylistic so knowing that Hi-Fi Rush was developed in unreal I wanted to see if I could get Unity working in a way that lends itself to a more stylized look similar to the techniques that are being used in Hi-Fi rush to my surprise this is where I discovered the true power of unity scriptable render Pipeline and where my mind was totally changed on unity's pivot to this new way of working scriptable render pipelines give you full unfettered access to how your game looks and surprisingly it's a lot less complicated to delve into than Unity may lead you to think hi there I'm Matt and welcome to gamedev guide in this video we're going to look at how to customize the rendering process in the Universal render pipeline we're going to get nice and cozy with the graphics profiler and use a custom volume component to manage our rendering properties we're going to look at how to write custom render features and custom passes and finally we're going to take a look at how we can use light modes in our shaders to handle our own multi-pass rendering hopefully by the end of this video you'll have a much better understanding of what's going on when the universal render pipeline draws your game to the screen and in turn unlock the full potential of customizing the rendering process giving you full control over how your game looks up until I started experimenting with a lot of this stuff I never really understood the point of the universal render pipeline I was working at Unity back in 2017 when the shift to srps started to happen I even made the official intro video explaining them and while the potential of these pipelines was communicated to me I could never really see how they worked or why anyone who wasn't a full-fledged Tech artist or Graphics programmer would be interested in using them to this day I think Unity has grossly failed to communicate why the script of render pipelines are so cool and how to actually use them to their full potential the ironic thing is that once you get your head around how it all works it's actually pretty straightforward everything is in place for you to customize unity's rendering really easily you actually don't need to be a dedicated graphics programmer if you you've even had a little bit of experience with shaders or even just played around a bit in the Shader graph you can still get great results but it's just so massively under documented and the way that Unity suggests that you do things in the docs or in their official tutorials barely scratch the surface of what's possible and fail to excite people so buckle up because today we're diving deep we're touching on the areas of the unity engine that I just think enough people aren't talking about and are going to explore what's actually possible in these new render pipelines and why they're so cool compared to the built-in renderer with all that said let's go ahead and get started by the end of this tutorial we're going to overhauled a large part of the universal render pipelines rendering process but the whole thing that got me sucked down into this rabbit hole in the first place was a question I had in regards to customizing unity's Bloom component you see Hi-Fi Rush's stylized Bloom is filtered through a Band-Aid dots effect so I just wanted to try it for myself in the old workflow this would have just been an image effect or a custom post processing profile but the universal render pipeline doesn't allow you to just create a custom post process and handle it for you the way that the built-in renderer does in the Universal render pipeline you have to tell it exactly what to do and write your own post-processing renderer you see ultimately Unity have opted to have the universal render pipeline do what the universal render pipeline does which is render geometry to the screen in a specific order via various passes if we open up the graphics profiler we can see this it has a very specific number of steps that Unity predetermines are needed in order to render the final of which is their own post-processing pass so theoretically they could handle this for you when you want to add a new post-process it could potentially just place your custom rendering code somewhere in this final stack here that made the most sense which is basically what the old built-in renderer would do now technically if you use any of the render feature pass override stuff in the pipeline settings they do actually do this you can get urp to do a lot of different sort of custom rendering things in specific places using these features but it's pretty limited we can inject our own rendering code anywhere in this stack and completely take control of any of these existing points in the stack 2 and tell Unity to do something completely different with it so to get our bend a bloom effect working we actually need to write our own post-processing pass that operates within this stack so that's what we're going to do we're going to achieve our Bloom effect by essentially carbon copying unity's built-in Bloom and then instead of rendering it to the screen render it to a custom texture that will then pass through a Shader where we'll make our dots to filter it and then render that to the screen instead we'll control the whole thing in a global volume here so it will feel just like a normal post process so let's start by first building out a custom volume component this process is more or less identical to the one used in the old post-processing system which I've covered in the outline post processing video so if you haven't watched that already feel free to check it out for a bit more context we'll create a new script in our project called bend a bloom effect component and this class will inherit from the volume component class and implement the I post process component interface at the top of the class We'll add the volume component menu for render pipeline attribute scriptable render pipelines all share the same volume component system but certain components are only going to be useful given certain render pipeline features so this attribute lets you define where and when the volume component shows up so since we're using the universal render pipeline we'll set that as our type here but if you were writing a volume component for the high definition render pipeline you'd add that here instead we'll then write in our various parameters that we want to display in our volume component these parameters will be the inputs for our material properties of our Shader I already know which properties we need for our Bloom and Dot shaders so I'll go ahead and set these up it's totally up to you which parameters you want to use here depending on what you want your volume process to control but for this instance we're basically taking the same parameters that unity's default Bloom component has and then adding a few additional parameters that we can use later to control our dots with the parameters set up our component is complete in unity we should now be able to head into our volume component and add our new custom component obviously the settings don't do anything as we haven't hooked anything up yet but we've got our inputs ready to go next we need to tell Unity what these sliders should control as I mentioned before we'll be creating our own custom render feature and using a custom render pass to handle our post processing before jumping into that though I'll try to explain what's happening a little bit and how these work so if we head back into our graph here we can see each of these stages in the list these are render passes controlled by a renderer feature script each of these steps are actually just c-sharp scripts inside of the render pipeline package the main rendering process is just a single render feature with a bunch of passes all listed here in the passes folder some of these additional scripts such as the full screen feature and the ambient occlusion feature are just these feature components we see in our settings are set here so we can actually use each step in the graph and take a look at that specific pass to see what's going on in code for instance the depth only passed here is handling the rendering of the depth texture and the post-process pass here is handling the processing of all the different volume settings via one single Uber Shader this is where I began to understand the whole scriptable part of the scriptable render pipeline thing so using this knowledge then we basically just need to make our own renderer feature and then write our own pass to render and output our custom bloom so let's start by making a new script which I'll name custom post process renderer feature this class will inherit from the scriptable renderer feature class which again is fully visible inside the packages if you want to inspect it the two main things we want to do here are set up and configure our render passes and then cue them into the renderer so let's also make another script for our render pass which we'll call Custom post process pass and here it will inherit from the scriptable render pass class the execute method is in charge of defining exactly what we want the pass to do and when we'll come back to this in a moment now that our pass exists let's go back to our render feature class and finish setting it up render features can contain and control multiple render passes so depending on what we want to do in our project we can write a number of different render pass scripts and manage them all inside of one render feature we're just managing the one pass in this feature but I still like to keep things structured in a way that allows me to add additional passes easily in the future so let's make a new field for our custom pass the create method of our class is useful to manage initialization of our render feature properties so here we'll create a new instance of our post-processing pass and assign it to the field we just created we then need to tell our scriptable renderer to use our new render pass the renderer actually has a list that we can add our passes to so in the add render passes method We'll add our new render pass to that queue if we had other passes that we wanted to render we could also include them here at this point we can head into our render settings in unity and add our new custom render feature however if we take a look in the frame debugger nothing is happening and we can't see our pass this is because our pass isn't doing anything yet so um let's get it to do something as I mentioned before I basically want to interrupt unity's default Bloom and grab the texture but stop it from rendering to the screen for this then we'll need to assign a bloom Shader in our renderer feature there's a ton of ways to assign shaders but I personally prefer to just expose them in the renderer feature settings so I can swap them out easily in our render feature script let's add a public reference to a bloom Shader and we'll add a reference to our bend a dots composite Shader as well back in unity we can now head to our feature settings and assign the Shader we want to use for our bloom now I personally wanted a bit more control over the bloom Shader so we opted to duplicate the Shader from the packages folder into my own file in the project but for this video we're utilizing the whole thing so you can probably just reference the one in the packages folder if you choose we'll also need to set up our composite Shader full screen shaders and Sample buffers were added to Shader graph fairly recently so if we're working in 2022 or later we can actually build out our composite Shader in Shader graph we'll make a new full screen Shader and for now we'll just write a basic Shader that references our Bloom texture and then draws it to the screen so let's grab a sample texture node and then create a property that will reference our future Bloom texture much like normal shaders we can grab any texture on our material via its reference name so we'll just set up a reference name for our Bloom texture to use later with our texture node set up let's just drag this into the base color Port of our fragment output we'll do more with the Shader later but this will do for now and will allow us to preview our Bloom texture once it's set up so let's just assign this Shader to our renderer settings as our composite Shader now it's time to get our renderer working let's set up our materials and build out our render pass Unity has a handy utility that allows us to create a material dynamically using our Shader so in the create method of our render feature let's use the core utils.create engine material method to assign materials from the shaders we just added then let's assign those to our render pass so they can be used by it in our custom render pass script let's set up our two materials and just for good measure we'll make sure to destroy these materials when disposing our render feature we'll also want to set up some of the target textures on the renderer [Music] now all we need to do is tell Unity what our render pass should do we can use the render pass event property to determine at which stage of the rendering that our custom path should execute on in this instance we'll choose to inject our pass before the normal post-processing pass we then just need to do some setup for our blue material a lot of this is taken straight from the bloom pass in the package so I won't go into too much detail but essentially this step sets up the MIP mapping textures and what kind of color space to use for the bloom processing [Music] as you can see here more recent versions of the render pipeline Library are using the RT handle class instead of render textures not T handle is essentially just a special type of render texture that scales automatically with the camera size allowing for better performance and memory allocations when using different cameras for rendering so instead of defining render textures ourselves we can use the various methods provided by unity such as this one to automatically create and manage them I'll post more information about them in the description as there are various pieces of documentation explaining how they work compared to normal render textures with that set up let's head into the execute method let's first get the info from our volume component and fetch the post-processing component so we can pass the settings into our material then let's get a command buffer from the pool in this instance we'll be using the command buffer to move a bunch of textures around so next let's define a new profiling scope for our render pass wrapping code inside of a profiling scope like this allows us to control how it's visualized within the frame debugger now a lot of the first steps here are just setting up the parameters for a blue material again most of this is just a carbon copy of what's happening in the default balloon pass so I won't go over it too much but we'll basically just grab what we need from the settings on our post processing component and apply them accordingly to our material we'll then Blitz the texture from our camera texture into the first MIP mapping texture that the bloom Shader uses as you can see there are four passes on our material Shader the first of which is the actual Bloom pass the second and third passes are the gaussian blur passes and then the fourth pass handles up sampling the final result back to the Target resolution and so the last setting here tells the blitter which pass to use so we'll run these passes and blip the textures back and forth accordingly finally the result of our Bloom Shader has been recorded to the first index of our MIP up textures so we'll tell the command buffer to store that image as our Bloom texture we'll also store the bloom intensity setting on our post-process component as a global float variable so we can use that in our composite Shader later [Music] now in unity if we enable our render feature in the settings and take a look in the frame debugger we should be able to see the result of our custom pass storing our Bloom texture foreign let's grab the settings for our compositing process and pass these properties into our compositing material then we'll just run the current camera texture through our material and copy it back onto the camera [Music] and now we should be able to see the bloom rendered on screen so our render pass is performing the bloom process saving it to the bloom texture property and then feeding it into our composite Shader via our reference name we've successfully hijacked unity's Bloom and saved it into our own custom texture now we can filter it through some Band-Aid dots and get the comic book look we've been aiming for in our composite Shader graph let's create a voronoi node and pass in a tiled screen position as the UV coordinate by setting the angle offset to zero we get a nice uniform grid of dots we'll create a density property and use the reference name from our post process this controls the amount of dots on screen then instead of a gradient to get a uniform Circle we'll pass this into a comparison node and create a cutoff property which can also be controlled by our post-process parameters our post process component can now control the scale of our dots and the size of them within each cell we actually get a variety of different looks and styles simply by experimenting with these two settings in our volume foreign next let's combine these dots with our Bloom texture using a multiply node and then let's also add another multiply node and hook in the bloom intensity from our volume settings [Music] finally let's use the urp sample buffer node to grab the screen texture due to the nature of how Bloom works we'll want to use an add node to combine the two results together our Bloom effect should now be filtered through the Band-Aid dots we can move our lighting around and get all sorts of cool looking results well we've successfully done it we've written our own rendering feature to achieve this effect and we can enable or disable it in our render settings so that's the gist of getting a render feature up and running but there's a bit more we can do to simulate the cartoon style of Hi-Fi Rush first then we'll want to move away from the default material and towards a custom tune Shader this video is long enough as it is so I'm not going to turn this into a full tune Shader tutorial I'll post a link below to good references for urp toon shading if you want to Deep dive into that in a nutshell though I took this tutorial from unity's blog showing how to get Toon shading in urp and Shader graph and then converted it into a fully custom hlsl tune Shader foreign anyway there's now a basic tune Shader on our character here but we also need to render an outline I have a second pass here in my Shader that is in charge of creating a simple inverted Hull outline on the character but by Design there's no default multi-pass support in the Universal render pipeline so how do you multi-pass well some people will tell you to make a different Shader apply it to a material and then duplicate the materials in the mesh but this is really dumb not only is it a hacky workaround that really does not scale it absolutely fails if you have a mesh with sub meshes or if it needs multiple materials and ironically Unity suggests that you use a Shader with multi-pass which as I said UAP doesn't support this is clearly a leftover message from the built-in pipeline but it's horribly misleading if you're not using it the other work around which is suggested by unity in their documentation is to change the light mode on the Shader unfortunately though this also doesn't scale well because well the universal render pipeline only supports these two light modes by default so if you just need two passes I suppose it's fine but if you need a third pass for say rendering second larger outline what do you do we can just write another renderer feature for it so let's create a new renderer feature to allow us to do multi-pass rendering ourselves in our custom feature we'll take a list of strings these will be the light mode tags that our feature will render by making a list like this we can expose the tags in our settings asset and then whenever we need to add new passes we want to render we can just add the tag to our list and our feature will render them automatically in our pass We'll add all of the strings into a Shader tag struct and then in the execute method we can just pass this list into the create drawing settings method of the render pass this will automatically create all of the settings that the renderer needs to collect objects and draw them to the screen based on this light mode tag we then just call the draw renderers method and submit the context [Music] finally we just need to add our feature enable it and in our frame debugger we can now see our objects getting rendered using our custom feature [Music] and finally let's finish the effect off and get that piece of stylization that we see in Hi-Fi Rush with these line Textures in the edges of objects this seems to be some kind of ambient occlusion passed in unreal unity's screen space ambient occlusion is its own pass with its own Shader and logic so similar to what we did with the bloom we can just duplicate most of their built-in occlusion pass ourselves but instead of blurring the texture like the default Shader does through other passes in the Shader we can just render out the results of the first pass into a custom texture we can then use that texture as a mask during compositing so that instead of full ambient occlusion around our objects we get this stylized line texture in its place instead and that's about it for this video there's obviously so much more to explore here and a lot more than I can cover in one video but hopefully it's given you a good place to start I know I've covered a lot so if you want to take a look at any of the source files used in the video to get your head around things all of the assets are downloadable as a Unity package for supporters over on patreon the link is in the description down below has this been helpful for your understanding learning all of this opened my eyes to the script or render pipelines when I finally got my head around it so I really hope it's been useful to you too in some way if you've enjoyed the video be sure to give it a thumbs up and do let me know your thoughts down below if you're new to the Channel please do consider hitting that subscribe Button as you'll get to know when new videos go live if you'd like to see more from me first feel free to check out one of the recommended videos on screen now as always thank you very much for watching and I'll see you again next time [Music]
Info
Channel: Game Dev Guide
Views: 116,407
Rating: undefined out of 5
Keywords: unity, gamedev, how to, making games in unity, tutorial, unity tutorial, learning unity, unity3d tutorial, programming, making games, scriptable renderer, scriptable renderer feature, scriptable render pipeline, render pass, scriptable render pass, custom renderer, unity 3d, universal render pipeline, hi-fi rush, hi-fi rush in unity
Id: 9fa4uFm1eCE
Channel Id: undefined
Length: 21min 5sec (1265 seconds)
Published: Wed Apr 05 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.