Image Based Lighting is used to implement ambient lighting for dynamic objects coming from static objects in a game level. In most cases, it is used for specular lighting or reflections. In this process lighting information at the potential point of interests is stored in a special type of cube maps called Light probes. These Light probes are created using environment maps captured at same locations, which are then blurred in some special way depending on the BRDF  that will consume them at runtime. Each mipmap of a light probe contains a version of environment map blurred by a different amount depending roughness represented by that level. For example, if we are using 10 mip map levels to represent roughness from 0 to 1, then mip-0 will be blurred by a value representing roughness 0, mip-1 will represent 0.1 and so on. The last mip level mip-10 will be blurred by a value represent the roughness of 1.0. This process is also called Cubemap Convolution . All of this is done as a pre-process and resulting light probes are fetched at runtime to enable reflections or Image Based Lighting.
From past few weeks, I have been working on Image Based Lighting. Finally, I have finished writing features to generate dynamic cube maps and generate pre-convoluted probes for both diffuse (lambert) and GGX based specular.
Still there’s a lot of work to do. Here’s some rough list of features(or problems) I will be working on next:
- Support for many IBL probes – I am thinking maybe 32
- Choosing IBL probe (probes) to be used for the current pixel being shaded – I am thinking to maybe implement something similar to point light selection using tiled rendering or something. I can just replace point light calculation with cube map sampling for IBL. This way I can store all probes in a single array can bind all IBL probes to the single slot. I think this would be faster than checking the closest IBL probe for every object and passing it while shading. (Same benefits as deferred rendering over forward)
- Parallax correction for local probes
- Realtime IBL probes? – I don’t know if I would need this or SSR would be good enough.
- Fix seams for lower MIP levels. I am not sure whether I should implement parabolic maps or code some other techniques like wrap, or something.
- Improve Pre-convolution performance by further randomizing the samples and use less number of samples.
I have been experimenting with tone mapping from a long time. I tried many different ways of implementing tonemapping & bloom and now I have finalized this feature in the engine.
Tone mapping is a technique used in image processing and computer graphics to map one set of colors to another to approximate the appearance of high dynamic range images in a medium that has a more limited dynamic range.
I tried few different Global tone mapping operators with various ways to calculate scene luminance.I tried 3 major approaches (with a lot of variations in between) –
In the first approach, I calculated scene luminance by averaging log luminance values of HDR render target of the scene. I did this by rendering a screen space quad calculating log luminance values for the scene and then I used GenerateMips to average them automatically for me. I tried some different tone mapping operators including Reinhard, filmic curve and modified filmic curve (or uncharted2’s filmic curve). At the end modified filmic curve with the ability to modify the curve as need turned out to be the best approach for tone mapping operator.Problem with this method is any few extreme dark/bright spots in the scene can make the whole scene look brighter or darker beyond desired ranges.
I have uploaded a video demo for Point Light Dynamic shadows with sponza scene containing 32 point lights with dynamic shadows.
Some performance results at resolution 1080p (as rendered in the above screenshot) –
- Depth Pass – 0.410 ms
- ShadowMap Update – 2.1 ms
- Lighting Pass (With 32 dynamic shadows) – 5.5 ms
- Lighting Pass (With 32 static shadows) – 4.9 ms
Cascaded Shadow Maps is so far the best solution to tackle Perspective and Projective aliasing in shadows. The basic idea is to use multiple shadow maps (mostly 3 or 4) to cover different areas of view camera frustum. The root cause of these shadow artifacts is lack of one to one mapping between shadow map texels and pixels on the screen (in view space). The Cascaded Shadow mapping provides higher resolution shadow maps to objects closer to the eye solving the root cause of artifacts.
(Screenshot – JustAnotherGameEngine)
Shadows are one of the key features for creating realistic and believable virtual scenes. Whether hard or soft or physically correct, shadows are very crucial in providing important visual cues. Shadows can be an important aspect of gameplay also. For example, it plays an important role in CounterStrike by allowing us to see enemies around corners, coming through doors, etc.
Real-Time Shadows is an active area of research. There’s still no single fool-proof technique of rendering shadows that can handle all the cases. Well, there exists a way to render high quality physically correct shadows through ray-tracing but that’s not fast enough to render in real-time on current hardware. So a better way to say this is that there’s no real-time hack of rendering high quality, flicker/ jaggies free, physically correct, etc shadows yet.
From past 7-8 weeks I have been mainly working on shadows along with some engine code refactoring, adding support for some new things to support shadows like Texture arrays, Geometry shaders, etc
Read the rest of this entry
Implemented support for Transparent Materials using Forward+ path. Even though it’s working I am not sure to go through a whole separate Forward+ rendering pass for transparent materials is the right way to go. I am thinking to test some other algorithms like Inferred Rendering, OIT or stochastic transparency, etc.
But before that I will be testing performance of proper Deferred Rendering with fat GBuffers vs Forward+. Right know I am using 3 32 bit GBuffers for Albedo, Normal and Specular (and yes 32bit Depth Buffer). In my current implementation I am getting 650+ fps in Deferred Rendering path and 350+ fps in Forward+ with 1024 dynamic point lights in sponza scene.
Here’s the sponza model rendered with 1024 dynamic point lights –
I am getting 650+ (650-900) fps in this scene depending upon how many lights are in the view and how many lights end up per tile. This is with Depth Prepass, which gave a boost of approx 150 fps in this scene.
Note – These results are without any optimizations on application side. – no frustum Culling, no sorting,data of all the lights is being updated perframe, etc.
Some more screenshots –