This week’s work can be summarised into the following categories; guard character, animation, pre-production, and the water shader. I have already covered the guard character & pre-production in previous mini-blog posts as to not clutter this blog with too large posts.
I started animating the guard character’s locomotion moveset to get it in and tested in-engine as quick as possible. These animations will be developed & tweaked over the course of the project as other tasks with higher priority are completed.
The rig and skinning also went through some tweaks as I started animating – which is expected to happen and luckily biped provides enough control for this to have not affected anything negatively.
As I decided to take inspiration from the second level in Dishonored 2 which has you arrive by boat at a port I needed to develop a water shader to reflect the ocean as that would play a large part in immersing the player in the world.
I researched into how to generate the noise function required to get that effect as utilising sin gives uniform peaks and valleys which is undesirable for what I needed as the water seems to sharpen as it reaches its peak and soften in the valleys. In my research I found out about a gerstner wave function from here – https://80.lv/articles/tutorial-ocean-shader-with-gerstner-waves/, where I utilised the shader function shown there and integrated it into my own setup with 4 layers of gerstner noise to displace the tessellated plane.
I also used the standard techniques of combining two normal maps being panned at different speeds and tiled at different rates to create noise on the surface. I did however opt to utilise just the standard opaque PBR shader for this as I don’t intend for the player to go underwater, and the consistency of the water is too dense to show anything underneath – though if the need arises I could still integrate translucency support.
Also a big part of keeping this shader physically accurate was reconstructing the normals per fragment (pixel) which I accomplished using GPU derivatives to read neighbouring pixels due to the way the array processor of the GPU processes blocks of 4 pixels (p.xy, p.x+1, p.y+1, p.xy+1) – so I used this to force the shader to buffer the result of the gerstner wave displacement, then I retrieved the cross product of the delta of the neighbouring heights, and then transformed the resulting vector back into tangent space combine the panning normal maps. This was all necessary as displacing a mesh does not recalculate the normal as that is pre-baked into the vertex stream (in the mesh), so to properly calculate light (especially with the amplitude of the waves being quite high) I needed to do this.
And as usual I exposed as many pertinent parameters as I could to best control the look-dev via material instancing.
This is the shader code so far – though as I bring in more environmental assets into the game it will change to meet the needs.
I used mesh gradient fields to find the pseudo intersection of meshes with the water to generate foam around them – though I intend to further push this, where I could also use this to generate a dynamic flow map to push the texture and even noise around objects.
If the need arises I also intend to look at rendering to a depth texture for a specific section of the level to apply dynamic buoyancy to objects in the water such as boats etc using vertex displacement.