**Finding the right rendering solution for our games**

made in GameMaker:Studio

made in GameMaker:Studio

#1 - Light Pre-Pass aka Deferred Lighting

28th of January 2015

**Spoiler:**

#2 - Shadows, SSAO, Translucency

14th of February 2015

Watsup guyz,

after some longer delay, I am back with another part of our tech blog. The reason why I am writing this after such a long time is that I have spent some time by researching and reading a lot of workpapers on topic of realistic real-time rendering, global illumination and stuff like that. From the beginning I wanted to go for a physically based rendering, which definitely is possible (we have already experimented with that, but that was just a Cook-Torrance BRDF), but it takes tons of work for ten people and a really good insight in real world physics. And I am just one person with a lot of other work to do. So I decided to go with the old "good" Phong and Lambertian and make our engine graphically appealing, not necessarily realistic. But still PBR might be our next step sometime in the future.

Ok, now let's finally have a look at what have we done so far...

**Deferred Directional Light and Real-Time Shadows**

This is something that we have actually finished before in forward rendering, so it was only matter of implementing it into our deferred lighting engine, which I guess is something that would interest you the most.

So the approach is similar to deferred point lights, but instead of applying lighting to a 3D model, we are using a separate shader on a full-screen quad drawed in a orthographical projection and we pass all needed matrices as a uniform. For the light itself you just need normals from the g-buffer and transform the light vector into the view space (or world, depends on your normals) by multiplying it with view (world) matrix.

The shadows might be a little bit trickier. I can't tell if the way we are doing it is the right way, so if you know a better one, you can share with us So what we need for them is a depth from our g-buffer, depth from the shadow map projection, and an inverse world-view matrix and a projection matrix used for shadow mapping. First we reconstruct view-space position and then multiply it with the inverse world-view matrix to get the world-space position.

Code: Select all

`float depth = decode_depth(tex2D(uTexDepth, IN.TexCoord));`

float3 viewPos = float3(uTanAspect * (IN.TexCoord * 2.0 - 1.0) * depth, depth) * uClipFar;

float4 worldPos = mul(uMatInverse, float4(viewPos, 1.0));

When we have the world position, when can just multiply it with our shadow map projection matrix (orthographic projection fits shadows from directional light the best).

Code: Select all

`float3 OrthoPos = mul(uMatOrtho, worldPos).xyz;`

Then we use the result to compare it with the depth from our shadow map. If the depth is higher, then the point is in shadow. But if we use only one sample for comparing, the shadows will have hard edge and that is not really appealing. Unfortunately GM:S does not support hardware PCF (percentage close filtering), which automatically takes 4 samples aroud texel and compares them to get the final result (=> shadows with softer edge), so we had to solve it another way. We decided to go for Stratified Poisson Sampling. For poisson sampling we need a poisson disk, set of points covering whole area of radius = 1 around our current pixel. Example of poisson disk:

Code: Select all

`float2 poissonDisk[8];`

poissonDisk[0] = float2(-0.6622922, -0.5487083);

poissonDisk[1] = float2(-0.8485804, 0.3489894);

poissonDisk[2] = float2(-0.2681293, -0.1692538);

poissonDisk[3] = float2(0.02454568, -0.9016637);

poissonDisk[4] = float2(0.2056526, 0.2451244);

poissonDisk[5] = float2(-0.220465, 0.7909056);

poissonDisk[6] = float2(0.5490856, -0.3020224);

poissonDisk[7] = float2(0.724205, 0.405656);

And then for every point we take multiple samples based on the poisson disk. The "stratified" part we get by using poissonDisk[] of (pseudo-)random index for every sample. This basically means that the result will have a noisy edge, as you can see below.

The result:

**Screen Space Ambient Occlusion (SSAO)**

SSAO is something that we already had in the previous tech blog (you can see it if you download the exe), but we didn't really like it's look, so we decided to make a little upgrade. (There is a lot of documents on topic of SSAO all around the internet, so I am not going to write here exactly how it works...)

At first we used the original CryTek's approach, where you only use depth buffer to accumulate the occlusion. But that leads to a lot of self-occlusion, because half of the samples end up being in the geometry, so the samples are basically wasted.

An improvement to CryTek's original method is orienting the sample vectors around normal of the surface => Normal Oriented Hemisphere. There are multiple ways how to get such hemisphere and we got to like Blizzard's approach (see slide 16) which they used in StarCraft II. That means that we flip any sample vector facing opposite direction than the surface normal, so it ends up being above the surface.

This is the resut. It is still not perfect, but it looks much better then the old SSAO:

**Real-Time Translucency Approximation**

Translucency and subsurface scattering is one of the topics that I have been researching about for the last week. It is something that just adds up on graphical quality and it gives better feel of realism, so I just wanted to have it in our engine. But it also is quite heavy to compute in real-time. Techniques like ray-tracing are not really acceptable for us and we don't think that we even need such approach for our simple purposes. We just wanted something that is not computationally demanding, but it would have the look. In our fortune, DICE (Battlefield series) has already thought of such approach and it is really well described in their presentation.

There is a huge collection of screenshots from our implementation:

(Sorry I just loved the effect )

If you want to see the effect in the action, you can download the exe from the link below

**But it is not optimized yet, so you can expect low fps/fps drops!**

**Download EXE**(#n represents the number from which tech blog the exe is)

**#1:**https://www.dropbox.com/s/qw4slav3qrq1209/DeferredLighting-Default-1.0.0.0.zip?dl=0

**#2:**https://www.dropbox.com/s/eu1n05fal7j29os/DeferredLighting-Default-1.0.0.1.zip?dl=0 (not optimized!)

**Links**

Used models and textures: http://www.turbosquid.com/3d-models/free-weathered-ruins-bus-truck-3d-model/668298

Phong reflection model: http://en.wikipedia.org/wiki/Phong_reflection_model

Blinn-Phong shading model: http://en.wikipedia.org/wiki/Blinn%E2%80%93Phong_shading_model

Encoding floats to RGBA: http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/

Normal mapping without precomputed tangents: http://www.thetenthplanet.de/archives/1180

Compact normal storage for small G-buffers: http://aras-p.info/texts/CompactNormalStorage.html

Light Pre-Pass: http://diaryofagraphicsprogrammer.blogspot.cz/2008/03/light-pre-pass-renderer.html

Shadow filtering: http://codeflow.org/entries/2013/feb/15/soft-shadow-mapping/

Texture lookup in cube map: http://www.gamedev.net/topic/443878-texture-lookup-in-cube-map/

CryTeks presentation: http://www.crytek.com/download/A_bit_more_deferred_-_CryEngine3.ppt

DICE Translucency Approximation: http://www.slideshare.net/colinbb/colin-barrebrisebois-gdc-2011-approximating-translucency-for-a-fast-cheap-and-convincing-subsurfacescattering-look-7170855

Cook-Torrance BRDF: http://inst.cs.berkeley.edu/~cs294-13/fa09/lectures/cookpaper.pdf

Poisson Disk Generator: http://www.coderhaus.com/?p=11

SSAO Tutorial by John Chapman: http://john-chapman-graphics.blogspot.cz/2013/01/ssao-tutorial.html