Optimizing art early, before you have a good sense of where the actual expense of rendering your scene is, can be a pretty bad idea.
So let’s do it!!
I’ll do it #procedurally.
20 gallons of evil per pixel
My ground shader is pretty expensive. It’s blending all sorts of things together, currently, and I still have things to add to it.
I don’t want to optimize the actual material yet, because it’s not done, but it looks like this and invokes shame:
As a side note here, this material network looks a bit like the Utah teapot, which is unintentionally awesome.
Every pixel on this material is calculating water and dirt blending.
But many of those pixels have no water or dirt on them:
So why pay the cost for all of that blending across the whole ground plane?
What can I do about it?
Probably use something like the built in UE4 terrain, you fool
Is probably what you were thinking.
I’m sure that UE4 does some nice optimization for areas of terrain that are using differing numbers of layers, etc.
So you’ve caught me out: The technique I’m going to show off here, I also want to use on the walls of my factory, I just haven’t built that content yet, and I thought the ground plane would be fun to test on 🙂
Back to basics
First up, I want to see exactly how much all of the fancy blending is costing.
So I made a version of the material that doesn’t do the water or the dirt, ran the level and profiled them side by side:
^ Simple version of my material vs the water and dirt blending one.
So, you can see above that the material that has no dirt/water blending is 1.6 milliseconds cheaper.
Now, if I can put that material on the areas that don’t need the blending, I can’t expect to get that full 1.6 milliseconds back, but I might get 1 millisecond back.
That might not sound like much, but for a 60 fps game, that’s about 1/16th of the entire scene time.
Every little bit helps, getting that time back from cutting content alone can take many hours 🙂
Splitting the mesh
To put my cheap material onto the non-blending sections, I’ll split the mesh around the areas where the vertex colour masks have a value of 0.
Luckily, the ground plane is subdivided quite highly so that it plays nice with UE4 tessellation and my vertex painting, so I don’t need to do anything fancy with the mesh.
Back to Houdini we go!
So, anything that has > 0 sum vertex colour is being lifted up in this shot, just to make it obvious where the mesh split is happening.
Here’s the network:
The new nodes start at “Attribcreate”, etc.
The basic flow is:
- “Colour value max” is set as max(@Cd.r, @Cd.g), per point, so it will be set to some value if either dirt or water are present.
- Two new Max and Min attributes per polygon are created by promoting Colour Value max from Point –> Polygon, using Min and Max promotion methods (so if one vertex in the polygon has some dirt/water on it, the then max value will be non zero, etc)
- The polygons are divided into three groups: Polygons that have no vertices with any blending, Polygons that have some blending, Polygons that have all verts that are 100% blending.
- NOTE: For the purposes of this blog post, all I really care about is if the Polygon has no dirt/water or if it has some, but having the three groups described above will come in handy in a later blog post, you’ll just have to trust me 🙂
- The two groups of polygons I care about get two different materials applied to them in Houdini.
When I export them to UE4, they maintain the split, and I can apply my cheaper material.
So, re-exported, here it is:
Great, mission successful! Or is it…
Checking the numbers
Back to the GPU profiler!
Ok, so the column on the right is with my two materials, the column in the middle is using the expensive material across the whole ground plane.
So my saving was a bit under one millisecond in this case.
For an hour or two of work that I can re-use in lots of places, I’m willing to call that a success 🙂
Getting more back
Before cleaning up my shader, there’s a few more areas I can/might expand this, and some notes on where I expect to get more savings:
- I’ll have smaller blending areas on my final ground plane (less water and dirt) and also on my walls. So the savings will be higher.
- I might mask out displacement using vertex colours, so that I’m not paying for displacement across all of my ground plane and walls.
The walls for flat sections not on the corner of the building and/or more than a few metres from the ground can go without displacement, probably.
- The centre of the water puddles is all water: I can create a third material that just does the water stuff, and split the mesh an extra time.
This means that the blending part of the material will be just the edges of the puddles, saving quite a lot more.
So all in all, I expect I can claw back a few more milliseconds in some cases in the final scene.
One final note, the ground plane is now three draw calls instead of one.
And I don’t care.
So there. 🙂