April 24 2023

Forward+ (Plus) Rendering in Unity URP 14+ - Shine Your Lights!


So, you want to make a game where you have tens or hundreds of real-time lights, huh?

Good luck with that, my friend.

No, I’m joking. You don’t need luck, you need knowledge.

So, at some point, when you are a game developer, you might want to make a game, or your boss will tell you to make a game that has a lot of lights, right?

Like look at this scene that I have here. Tons of juicy real-time spot lights.

Too many real-time lights: a problem in URP Forward?

Is this scenario actually doable?

As you know, we have mainly three pipelines in Unity: Built-in, URP and HDRP. Currently, we’re talking about URP.

HDRP is great with deferred shading, as we can virtually have an endless amount of lights. But deferred shading doesn’t scale well with tile-based renderers, that’s why we don’t use HDRP over there. Sorry!

In URP, you are stuck with forward rendering, right?

Are you? … Are you really?

Let’s have a look, because Forward Rendering is MAYBE not the only thing that you have.

If I go to the project settings, you will see the rendering path. Here’s how it looks on my end.

URP Forward: not particularly liking having over 8 lights projected onto a single object

Traditionally, for mobile we always choose forward rendering. Which means: every time that you draw an object, e.g. this floor, you send it to the GPU pipeline and it goes from zero to finish. Rendering this floor sequentially follows several steps: vertex shading, rasterization, fragment shading, blending, etc..

So the rendering of your objects just goes forward.

With forward rendering, performance is really good when you have a little amount of lights, but when you go over a certain amount of lights, complexity and performance explodes. BANG. In fact, you have an upper limit of eight real-time lights that may affect a single object at a time.

Like, just look at the earlier screenshot. I have over 30 lights, but how many do you see? Not as many.

There’s a limit to this because we can’t pay enough performance currency to render so many real-time lights in the forward rendering path in URP. I elaborate on the reasons behind this on the Performance Taskforce membership.

So, what can you do when you reach this limit?

  • Make it work somehow. Reduce the number of lights or affect the lights. Or implement your own shaders that do not use Unity’s internal lighting setup.
  • Make more lights static.
  • Whatever else. Just make it work within the budget. This is on you.

But this becomes impossible in some situations.

Have you played Quake-like games? You can shoot rockets at your foes. These rockets emit light. So guess what happens when 20 players shoot every second. 40 lights affecting the floor, yummy.

Okay, so where do we go from here?

  • 1) go for deferred shading, which is not really a viable option for mobile for tiles based renderers. I explore why on the membership. I mean, you can still make it work, but you must be a very experienced rendering programmer to pull that off.
  • 2) use the new toy coming with Unity 2022.2 beta and URP >= 14. Whatever the numbers, I don’t care, they change more often than I change my tshirt.

This toy is a new render technique called Unity URP Forward+ (Plus). Just go to your URP asset and change your rendering path to Forward+ like below.

URP Forward+ (Plus): big brother is obviously happier with so many lights than Forward

Magic.

The limit of eight real-time lights per object is not there anymore. Now it is… well, it depends. 16, 32 or 256 real-time lights PER camera depending on your target hardware and graphics API.

So just select Forward+ (Plus) in URP and forget?

Not really. I suggest you to understand how the Forward+ rendering path works in URP in comparison to Forward. Because it has other sort of performance implications, as I explain in the Performance Taskforce membership.

The summary is:

  • Do you own research.
  • In my own research as of today (not as of the day you’re reading this), Forward+ pays off when you go beyond 6 real-time lights. Otherwise it is more expensive than Forward due to the clustering algorithm that this render path introduces.

Forward+ (plus) aggregates all this light information into clusters so that in the fragment shader, you just need to iterate over iterate over these clusters. And then it becomes easy&fast to do the light calculations in there. Instead of iterating over every light, we just iterate over clusters, reducing the data set that we have to access in memory on the GPU.

I encourage you to understand how this works and then do your own research because there are many options other than just blindly following the advice of a random guy on the internet (that being me in this case).

I hope I helped you become aware of alternative solutions other than the boring Forward rendering path.

If you want to see more stuff or more numbers or more explanation and more technical background and all of that, you know where to go to (Performance Taskforce membership) or book a consulting call with me.

I hope you have a good day and happy game developing. And make sure to make your players happy with nice visuals.

Ruben (TheGameDev.Guru)

The Gamedev Guru Logo

Performance Labs SL
Paseo de la Castellana 194, Ground Floor B
28046 Madrid, Spain

This website is not sponsored by or affiliated with Facebook, Unity Technologies, Gamedev.net or Gamasutra.

The content you find here is based on my own opinions. Use this information at your own risk.
Some icons provided by Icons8