UE5 Procedural Grass Using Bezier Curves pt. 7: Optimizations

Поділитися
Вставка
  • Опубліковано 27 вер 2024
  • Here we scale up from 500k instances to 8 million, and look at a first pass for optimizing our grass setup by adding LODs, controlling shadows, setting a graduated culling system, and also prevent clipping and unnecessary instances by allowing meshes or volumes to block grass from spawning.

КОМЕНТАРІ • 30

  • @tuna1867
    @tuna1867 2 місяці тому +5

    Amazing! Thank you very much for sharing all these tutorials!

    • @tuna1867
      @tuna1867 2 місяці тому +3

      really appreciate you explaining all these nuances

    • @TechArtAlex
      @TechArtAlex  2 місяці тому +1

      Sure thing, thanks!

  • @topraktunca1829
    @topraktunca1829 10 днів тому +1

    First of all I want to thank you and say these tutorials are one of a kind on this topic. I have never seen a video covering foliage in this much detail. And explaining everything crystal clear. Also my pc is not that great and the meshes caused some frame drops, at first I thought this is normal. But I experimented and found out that using hierarchical instanced sm instead of normal instanced sm makes a day and night difference fps drops completely gone and noticeble performance boost. When you freeze the rendering you can see hism does culling more aggresively. Instanced sm does that too but some kind of a half loaded thing idk (mesh is loaded but probably just not rendered in).

    • @TechArtAlex
      @TechArtAlex  9 днів тому

      Thanks!
      On my machine it didn't make any difference what type of instance I selected, but yes in theory HISM or Foliage instances should perform better than standard instances. They also cull in grid clusters instead of individual instances which is faster, but in some cases you may notice a blockiness to the culling.

  • @christopherfrancique9912
    @christopherfrancique9912 Місяць тому +1

    bro looks so good coming along so well

  • @Stygmire
    @Stygmire Місяць тому +1

    Thanks-again.
    FWIW, and it might be my specific implementation + stupid-compiler-tricks but I was able to save a few (3) instructions by swapping-out the MakeFloat2 for Wind_Speed_X and Wind_Speed_Y for a 4vec parameter, using a component-mask off the top (white) pin and the B channel for the Wind_Speed_Multi parameter.
    YMMV, but try it; every savings helps!
    I find, generally, any time you use an append-vector it will cost you at-least-one instruction. It's part of my performance pass to see where I can eliminate such ops and pack values. Not always, for sure, but more often than not, I find I can scrounge a decent savings when doing so.
    Also, my understanding is that a power() function is just-expensive to load and run, not something that is reflected in the instruction-count. Unless one uses a large/very-small value for the exponent; eg: it's better to multiply-out a power-3 or power-4 vs using a power() function (confirm/thoughts)? For the power-functions on the camera-tilt, I swapped out the power(0.5) for a divide by 0.1 -> Saturate (seems close-enough in the visual comparison of the gradient), and the power(0.33) for a divide by .025 -> Saturate. Doing so saved me several more instructions..
    Additionally, as I have grass going out to ~500 meters (I mean how much do you really need), and at that point it's almost sub-pixel size, I added a distance-gradient to scale-up the WPO operations (straight multiply) for the final bezier-curve, the tilt-power, and the y-scaling of the mesh. At a very-far distance this helps 'fatten' the grass and back-fill screenspace, helping to provide good mesh coverage/saturation at a distance. Even just doubling it, at that distance you wouldn't notice the scaling of the mesh and it will keep more of the mesh in the visual-field. If you just multiply the last-part of the WPO-chain, it will mush the grass at a distance, so you have to spot-multiply those three places.
    EDIT: I moved the scaled WPO multiply to earlier in the tilt-calculations so it increases the amount of tilt based on distance vs just increasing tilt overall. Dumb on my part...
    Finally, I enabled the Early-z-pass option for masked-only in project-settings, switched the grass-shader from opaque to masked, and plugged in a smooth-stepped PerInstanceFadeAmount into opacity-mask. This did garner a few extra FPS in testing, and made a very nice fade out you can't really notice.
    I added a shaderbin link: blueprintue.com/blueprint/510z4it9/ (updated!)
    Hope these help, and keep the lessons coming! :D

    • @TechArtAlex
      @TechArtAlex  Місяць тому +1

      I have seen append go either way when it comes to instruction counts. Vector packing can also go either way in frame times, but I'd still consider it good form.
      That said, in a complete implementation wind params probably should be coming in from a collection parameter node anyway as it will probably be part of a larger global wind system so those were all just placeholders since I don't plan on building all of that out.
      Pow is in theory more expensive, but in practice most compilers are doing lots of automatic optimizations that may make that mostly irrelevant. For example if you put in a power of 2, it'll generally just multiply by itself anyway - although there are more sophisticated tricks being used as well. But it doesn't hurt to avoid potentially costly operations.
      I'm always quite skeptical of instruction counts, but if we can observe a frame time reduction then I'm all in. Like you said - doesn't hurt to try and see what happens. I'll have to test these changes out.
      Grass takes up a lot of screen space, and in this case has lots of verts, so every little bit counts.
      As for scaling the grass at a distance, I think this is generally a good approach. It is basically what nanite volume preservation does - and it is what outerra proposed in their blogpost that inspired Sucker Punch. Every time they cut the polygons in half, they double the surface area of the remaining grass. This can help reduce quad overdraw, reduce aliasing and lots of other goodies.
      Ideally I'd cull much more aggressively than I did in this demo, but I kind of wanted to show off a ridiculous number just because I figured people would want to see it haha.
      I've also been experimenting with importance based culling, but I'll probably make a separate video on that rather than bundling it in this series. Basically, rather than using distance alone, we can assign an importance value to grass based on things like proximity to a mesh. For example, if the grass is near a certain mesh, it should be culled later - since that grass is visually more important to the scene as its silhouette is contributing much more detail than a random blade in a field. The result is quite good I think. Another example would be that grass on a hilltop is more important than grass in a rut, although that's a bit harder to procedurally define.
      As for culling, I prefer to shrink and/or sink the grass blades while blending their normal towards the underlying terrain. This avoids the need for a masked material while still making the transition basically invisible. When I get around to the next video, it will probably be about blending out in more detail, and how to get the underlying terrain normal vector without needing to sample a virtual texture (thanks once again to PCG data packing).
      Thanks for the feedback and sticking around!

    • @Stygmire
      @Stygmire Місяць тому +1

      @@TechArtAlex >as for culling, I prefer to shrink and/or sink the grass blades while blending their normal towards the underlying terrain.
      How does this work? I have been informed/learned that using WPO to move a grass mesh, for example 10,000 down the Z-axis so it's culled and never drawn. Is flattening the mesh the same kind of thing? Does it prevent the mesh from being drawn in the first place?
      Genuinely eager to learn. From what I recall Final Fantasy had a similar/same thing with moving particles around the camera to be culled. Is flattening the grass the same thing?

    • @TechArtAlex
      @TechArtAlex  Місяць тому +1

      @@Stygmire Pretty much, yeah. Using the vertex shader this way doesn't completely remove the mesh, because its verts technically still exist. However, because they occupy zero screen space, there is no fragment/pixel shading happening. And because they're instances, the verts were cheap anyway.
      This is the same reason why the extra/wasted triangle is basically free. Yes that vertex exists, but ideally no fragments are being shaded.
      If the grass sinks below the ground, then the z- pass can make sure the fragments are skipped too.
      Eventually the instance is entirely culled, but by that time it is already invisible. Using several cascades like I have cleans up the nullified meshes regularly to keep the instance count down, even if many are not really drawn.
      I tried assigning random cull values on a per instance basis, but it was not scalable to this many instances. But that might work for other things.

  • @dcrookz
    @dcrookz Місяць тому +1

    I'm interested in the transition to grass textures at distance that you mention here, if you've got the juice to do another video on grass!

    • @TechArtAlex
      @TechArtAlex  Місяць тому

      Still planning on it! Just needed a bit of a break haha. Maybe sometime this week.

  • @medmel2160
    @medmel2160 Місяць тому +2

    This is more and more looking like Ghost of Tsushima grass! Keep it up!

  • @UnrealArtist
    @UnrealArtist 2 місяці тому +1

    Great tutorial, I guess its the final video for this series? I have been following this series since the start. One question. Why are you using PCG? Why not traditional landscape mesh spawning system for certain material layer? And what if we want to universaly control the direction and speed of wind for grass and other foliage meshes? Like in the ghost of tsushima? I would love to see your approach oin this.

    • @TechArtAlex
      @TechArtAlex  2 місяці тому +5

      You certainly can use more traditional methods like landscape grass types, but PCG gives a lot of extra control. PCG allows us to communicate more than just landscape layer/material data to the grass, but basically anything we want - without needing to compute it every frame or bake it into a texture.
      For example, culling the grass in spots that overlap scenery meshes could be done with a virtual texture, but sampling virtual textures incurs a runtime cost.
      For controlling wind variables globally, you'd just want to set up a material parameter collection and set the values with bp/code instead of setting the parameters in the material instance.

    • @TechArtAlex
      @TechArtAlex  2 місяці тому +6

      And I am planning on doing one or two more.

  • @halftempted1047
    @halftempted1047 2 місяці тому +2

    Do you intend to cover grass interaction?

    • @TechArtAlex
      @TechArtAlex  2 місяці тому +4

      Not currently planning on it, but if I did, it'd probably be done in the conventional way - with a render target.

    • @Cloroqx
      @Cloroqx 2 місяці тому

      @@TechArtAlex Yeah, grass interaction with this setup would be interesting to see.

    • @halftempted1047
      @halftempted1047 2 місяці тому

      @@TechArtAlex If you can find the time, I’d love to see your implementation. Your ability to maintain performance is refreshing in comparison to others.

  • @avilxy8305
    @avilxy8305 Місяць тому +1

    Hello Alex, I am loving this series and have managed to get a similar result to yours but I would like to add player interaction. I have created an interaction system that works on most foliage but when I add it to the WPO output of the grass it produces a very strange stretching/deformation affect. I believe this is because I am not controlling the individual control points. Any ideas on how I could go about adding a normal WPO interaction to this grass?

    • @TechArtAlex
      @TechArtAlex  Місяць тому +1

      In theory as long as it is the last thing you do, after applying all other modifications, you should be able to use regular deformation. But I would strongly recommend using and shifting the control points with your interaction system instead. This will make sure you have correct normals, and we have built in the length preservation system already it can take advantage of.

    • @avilxy8305
      @avilxy8305 Місяць тому

      ​@@TechArtAlex I can't work out how to directly affect the points. Do u have any idea on how I can apply normal WPO (regular deformation) such as simplegrasswind to the grass blades. Using an add node does not work as it creates the same stretching affect. Any rough idea would be appreciated.

    • @TechArtAlex
      @TechArtAlex  Місяць тому

      @@avilxy8305 Directly applying to the points would be done in exactly the same was as the wind video. In that case we added and subtract from the control points coordinate based on the wind sine wave. But we can also push it based on things like our interaction system render target.
      The Simple grass wind always stretches meshes, which is part of why I recommended against using it in the wind video in favor of my method, which is capable of estimating and preserving approximate length.

  • @ScorpyX
    @ScorpyX 2 місяці тому +2

    Gold information

  • @BooneyTune
    @BooneyTune 2 місяці тому +2

    I would like to see more on the optimization. I don't have much experience with it but this is great tutorial series to get to know more about it and learn.

    • @TechArtAlex
      @TechArtAlex  2 місяці тому +1

      I'm not sure there's a whole lot more that can be done besides controlling these elements more tightly or cutting features.
      For example, one can cull the grass much more aggressively than I did. Ghost of Tsushima culled 75% of blades at each cascade, whereas I'm only culling 25%.
      But if I think of any easy performance wins I'll make sure to mention them.

  • @zeon3d755
    @zeon3d755 28 днів тому

    where can i get the code pls

    • @bartosztitkin2700
      @bartosztitkin2700 22 дні тому +1

      Make it yourself from part 1 to achieve knowledge and understand how it works!