Bug – XboxOne:
Strange lighting/shadow artifacts on our crystals in the Xbox build (not on PC or PIE). Solution:
The crystals had a negative scale, which somehow broke the lighting. After double-checking all shader properties, quality settings and lighting settings i figured the only difference between working and broken crystals was the negative scale. Thx once again Unreal. Time spend: 2 days.
Bug – XboxOne: Skewed geometry, e.g. arrow blueprints in our menu scene.
The arrows had negative scale inside the blueprint and inside the scene in addition to a 180-degree rotation (whyever we did that). Changed the scale in the scene back to positive (component scale in the blueprint is still negative) and reset all rotations of the components back to 0. Still not quite sure what caused the bug exactly, but it seems it has something to do with component rotation and scaling inside a blueprint in addition to scaling and rotation inside the scene. Time spend: 4 hours.
I’ve been doing some experimenting lately and wanted to show you some of my results. Shannon Berkes comment about using the shit out of gradient mapping made me think about how to use it inside the engine with particles. I’m certainly not the first one to try this, but i’ve never seen someone post anything about it, so i made a small tutorial on how to do it in UE4. I think this is super useful and offers new possibilities color-wise. Not the best example, but you can see some of the gradient mapping in action here:
And here are some simpler examples in which I use random gradients to change the color of each particle. It’s just two mesh particles with some noise texture panning applied to it.
*Each GIF fires several times before looping, notice the slight variations in color each time the effects fire*
Before i go into any further technical detail, here is a list of pros and cons of this method:
More color variation without the loss of detail
Particles not bound to one particular color texture
Less memory needed than with a regular color texture
Sometimes difficult to map the gradient colors exactly to what you have in mind
Particle shader is more complex
Color changes are not done directly inside the engine, but in photoshop, which can be more time consuming
Aight, let’s get started.
First, we want our textures to be black and white *doh*, with as many in-between grey values as possible. This will obviously also depend on what kind of effect you are making, but i usually like to use more shades of grey, to get more color awesomeness in the effects later on.
Depending on what you want to achieve you can minimize texture usage quite a bit here. We could for example store four different particle textures in our RGBA channels, if you are using premultiplied alpha, two textures + alpha. -> Profit.
I will just use a faded out ring texture, with some noise and blur applied to it, to get nice soft grey values. That’s all, for simplicity of this tutorial.
After creating our b&w texture, we create our very first gradient map *uhhh*. Like the example map in Ryan James Smith’s gradient map tutorial (Gradient Mapping – An Awesome Way to Get Cheap Variation in Textures), i’ll go for a map with four different gradients.
So let’s create a new texture, mine has a size of 256×64. You could go smaller than this (e.g. 256×4), but we’ll leave the size like this for now, you will see later on why i prefer a height of 64px.
This is how my gradient looks like, i just went crazy with Photoshop gradients and create two different gradient maps:
Ok. We are done with Photoshop and switch to Unreal.
Inside Unreal, we will setup a basic particle material and add the gradient mapping to it (use the texture and your gradient map we created earlier here).
I’ll just set the Blend Mode to AlphaComposite, Unlit and Two Sided. I will not go more into detail here, this is basics. After your basic particle setup, add this for the gradient mapping:
I added some more details like an alpha dissolve, you can copy+paste my material, if you want to: GitHubGist or download the .uassets here on Dropbox.
One important side note: look at the dynamic particle parameter, which allows us to control the GradientIndex inside our particles later on.
After material compilation, create a material instance and we can already see the results of the gradient mapping by changing the GradientIndex parameter (values between 0-1 e.g. 0.7) *kazam!*
Here is another example with the second gradient map and a noise texture.
Okay, once done with the awwwmazement of changing the GradientIndex, we change it back to 1.0 and create a ParticleEmitter, adding the material to it.
I’m going for something simple here, just one particle, growing in size, with an alpha fade out. When you are happy with your emitter, add a dynamic parameter to it. Open the first section there and change the Distribution to Float Uniform between 0-1, also check SpawnTimeOnly = true.
Congratulations! Your particles now randomly use one of the four gradients of your gradient texture each time they are spawned.
This might look strange sometimes, depending on your gradient, just adjust your gradient in Photoshop as needed. Here is what mine looks like:
The amazing thing about this: the color details in your effects will be preserved, they can have multiple complementary colors within one emitter, and can be changed to something completely different with the blink of an eye. In addition, you can have more color-variation, but use LESS TEXTURE MEMORY! How cool is that?
We can also do fancy stuff, like changing our Dynamic Parameter via Blueprints (for example using a trigger -> changes color of effects (by changing GradientIndex).
One last thing i want to show you is changing the GradientIndex over lifetime. Let’s do this by changing our Distribution in the Dynamic Parameter to Float Constant Curve, add two Points (0,0 and 1,1)
and untick Spawn Time Only. That’s it, our effect changes gradients over lifetime now. You will notice the sudden changes from one gradient to the next though.
What i did to smooth out the transition, was *who would have thought* to blur the gradient map. After a quick Gaussian Blur, my gradient looks like this, which results in a much smoother transition:
The blurred gradient is also quite useful for smooth transitions set inside Blueprints (for example using an objects Velocity (normalized) to control the GradientIndex). Imagine a Spaceship engine-fx going from orange to blue, the faster the ship.
This is what my effect looks like with the blurred gradient and some slight curve tweaking:
Thanks for reading, i hope this was helpful and will spark some new ideas in the near future.
UPDATE: Since some developers remarked on this, this guide is for indie devs (who not necessarily have their own engine programmers dedicated to ensure performance optimization). It might not be relevant for large studios.
UPDATE2: With Unreal 4.14, the forward renderer came out. We are using it for Dashing Dinos and saved ~ 5-10ms. It nearly looks the same, sometimes even better than before, because we can go full 1080p (r.ScreenPercentage=100). The only drawbac, -> no SSAO.
When porting our game to XboxOne, we realized our existing graphic settings resulted in 20FPS (down from 90FPS on one of our killer-PCs). Dashing Dinos needed to run with min 60FPS in order to feel smooth. This proposed a huge challenge, because we obviously didn’t want to lose too much visual quality. Multiple posts already address how to improve performance within UE4, especially since VR got bigger and bigger, but this detailed post will hopefully be helpful for some people struggling with the same issues anyway.
First off: this guide focuses on GPU Performance Optimization, not CPU. No changes to the engine were made for this guide and you should be able to follow easily without any C++ knowledge. You should however, know how to profile your game’s performance. If you don’t, take a look at the thorough documentation from Epic here: Performance Profiling.
Check what limits your performance with the “stat fps” command and if you are GPU bound you’ve come to the right place. Don’t forget to use the GPU Profiler (Shortcut: Ctrl+Shift+,) from time to time when tweaking your performance.
Quality Settings (~80 FPS)
Before we dive into concrete config variables and scalability settings, we quickly discuss how UE4 prioritizes different methods to set these values. Values set with a lower priority will always be overwritten by higher ones, which you should keep in mind when creating different quality levels for your game. Here is the priority order from highest to lowest priority:
The DefaultDeviceProfiles.ini is a config file inside your projects’ config folder (e.g.: \DashingDinos\Config\DefaultDeviceProfiles.ini). If it doesn’t exist, just create it. You can either set commands directly inside the .ini file or within the editor. Setting it in either is directly related to the other. Here is an example of how you can access the file inside the editor and how the config file looks like within a text editor like Notepad++.
The DeviceProfile commands override all other commands, so be considerate about what you set there. Hence, we use the DeviceProfiles nearly exclusively for XboxOne (which only needs one level of quality). For PC we want the users to be able to switch quality levels, depending on their hardware, and therefore need to set the quality values through console commands/scalability. Only commands shared by all quality levels are written within our Windows DeviceProfile:
As you can see, scalability commands are being set first (sg.xxx) and individual values will be changed later. When just looking at the scalability settings, one would assume the settings are quite high quality (Effects-, PostProcessing- and TextureQuality = 3), however, most of these settings get overridden later by our individual commands at the bottom.
With all these values it’s a trade-off between visual quality and performance, especially “r.ScreenPercentage”. Setting the value down to 85, impacted our performance tremendously. By rendering less pixels and up-scaling the rendered image you will obviously have quality loss, but we tried to compensate this as good as possible by increasing AntiAliasing and TextureQuality. Just check how low you can set “r.ScreenPercentage” without losing too much visual quality and watch your FPS skyrocketing.
PC – Quality Levels
Let’s get back to Windows, where everything is a little bit more complicated. Players have many individual hardware setups, on which your game will perform completely differently. This will bite you in the ass, nothing you can do about it. What we as developers can do however, is to give players the possibility to change settings according to their needs, to ensure a smooth playing experience. We didn’t want hundreds of individual settings for Dashing Dinos and therefore created some presets for different levels of quality.
Take a look at the code on the left if you are interested in how we create the Data Assets. The Data Assets just hold individual quality settings which can be applied within our graphic options.
You might notice that the values we set inside the Data Assets are quite similar to what we did within the DefaultDeviceProfile.ini for XboxOne. However, this system is much more flexible for different levels of quality, which is exactly what we need for PC.
If you are interested in the individual values for our quality levels or if you want to know how we applied the settings, write in the comments and we will add this information here.
We are done with the main optimizations at this point. The interesting part comes now, where we try to optimize the not-so-obvious stuff.
Advanced Improvements (~55 FPS)
Post Processing and Particle Effects (~10 FPS)
By adjusting some of the Post Process settings in our quality levels/ DeviceProfiles, we already took care of most performance killers related to PP effects. However, for example in Dashing Dinos, we wanted to disable Depth of Field completely. Unfortunately we had one particular scene where we needed DoF and could therefore not disable it within our settings by default. Some PP effects should also vary from platform to platform and just to have a little more flexibility, we created two seperate PostProcessVolumes for PC and XboxOne. We then enabled and disabled them depending on the platform. This is an example of one of our LevelBlueprints where we do exactly that:
In addition to the PP effects, we disabled most of the environmental particle effects on XboxOne. Dust and fog particles for example, are quite expensive due to the overdraw. Enable the Shader Complexity View Mode in the viewport to better see how costly your particles are.
Lights & Shadows (~40 FPS)
Dynamic lights and shadows are a performance killer. Try to avoid them at all costs. Bake everything you can and only use static and stationary lights. We have no dynamic lights or dynamic shadows in Dashing Dinos at all! Some of our lights are set to stationary in order to get nice reflections on ice, crystals and rocks, however, dynamic shadows are disabled in all of them.
All character shadows for example are faked with decals. Whether or not you can do this is obviously totally dependent on the game you create. It will never look the same as with real shadows and you will lose visual quality, but with an isometric perspective like ours, the differences were minimal and the performance gained was massive.
Translucency (~5 FPS)
Translucent objects are expensive, so always try to minimize overdraw. This applies to particles as well. Unreal handles tens of thousands of triangles quite well, so creating some extra meshes to reduce overdraw might be a good idea in some cases. Take a look at our logo below, where we used a unique mesh instead of just a plane, in order to reduce massive overdraw.
We hope this will be helpful for other developers struggling with GPU bound FPS performance issues, if you have anything to add or thoughts for improvements please let us know in the comment section below.
Btw, these are the current FPS on our PCs (CPU: i7-4790K, GPU:GTX 970) and on the XboxOne:
XboxOne: 60 FPS
PC highest settings: ~90 FPS
PC lowest settings: >200 FPS
To round this up, we created screenshots for different quality levels and platforms, which is what you see below: