Choosing Between Forward or Deferred Rendering Paths in Unity

One of the most important Unity features is the ability to choose a rendering path.  For those who are not very familiar with Unity, choosing (usually) between forward and deferred rendering paths may be something comparable to choosing between “normal”  and “strange looking and something’s broken” rendering methods. To understand better why there is more than one rendering path, first you will need to understand the motivation behind it.

It’s all about lighting

Lights are expensive, mostly because a lot calculations has to be done to find out the valid color of a pixel when there’s a light in range. In Unity lights can be evaluated per-vertex, per-pixel or as Spherical Harmonics (SH). In this article we will talk only about the former two.

pixel vs vertex lighting

In per-pixel lighting, each pixel color is computed individually (as on the left.) You can see that even when I use low-poly sphere for this example, the lighting still makes it look round. If it wasn’t for the edges, it’d be really hard to spot where all the vertices are. Then, there’s per-vertex lighting. It makes one light calculation per vertex. All the other pixels between vertices evaluate the color using regular color blending algorithm (without further light calculations.) This is the cheapest method of lighting and yeah… it looks cheap (if you’re wondering where’s the pixel and vertex lighting switch, it’s hidden in the Light component under Render Mode option. Important option is forces the light to be pixel light, Not Important is vertex light, Auto makes the strongest light a pixel light.)

It’s not a secret that game developers love per-pixel lighting much more than per-vertex lighting. Yet it has a significant downside. Each light causes the additional rendering pass of each object in the range. There’s a limit of four lights that can affect the object. What’s more, there’s also a limit of shadows – based on Unity documentation only one light can have shadows (for some reason I’ve managed to get two shadows in Unity 5.3.4, so I’m not really sure about this one.)

Deferred rendering to the rescue!

There’s a technique that allows you to use as much lights as you want on your scene with keeping the performance at reasonable level. It does not limit the number of shadows and it does not cause additional draw passes if scene objects are within light range (objects casting shadows are exceptions.) It’s called Deferred Shading Rendering Path.

4 lights deferred lighting

Why is it so different? Mostly because most of the models are rendered without lighting calculations and when the scene rendering is nearly done lights are applied to rendered 2D image. Making changes on this stage is usually called doing something in screen-space. Knowing that, we can say lighting in deferred rendering is screen-space. To understand it better, let’s look at the Frame Debugger.

Scene rendering starts with rendering all geometries:

deferred opaque

This is a flat image, so how graphics card will know how to apply lights and shadows? Thanks to the depth buffer! You can think of depth buffer as of another image that is hidden from you and that stores the information about how far from the camera each pixel is located. When represented as image, it may look like this:

cf538839-afaf-4afb-911b-52db5c062af7

Depth information alone isn’t enough to figure out how light should be applied on the surface. Still, we need at least one more thing – the orientation. Orientation in 3D space is usually represented by normals. The unusual thing is that along with color buffer and depth buffer, there is a buffer with normals!

346210b1-c144-4a66-b1bc-bd7488e3b06b

How can you tell that these are normals? It’s pretty easy! Just look at the Scene Gizmo.

scene gizmo

Do you see the color resemblance? Red cone (x) points to the left, so do left faces on previous image. Green (y) to the top and blue (z) bottom-right (from this perspective). It all matches the colors of faces from before.

Basing on that information, lights and shadows can be rendered. It really doesn’t matter how many objects there are on your scene. Everything gets done only on the final image.

After lighting pass

After lighting pass

The image above is the result is an inverted version (1 – color) of lighting pass. At the end it is blended with the first opaque image to get the final result.

Which one should I choose?

After reading all of this you may be full of enthusiasm to use the new rendering path, but hold your horses! Deferred rendering is not a remedy for all of the world problems. It has some…

Limitations

It would be too great to be true, wouldn’t it? There are some limitations.

First of all, deferred rendering does not allow us to render semi-transparent objects. That’s because if something semi-transparent exists on the scene, there’s no way to write down depth and normals for objects that is visible through semi-transparent objects and for current object itself. Unity handles this limitation rendering semi-transparent objects using forward rendering path at the end of the whole process. It works quite well, these objects can cast a shadow, but unfortunately they are unable to receive shadows from other objects. They can also cause some unexpected issues, not known when using forward rendering.

