So I added directional and ambient lights to the deferred renderer:
The G-Buffer is pretty simple. Color (RGBA8), positions (RGBA16F), and normals (RGBA16F):
(I didn’t convert the normals properly, since I just used apitrace to dump the framebuffer contents).
Of course, it could be optimized. I’m going to pack specular into the alpha channel of the color buffer. I could make normals RG16F and get Z from X/Y (1 – x * x + y * y) since they’re normalized (of course). And I could make positions RG16F as well and get the Z from the depth buffer and camera matrix.
But I think that’s a bit premature. :)
The lights are composited in a separate buffer:
…and then blended with the colors from the GBuffer to generate the final lit image.
In the DeferredRendererPass, I collect light nodes and push them into their own list:
function DeferredRendererPass:walk(node, delta)
if node:isCompatibleType(LightSceneNode) then
table.insert(self.lights, PendingNode(node, delta))
else
— …
end
— …
end
Then when I draw lights, after building the GBuffer, I switch based on the derived LightSceneNodeType:
for i = 1, #self.lights do
local node = self.lights[i].node
if node:isCompatibleType(DirectionalLightSceneNode) then
self:drawDirectionalLight(node, delta)
elseif node:isCompatibleType(AmbientLightSceneNode) then
self:drawAmbientLight(node, delta)
end
end
And finally, I handle specific logic in the drawDirectionalLight/drawAmbientLight/etc methods.
Again, this could be optimized. I could push all lights of specific types into their own queues and render all directional lights in one go, then all ambient lights, then all point lights, etc.
But it’s too early to optimize.