So shadows…
What?! Shadows again? Yep, I’m sure you’re getting tired of hearing about it, I’m getting tired of thinking about them… But here is what I’ve done in the last few weeks.
- Decoupled the filter radius from the SpotLight penumbra [1]
- Optimised shadow sampling by adding early out [2]
- Increased sample count for shadow edges from 16 to 32
- Implemented "Randomly Rotated" poisson disk sampling to address some banding/muddy shadows [3]
- Fixed pervasive SSAO issue where samples were flipping at face boundaries [4]
- Added a simple AmbientLight that supports just colour and intensity
- Refactored Light class to support the AmbientLight
- Added a ShadowCasterLight extension that both SpotLight and DirectionalLight extend
- Added a Shader DAG node to the SceneGraph
- Added the Picker pass for rendering selectable objects for user interaction
- Added Outline renderer that renders outlines around selected objects
- Added SelectionManager to manage selection state
Shader Time
After putting shadows to bed (for now…) I turned my attention to shaders.
Up until now I only had the humble Lambert shader (excluding utility shaders like SSAO, geometry pass etc.). Lambert is a good place to start because it’s simple to implement and understand. It is a lighting model that describes a completely matte surface where light is reflected equally in all directions.
But what about shiny things?
I’ve added a Blinn shader for that. Blinn adds a specular component, light that is reflected in one direction and creates the bright highlight on shiny objects.
While working on the Blinn shader, I realised I had hard-coded some “ambient” light into the Lambert shader to lighten things up a bit, and had forgotten all about it.
Ambient light is light with no definite source, light that’s just bouncing around the scene.
Adding ambient light to a shader is cheap, though not exactly accurate. But without it (in the absence of ray/path-tracing), shaders tend to drop into complete darkness and look very computer-graphicy. Of course, you shouldn’t hard-code values into your shader, I had done it temporarily and just forgot I did it.
As is with everything else so far, adding the Blinn shader though itself simple, came with a barrage of other fixes/additions/refactoring.
Object Picking
Now that I have multiple property panels in the GUI to tweak when testing shader/light interactions, it quickly became necessary to be able to select objects in the scene and only show properties relevant to that object.
I read about object picking a while ago in Shi Yan’s WebGPU Unleashed series (https://shi-yan.github.io/webgpuunleashed/Control/object_picking.html), here Shi Yan describes two methods:
- Raycasting: Shoot a ray from the camera through where the user clicked, iterate over the selectable objects, and check if the ray intersects their bounding box. The closest hit (if any) is your selected object.
- Colour ID: Encode a unique RGBA colour for each selectable object and render the scene to a texture. Then sample the pixel where the user clicked and decode it — that’s your selected object.
There are pros and cons to both approaches. Raycasting typically uses bounding boxes for speed but they aren’t very precise and often overlap, making picking accuracy a bit iffy. A Colour ID pass, however, has pixel-perfect accuracy, but does have a one-frame latency and requires additional bind groups and render passes.
I ended up needing both, I use the raycasting approach for selecting locator shapes (largely because they aren’t part of the main render pass, but also because they’re not solid shapes) and Colour ID for mesh objects. Both of these systems run through a single SelectionManager that handles tracking selections and updating the GUI as necessary.
AT-ST Returns
Here there the AT-ST model is back (still no textures, I never got that far with it), but it has my new Blinn shader applied. The ground plane has the original Lambert.
Also just a reminder that you can enable ‘Locators’ in the ‘Render Options’ if you want to play with the light settings.
Next up is PBR (Physically Based Rendering) shaders!