The second limitation is the lack of anti-aliasing support. The reason is similar to the issue with semi-transparent objects, but Unity does not try to workaround it in any way. Instead you can use screen-space AA algorithms (image effects), but the visual effect may be less good-looking.

Another limitation is that you can use up to four culling masks. In the documentation you can read:

that is, your culling layer mask must at least contain all layers minus four arbitrary layers, so 28 of the 32 layers must be set. Otherwise you will get graphical artifacts.

And finally there’s no support for the Mesh Renderer’s Receive Shadows flag.

Requirements

If that’s not enough, deferred rendering works only on a limited set of graphics cards. When it comes to PCs, you can safely assume that all graphics cards not older than 10 years will support it. When it comes to mobile devices, you should assume nothing. But that’s not a big issue, because…

Performance

The most important thing is that deferred rendering in most cases will get a worse performance on mobile devices than forward rendering. It’s because of additional passes that need to be done on each frame. If you’re using only one light, then it may not be worth it.

On the other hand, adding extra lights is quite cheap. In the worst case scenario performance will drop linearly and compared to forward lighting, it’s independent of number of objects on the scene.

Cities: Skylines (done in Unity) decided to use deferred rendering path. There's a lot of small lights in this game and it still performs really well.

Cities: Skylines (made with Unity) decided to use deferred rendering path. There’s a lot of small lights in this game and it still performs really well.

Resources

I hope that this article will cast some light on what rendering path you should choose for your game. Anyway, you may also be interested in these resources:

Steamworks and Unity – P2P Multiplayer

share_steam_logoSome time ago we talked about how to integrate Steamworks.NET with Unity game. It is a good start, but now let’s go further and talk about multiplayer in Steam. This won’t be a tutorial, but a guide on using Steamworks in your own game. We will be using Steamworks.NET library that is a wrapper to steam_api.dll.

Please note that you need a Steam App ID of your game to get it work. You can get one by getting greenlit or by having your game approved directly by Valve. Still, this is a guide, so if you only want to check if Steam is a convenient platform, just read on…

P2P Multiplayer

One of the greatest features of Steamworks is matchmaking and P2P network communication. You don’t have to worry about servers’ setup – all the things are already there.

You may not be familiar with building a multiplayer game using P2P connection, because the most popular approach involves setting up client and server as a separate entity. In this situation client is the game itself and the server is an application that contains server-side logic, connects all the players together and protects them from cheaters. Since client-server scenario may be good for bigger games, for something relatively small and less competitive consider dropping-off the server part for the favor of two clients speaking with each other. Server-side logic will be included only in a single entity, so you won’t have to worry about writing additional applications to make it work. For that reason we will put P2P Steamworks communication in good use.

Is it really that easy?

Steamworks is trying to be as simple as it can be. You don’t even have to worry about making a connection, all you need is a SteamID (SteamID is a unique steam user identifier. Usually it is a big number wrapped inside a CSteamID object. You can easily get a CSteamID of any user you’re interacting with, for instance through the lobby.) When you have it, all you need is to execute this method:

pubData is the data we want to send, cubData is the number of bytes we want to send, eP2PSendType is a method of delivery. Let’s not talk about nChannel, the default value is enough for now.

Here’s an example of how to send a “Hello!” string:

There are four of these regarding the delivery method:

  • k_EP2PSendUnreliable – Small packets, may get lost, can arrive out of order, but fast.
  • k_EP2PSendUnreliableNoDelay – As above, but won’t do any connections checks. For this purpose it can be thrown away, but it is the fastest possible method of delivery.
  • k_EP2PSendReliable – Reliable message send. For big packets, will arrive in order.
  • k_EP2PSendReliableWithBuffering – As above but buffers the data before sending. Usually when you’re sending a lot of small packages that is not so important to be delivered immediately. (will be forced to send after 200 ms.)

What about the other side?

If one peer is sending the data, the other one receives it in some way. Of course there are some security precautions. You cannot send the data to any Steamworks’ client that’s out there. Before client can receive your data, he has to accept your P2P session request.

P2P session request is something that occurs when you try to send the first chunk of data to the Steamworks’ client. The process will repeat itself if you haven’t sent any data for a while (usually a couple of minutes.) You should accept only the connections you’re expecting to be made, like from another player in the lobby you’re in.

How to accept the session request? It’s really easy! All you have to do is write a code like this one:

This way a P2P session will be accepted and you’ll be ready to…

