Six Grass Rendering Techniques in Unity

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
grass many games use grasp fill otherwise empty trains sometimes it's quite basic and other times it really helps to bring a scene together there are lots of ways to render grass and there isn't really a best way to do it since it heavily depends on what you're doing with the grass so in this video I want to compare a range of techniques and explore why you might choose each one in certain situations six techniques to be exact this will be less of an explain exactly how everything works video and more of a here are the important bits video but all the code is on GitHub if you want the complete picture including usage examples there's a mix of straighter and non-shader approaches and although I focus on urp the concepts aren't transferable to all the pipelines before we get started I want to let you know that my Shader book is out now it covers urp hdrp the built-in render pipeline Shader graph from making your first chain so all the master you want to know to Lighting post-processing in each pipeline textures depth effects and everything in between it's linked in the description and I'd love for you to check it out I am also trying out Channel memberships that I'll run like a condensed version of my patreon so check out the join button for details let's crack on with some grass shaders the first approach is one that everyone already knows how to do just copy and paste a grass smash all over your scene no really don't click off the video I promise this is going somewhere although this seems silly it really does have some tangible benefits first and foremost it's easy to understand you want grass somewhere you just put some rest there is a lot of individual control over where the grass goes but that's not the only benefit this approach makes it easy to customize and add Behavior to each bit of grass in a way that is often a total pain with other techniques as we will see for example if you want each tuft of grass to play a shake animation when the player walks through it then we can attach a trigger collider and a small script to the grass to make it happen it's also relatively easy to apply custom colors or maybe vertex based swaying in the wind via shaders maybe that's a weird point to make but with the other techniques it is a lot more difficult or sometimes even impossible to implement features like that I chose to use Alpha clipping for my mesh grass because sometimes you can run into sorting issues when you use transparent rendering and the grass blades overlap Plastering meshes across the world may seem inefficient but Unity can optimize the rendering of meshes placed like this to reduce the performance impact via a feature called Bat chain this happens largely behind the scenes and the intention is to reduce the need to send repeated data from the CPU to the GPU if multiple objects use the same mesh and the same material we don't need to waste time sending all of the material and mesh data for each fresh grass blade one day I'll go into more detail about batching and the SRP Batcher but that's all we need to know for now it's not all plain sailing of course if you want lots of grass it's going to take lots of clicking and moving and dragging when I say drag I mean it is a drug that said there are a couple of added tricks for quickly placing objects if you multi-select a few game objects and then type say l open parenthesis 1 comma 10 Close parenthesis then Unity will equally space the object out between 1 and 10 on whatever axis you typed it in needs there's also RN brackets if you want to randomly space them you can even put these Expressions inside cause or sign to make a summoning Circle anyway I'm getting away from the points a little bit even with these tricks manual placement can be annoying plus with lots of meshes even batching can't save you and you'll end up eating into your frame budget so when would I use this method to draw grass if I need a fairly limited amount of grass and I want each bit of crass to react to the player then this is the way to go sometimes you don't need to over engineer things and the simplest approach is good enough but if you want a large field and don't mind sacrificing customizability for performance then you'll need another approach I already made this video but I want to talk about how this approach compares to others so let's take another look at geometry and tessellation shaders geometry and tessellation shaders are two optional Shader stages that slot in between the vertex Shader and the fragment Shader simply put you run the vertex Shader and then the tessellation Shader can subdivide the resulting mesh data into smaller and smaller triangles giving you more vertices to work with that's useful if you need finer details in vertex based effects such as wave displacement the geometry Shader which runs next has the power to discard or generate new geometry so when you put this all together we can take a quad mesh subdivide it using a Tessellation Shader then generate thousands upon thousands of grass blades on each vertex discarding the original mesh as we go with this technique it is still possible to customize the grass quite a lot if you want to tweak the color you can do that if you want to add wind simulation it's a bit more complicated but this Shader can do that since we can explicitly control the position of every Vertex or each grass blade using the geometry Shader it might not be as elegant as creating an animation in blender but at least it's possible where this approach starts to fall down a little is in its rigidity and complexity since we are hard-coding the logic for building the grass blades inside the geometry Shader that necessarily means we can only generate one type of mesh inside the Shader if we wanted different kinds of grass or if we wanted to use this technique to distribute flowers or rocks across the ground then we must write a whole new Shader of course we can copy over a lot of the code but it's still cumbersome in terms of complexity there is a lot more code to write than the basic mesh based method I won't go into full detail but the tessellation process is actually made up of two separate programmable Shader stages the hole Shader for generating new vertices and the domain Shader for interpolating properties between those new points both stages have a hefty list of options and settings that you would need to navigate that's not dimash tessellation shaders they are powerful and I would wholeheartedly recommend exploring how they work geometry shaders on the other hand are a whole other storing with this approach we're building each grass mesh for a given frame then discarding all that work and building the entire thing from scratch again in every subsequent frame that's extremely wasteful probably the worst thing about them is that they are not supported at all on certain platforms and devices such as the Oculus Quest and some mobile devices emulate them in software because they are especially expensive on mobile gpus if you're worried about geometry Shader performance in your game then always profilers and compare it with other techniques procedural rendering is a technique that lets us render many instances of a mesh in different positions all in one draw call although we discussed matching measures earlier back feature is limited because it is a one-size-fits-all solution that has to account for material changes and variable numbers of objects but with procedural rendering we specifically instruct the GPU which mesh to use exactly how many times to render it and which material to use the basic workflow is that we first work out where each grass bleed mesh should be positioned and how it should be rotated then we can build a model Matrix per grass blade that represents those transformations then we need to upload the grass blade mesh to the GPU alongside those matrices and run a Shader that can render the grass using that information for the first step which is working out the positions I'm going to use a Terrain mesh and I want to place a grass blade at every vertex of this terrain match you need to enable read write on this mesh which I don't think you can do with unity's built-in meshes so I'm using this custom mesh which I whipped up in blender I've attached a script called procedural grass to the terrain and in the start method it will access the vertex array and the triangles array of the terrain mesh and convert both arrays into Graphics buffer objects together these two buffers give us enough data to start building transformation matrices for our graphs I'll store these matrices inside a third Graphics buffer now let's fill this buffer I'm going to do this using a compute Shader I don't think I've talked about these in a video before so briefly a compute Shader is a general purpose type of Shader that exists outside the vertex to fragment pipeline we've previously seen you can run all sorts of arbitrary compute programs directly on the GPU they don't draw to the screen but that doesn't mean they're not helpful for graphics applications we want to build potentially thousands of transformation matrices and the GPU happens to excel at Matrix calculations so we can use a computrader to build them in parallel a compute Shader uses hls or syntax entirely no trace of unity Shader lab here and it's made up of a list of variables and functions I've included two structured buffer objects to link with the graphic spliffers I made in the script for the terrain vertices and triangles for the transformation matrices I use a slightly different syntax RW structure buffer the RW stands for read write switch I need because we'll write into this button from the compute Shader there are a few other variables related to the graphs including the terrain's object to World Matrix the meat of a computrader is in what's called a kernel function I have one called calculate blade positions in my Shader but you can have several a kernel function is run by many GPU threads at a time this is how the GPU is able to process data so quickly different bits of the total workload are processed simultaneously on the basis that each task is independent from all the others just above the karnal function we have a num threads attribute that specifies how many threads run at once to oversimplify when you multiply these numbers together that's how many threads run simultaneously so in this example the computator processes 64 triangles at once the kernel is going to run once per triangle so first I have a fail safe which prevents errors if we don't have a multiple of 64 triangles this code generates a transformation matrix for each triangle then places the results in the transform matrices buffer I know this is a lot to take in if it's your first exposure to them but computers are super useful once you've played around with them a bit back on the scripting side we set up a few other variables that the combich header needs then run it using the dispatch method we need to tell it how many thread groups to run so if we have exactly 640 triangles that would be 10 thread groups 641 triangles would then need an extra thread group the great thing about the approach I'm using is that we only need to generate the Matrix buffer once at the start of the game then we can use those very same matrices each frame when rendering the grass this gives us an obvious speed up over the geometry Shader approach which needed to do a lot of repeated calculations every frame that's half of the work done so next we need to use these matrices to render the grass blades first in start we will read the vertex list and triangle list for the grass blade mesh into new graphics buffers like we did for the terrain mesh but this time we will also read the UVS into a third buffer in the update method we can call a method to do the rendering there are many slightly different methods in the graphics class such as render Primitives and draw mesh but the one we're going with is the draw procedural dual procedural takes in many many arguments the material the topology of the mesh the triangle buffer and its size the number of instances a material property block to set the material properties which I create back in start and shadowing options now as you noticed one of those things was a material but what kind of Shader does that material use this Shader Works slightly different to a typical mesh Shader instead of having an app data struct containing the vertex position UVS and so on this vertex Shader only gets given a Vertex ID and an instance ID this means that we need to pass data to this Shader via structured buffers Yep they're not just for compute shaders these buffers get sent via the material property block that I mentioned so in the vertex Shader we use the vertex ID to access the vertex position from the vertex list and the UVS on the UV list and then we use the instance ID to get the correct transformation matrix for this mesh fragment Shader works just like you're used to so I use it to apply basic colors textures and shadows so what are the advantages of doing things with procedural rendering well it's savagely efficient and if you won't be adding removing or moving any of the grass meshes you can just run the computator at the start taking a huge amount of work away from each subsequent frame it's also very easy to swap out the grass blade mesh for any other detail mesh a rock a birch or a tree can be spliced in and it'll work just fine unlike the geometry Shader approach you won't need to totally rewrite the Shader procedural rendering might not be as great if you need fancy animations for the grass or whatever object you're rendering it's sometimes possible to create basic animations directly inside the Shader but if you're doing anything more sophisticated than swaying in the wind it's going to pale in comparison to any other animation technique this approach also requires that you have the amounts of work with scripting and setup and while I think it's worth it for the payoff it's not an approach that will work in all scenarios also I should warn you that computers are not supported on all platforms however in this instance it would be feasible to replace the compute Shader with more scripting I just didn't want to because computers are cool in all I really do love this technique for details that you want to plaster across a large scene it's a great mix of efficiency and Fidelity while sacrificing a little bit of the agency you have over what each mesh can do so far we've been trying to render High Fidelity grass up close the kind of grass the player will walk through however you won't always be seeing grass right in front of your face especially in a large scene sometimes the grass is far away and tendings like billboarding are your friend here with billboarding we render quads with a grass texture attached to them and Orient them towards the camera the upshoot here is that we can use a texture containing several Blades of grass so a quad invariably uses fewer vertices overall that means that you can either render grass at a greater distance or how dense the grass a bit closer the Shader required for this effect is quite simple as you only need to sample the texture in the fragment Shader the vertex Shader is the most basic kind that transforms vertices where they need to go and that's it you can choose whether to use Alpha blending or an alpha cut off but since the grass is so far away you might as well use a cut off and save yourself from the inefficiency of blending several quads together it's possible to write all the billboarding logic inside the vertex Shader which requires a lot of fiddly Matrix Transformations but I did it all in scripting instead because it's only a couple of lines so Billboards are just simple quad meshes that Orient themselves towards the screen however there's nothing stopping you from combining all boards with procedural rendering if you want to do that generate a buffer filled with transformation matrices that position the grass where you want it and rotate them towards the screen then call draw procedural with a quad mesh I'll leave that one as homework Billboards fall down not literally when you try and place them too close to the camera and it becomes very apparent that Billboards are being used it's especially noticeable when you rotate the camera and some of the Billboards clip through each other like this you can avoid it with some smart placement so they can never overlap but then the grass doesn't look as thick Billboards will only ever be a trade-off between performance and Fidelity they are Best Kept to scenes where thoracic sends very far away where you don't need High Fidelity but you do want to see something at that distance next let's look at two different extensions of billboarding that may be useful to you you know it you love it it's the unity turn system I think a lot of people end up using third-party plugins for trains in their games but unity's terrain system is still knocking around and it comes with support for details such as grass it's pretty easy to set it up go to the paints details tab on your terrain and then click the edit details button and add a grass texture you can fiddle with the settings for size and coloration here and crucially the texture itself this is also where you can choose whether to make the grass act as a billboard or not when you're happy with your choices hit the add button then you can use the paint tool to paint grass wherever you want in the terrain settings tab there are a few more settings for stuff like wind and detail density so you can just tweak these as you want point is terrains make use of billboarding when you enable it and they use instancing to efficiency render the graphs so you can get away with making it rather large terrains with tons of grass without much Slowdown there are some obvious advantages of this method first you get some very nice painting tools which alleviate the problems I was having when I was manually placing meshes it would probably be possible to write your own placement tool and the style of unity's terrain tools to use for your own custom grass system so if you've got a tools programmer on your team get them on that ASAP the main drawback freely is if you don't like unity's terrains then you won't like this kind of billboard grass and it is just billboard grass at the end of the day so you get the same benefits and drawbacks as I already mentioned that just leaves one more method and it's a pretty cool one finally we come to imposters one of the key drawbacks of billboarding is that you only ever get one perspective on an object if you Pan the camera around a billboard then it rotates to face you in scenes where the Billboards are at a middle distance this becomes noticeable and it's kind of irritating to look at imposters are a solution to that problem they work like Billboards in that they Orient themselves towards the camera but we can swap out the texture apply it to the quads to one that was captured from that angle essentially we have a 2d array of screenshots of a single mesh each one captured from a different angle that we pick from based on the camera angle here's the setup ahead of time we place the mesh in the scene and then reposition a camera at different positions on a hemisphere around the mesh if you also want to view the Imposter From Below then you can use a sphere but the maps are slightly different at each position we render a virtual camera using layers to make sure we capture only this mesh and put the resulting texture into the correct position on an atlas there is a mapping between the 3D hemisphere shape and a 2d grid which is what this bit of code is doing I made a generator script that we can run in the scene view to do all of this which lets you pick how many positions there are around the mesh once we've generated the texture Atlas we can go ahead and delete the generator and the mesh because we don't need them anymore to view the imposters I'll add a bunch of quad measures to the scene like I did with the vanilla billboards each one has a new script attached which at runtime does the same thing as the billboard script from before plus it sends the view Vector between the camera and the Imposter mesh to the Shader in the Shader we run some codes to convert from The View Vector to a position in 2D space and use that to pick the correct subtexture from the texture Atlas the fragment Shader is again very simple as it just samples that texture here's what it looks like in action when you rotate the camera around the object it'll swap between textures automatically I'll admit that there are ways to make this effect look a bit smoother like using blending between textures to avoid the kind of popping effects here but you can just bump up the number of textures if it's too noticeable so what's the benefit well for this grass maybe it's a bit Overkill the mesh I started with has 424 vertices and 318 triangles which is quite a lot for a small tuft of grass but we could still get away with just placing thousands in the scene and the game would still run fine however if you were to use something like this tree model which apparently uses 400 000 vertices that's too many it's going to be noticeable if you put a few of these in the scene with imposters you can condense that down to four four vertices that's a saving by a magnitude of one hundred thousand in the virto Shader the trade-off is that you're using quite a lot of texture memory but the texture can be shared between all of the imposters in the scene The Imposter effect breaks down when you view objects up close that I would propose the following setup if you want a massive scene full of detailed objects up close just use the full mesh maybe optimize the mesh so it doesn't use 400 000 vertices but here's the full mesh at a middle distance if you have a large enough objects that might conceivably be able to walk far enough around the object that a single texture looks weird use imposters and when you have extremely far off objects use Billboards and save yourself a bit of texture space I hope you learned a lot about grass in this video and if you're looking for a deeper dive into geometry here to Grass check out the video I mean about breath of the Wild's grass here thanks to all of my patreon supporters I am so sorry for the wait between videos but my PhD is over I'll be sure to help them out quicker see you in the next video whenever that is I think I'm gonna go touch some brass now
Info
Channel: Daniel Ilett
Views: 42,339
Rating: undefined out of 5
Keywords: unity, unity urp, universal render pipeline, shader, shaders, unity urp shader, unity shader, grass shader, unity grass, geometry grass, geometry shader, tessellation grass, tessellation shader, compute shader, compute shader grass, procedural rendering, procedural grass, unity terrain, terrain grass, billboard grass, unity billboard, billboarding, impostors, unity impostors, impostor mesh, impostor grass, zelda botw grass, pokemon grass, grass technique, grass method, hlsl
Id: uHDmqfdVkak
Channel Id: undefined
Length: 24min 10sec (1450 seconds)
Published: Sat Dec 03 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.