Thursday, October 31, 2013

Tech Feature: Pre-pass lighting

Progress on the new engine, HPL3, is coming along nicely and not too long ago I changed the core rendering system into something named Pre-pass lighting. This switch has been produced for a number of reasons, but just before I got into that and what pre-pass lighting specifically is, I need to have to clarify how we did it back in the "old days".

Forward Rendering
The engine powering Penumbra (HPL1) makes use of something referred to as forward rendering. This variety of rendering functions by rendering the entire scene on an object basis. So when rendering a chair, wall, or any geometry in the globe, this was accomplished by drawing it 1 time for every light that touches it. So an object that is lit by three lights has to be drawn 3 instances, and so on. This strategy can be fairly limiting when setting up scenes as you require to be very careful when adding lights. It might not truly be clear precisely how a lot influence on performance a single light will have and levels normally require very some tweaking to get appropriate. The complexity of a scene can be expressed as:

Draw calls = Objects * Lights

This means that the number of draw calls can simply get extremely huge and only adding a single light, even if it has tiny impact on the scene visually, can have very adverse effects on efficiency.

Deferred Shading
When starting work on HPL2 (which was utilized for Amnesia) I wanted to get away from this annoying light limitations. Since HPL1 had been produced a new method called "Deferred shading" had emerged and when function on HPl2 was started, the average Computer program was up for the the job.

What makes deferred shading special is that it separates rendering objects and rendering the lighting. This operates by initial rendering to a unique G-buffer that contains details such as normals, depth and color of all on screen objects. The final output appears like this:

From left to appropriate: Color, normals and depth. Note that these texture have four channels each and not visible are also saved specular intensity and energy. These three texture then represent the properties of all visible data. It is then employed by the lights to render the final image. This tends to make the complexity of the rendering:

Draw calls = Objects + Lights

This is a lot nicer and as lights and objects are separated, it is a lot simpler to add lights to a scene with no worrying about overall performance hits. It is also considerably simpler to intuitively understand how performance will be affected. By utilizing this technique we exactly where able to use a lot far more light sources in Amnesia and contemplating all of the dynamic lights necessary for the mechanics, the game would have been a lot harder to make employing forward rendering.

Deferred rendering is not without issues although. Initial of all, rendering the G-buffer means rendering to three textures at 1 time which is very overall performance heavy, meaning a scene with handful of lights runs more rapidly on a forward renderer. Secondly, there is no help for fullscreen anti-aliasing either, and one has to do some hackish tricks to eliminate jagged edges (the "edge smooth" function in Amnesia). Lastly, there is a lot much less material variety achievable as every home needed to generate the final image wants to be in the G-buffer. Because we could mange without fancy skin shaders in Amnesia, it was turned out to not be also considerably of a issue though.

Scenes like the test of Agrippa above would not be attainable in our old Renderer. In this test shot around 30 lights support light Agrippa in a good style, and because the geometry and lighting is decoupled it is achievable to run this with a high framerate.

Pre-pass lighting
I heard about this strategy (first saw it here) for the duration of the development of Amnesia and was a bit interested in trying it out. I was interested in the tech back then because it created light rendering go more rapidly, something that had proved a bit of a bottle neck in Amnesia. Nevertheless, I did not have time back then and decided against it.

As I started to update the engine to HPL3 I once again looked at this technologies. This time a lot more had been written on the subject and it had really been tested. For instance a equivalent algorithm was used in Insomniac's Reistance 2 and Crytech goes more than it in a paper about CryEngine two. This also meant that the approach was practical, and was nicely worth attempting (I normally attempt and use tech I have been in a position to attempt in other games, as tech dead-ends can prove fairly costly).

Pre-pass lighting (or deferred lighting as it is called sometimes) is very comparable to deferred shading and I could use significantly of the code from HPl2 when implementing it. Only a few modifications in supplies and light rendering was genuinely necessary. The rendering functions by also initial rendering to a G-buffer, but one particular only containing normal, depth and specular power. Following that lights are rendered, but they render only part of the light equation essentially colour and specular intensity. Then in a final pass all objects are rendered once more and the light information from the preceding pass is utilized to render the final image. The sequence is like this:

Render Normals+Depth -> Render Lights -> Render final image

The 1st good point is that this strategy is able to render lights quicker, considering that each lights has to do much less equations and access less textures. The algorithm also contains an extra step at the finish, but this does not matter that much, as the added the final render requires is regained by the 1 significantly less buffer required to be rendered to in the initial g-buffer pass (only two textures necessary alternatively of the three deferred shading makes use of).

This speed up was not the main purpose why I utilised it even though. Because every single object rendered once more during the final pass, it is feasible to have a considerably bigger range of material types. Alternatively of getting confined to making use of what can be fitted into a g-buffer, a material can do distinct calculations the final image pass. This allows for specialized skin shaders and other tricks. For instance, it is now attainable to have a lot more functions packed into the decal supplies:

Above is a decal with both colour, normalmap and height map, something not achievable in the earlier engine. (Note that colour and standard have separate alpha and that the height map make the tiles appear carved out of the ground).

Finish notes
Now I have provided a tiny rundown of how the new renderer performs and how it differs from the old one particular. I have skipped a lot of the particulars and a lot more technical stuff, to make the post a bit shorter. So if you have any queries, comment and I may well have some type of answer!

Also, sorry for the lack of new and thrilling photos in this post. Subsequent tech function need to be a lot more fun on that element, as I am now moving on to Terrain...

I eventually did some tests on the algorithm and compared it to the old renderer. Final results are:

No comments:

Post a Comment