Read the message

All the messages are stored in the Steamworks message queue. To read it you have to tell it you’re ready to get it. Usually a good place to do so is an Update() function. Your application will check for new messages as soon as possible.

That’s it!

Summary

This guide does not cover cleaning up (it’s optional because unused sessions are being cleaned up automatically) and error handling. You can read about them in official Steamworks documentation, but remember that you need to be Steam partner to to have access to it. If you’re not, I hope that after our articles about Steamworks you will take into consideration becoming one.

How to Integrate Steamworks with Unity Games

Integrating Unity Games with Steamworks

I believe that many of you have thought of publishing a game on Steam. It wouldn’t be surprising, as Steam is a great distribution platform for PC and now also for Mac and Linux games. But Steam is not only about distribution. When you get approved by Valve, you gain the access to something that may help you a lot with your game development. This little thing is called Steamworks.

Steamworks features

Here’s a list of some most known Steamworks features:

  • Achievements – provide free grass roots marketing for your application. As players unlock achievements it exposes your product to their friends.
  • Error Reporting – provides dead simple error collection so that you can quickly find and fix your most common bugs. With a few simple api calls Steam will automatically collect the most common crash reports for the game or software. You can then review error reports on the error reporting page, which you can find from your application landing page in Steamworks.
  • Cloud Saves – is free storage that gives players ability to play where they choose as well as the peace of mind tha they won’t lose all the work they’ve put into your game. Cloud can also be used for software applications to store work-in-progress or special configuration settings.
  • Steam Workshop –  is a system of storing, organizing, and downloading user-created content uploaded through your application. This makes sharing custom levels, skins, or complete mods easy and user-friendly.
  • Other features to consider are stats, leaderboards, and multi-player matchmaking.
As you will accept Steamworks SDK terms & conditions you will get the access to official Steamworks SDK documentation.

As you will accept Steamworks SDK terms & conditions you will get the access to the official Steamworks SDK documentation.

Integrating Unity game with Steamworks

Steamworks SDK is distributed as a native DLL file (*.so when talking about Mac and Linux). In order to make it work with Unity you have to create a binding. Fortunately such binding already exists and it is distributed also as an easy to install, unitypackage file!

I am of course talking about Steamworks.NET. It’s an open source wrapper distributed under MIT license (you’re free to use it even in commercial projects!). The good thing about Steamworks.NET is that the authors value API compatibility over simplicity. That means that you only need a quick look over how it should be used and when you’re familiar with the concept, all you need is the official Steamworks documentation. The downside of this approach is that callback setup need one extra step, but it’s not a hassle.

Installation

To make Steamworks.NET  work you have to be a Steamworks developer and you need an AppID (this is just a number in Steam database). At the time of writing of this article you can get one after passing Steam Greenlight or by making a custom deal with Valve.

When you have acquired an AppID all you have to do is import Steamworks.NET unitypackage file to your Unity project. At the time of writing of this article the current stable version is 7.0.0, but please use installation page links instead to always get the latest version.

steamworks unitypackage installation

Steamworks.NET package includes libraries for Windows, Mac and Linux in x86 and x86_64 architectures. After importing it you don’t need to add anything else to your project. Even official Steam dll/so is included, so there are just two more steps to go.

After importing the package, a new file called steam_appid.txt will be created in your project root directory (this is the one that contains the Assets and Library folders). Open it in the text editor and replace 480 with your Steam AppID.

Finally, the last step – create a new empty game object on your scene and add SteamManager script to it. There! Now you’re good to go!

Checking to make sure it works

Make sure that Steam is running. Then create a script like this:

Add this script to a new game object on your scene and hit the Play button. If everything is OK, you will see your Steam name in your Unity Editor console!

When something went wrong you will end with an error message that may not tell you what exactly has gone wrong. If you’re working on Windows then you may want to get DebugView application. Just run it before running your Unity game and after the error is printed out, alt-tab to DebugView window and see if there’s something more in there.

 

More information and getting help

You can learn more about how to get started (and how callbacks should be handled) on the Getting Started page of Steamworks.NET documentation. It you ever feel lost, you can use SteamworksDev discussion group. It’s invite-only so you should contact Steam about getting access to this one. It is worth it!

steamworks discussion group

If you ever feel lost, please leave a comment here or reach the Knights using our Facebook page.