Overview
It has been a long time since my last post. Life was not that good here in Hong Kong, but I was still working on my engine in spare time. This post will show some of the stuff I have been working on in the past few months.

Shading
On graphics side, the engine is now switched to use physically based shaders with GGX model for specular and Oren-Nayar model for diffuse shading. The GGX model is implemented according to this paper from Unreal Engine 4[1]. While the Oren-Nayar model is implemented according to this paper from tri-Ace[2].

Meshes shaded with physically based shaders with different roughness

Oren-Nayar model is chosen over Lambert model because it takes roughness into account. During shading, for those mesh with roughness map but without normal map, using Oren-Nayar model will show a bit more details.

A mesh shaded without normal map under indirect lighting:

From left to right: final result, lighting only, normal, roughness, albedo

Also, a radiosity[3] baking tool was written to calculate the indirect diffuse lighting for static mesh by rendering cube-maps at every light map texel position. The cube maps are then projected into Spherical Harmonics(SH) during the radiosity iteration. And the results are stored in SH luma + average chroma along the vertex normal hemi-sphere (but the chroma format doesn't play well with the texture compression, so may need to find another storage representation in the future...).

Direct + Indirect lighting

Direct lighting only

Lighting only

The indirect specular lighting is baked by capturing reflection probe and pre-filtered with GGX model according to the Unreal Engine 4 paper[1]. Currently only the closest reflection probe is used without parallax correction.

Reflection probes placed within the scene

Shading with pre-filtered cube-map

The indirect diffuse lighting for dynamic mesh is baked into the Irradiance Volumes[4], storing in SH coefficients. The SH coefficients are modified according to roughness described in the tri-Ace paper[2] (This is also done for the SH luma store in the light map for static mesh).

Irradiance Volume samples

Dragon shaded with Irradiance Volume

Shadow
The shadow system is updated to use with a mix of baked and real-time shadow. The light map baker described above calculate a shadow term for the main directional light at each light map texel and stored using signed distance field representation[5][6] (Also, an additional binary shadow term is calculated for each Irradiance Volume sample position for dynamic objects).

Real-time shadow only

baked shadow only

Baked + real-time shadow

For the dynamic soft shadow, Exponential Shadow Map(ESM)[7] is used. But the contact shadow looks too soft, so the shadow term calculated by ESM is raised to the power 4 (i.e. s'= s^4, where s is the shadow attenuation result from ESM) to make it darker.

Potential Visibility Set
A potential visibility set (PVS) baker is written to calculate which meshes are visible to the visibility cells for culling the scene during runtime. A brute force approach is used for baking by taking some sample positions on a given mesh (e.g. vertex position + light map texel position) and then rendering the scene to check whether the visibility cells are visible.

Render without PVS culling

Render with PVS culling

Final rendered result

Another view for the same camera render without PVS culling

Another view for the same camera render with PVS culling

Visibility Cells are placed within the scene for possible camera location

Particles
A basic particle system is implemented which can receive static lighting and receive static shadow. The particle are shaded on CPU using the Irradiance Volumes described above and can be calculated either per vertex, per particle or per emitter.

Particles with lighting and shadow enabled

Self-shadow disabled

The particles can also receive self shadow using the Fourier Opacity Mapping[8]. An opacity map is computed on CPU side for the main directional light which assume each particle is of sphere shape. Then a shadow term can be computed for shading.

Cross platform support
The engine runtime supports 3 different platforms: Windows, Mac, iOS(Editors are Windows only). On Windows, the engine mainly runs with D3D11. An extra OpenGL wrapper was written for Windows to ease the porting to iOS and Mac because it is easier to debug OpenGL with tools like Nsight.

The engine runs on Windows, Mac, iOS platforms

To write cross platform shaders, shaders are written in my own shading language which is similar to HLSL. Those shaders are then parsed by a shader parser generated by Flex and Bison[9] to obtain a syntax tree to output the actual HLSL and GLSL source code.

Final words
Hope you enjoy the above screen shots and wish I can find some time to describe them in details in future posts.

There are some other tasks implemented in the past few months,
such as various editors, audio coding as well as asset hot-reload