Step 1: Create thin gbuffer: by "thin" I suppose you mean either less things in the gbuffer, a lower DXGI format, or both? The current Gbuffer consists of Position, Normal, Diffuse Light, Specular Light, and Material type.
It's not "Diffuse light" and "Specular light", but material properties of a pixel.
On this stage you are writing to GBuffer properties for each pixel.
These MATERIAL properties will be used with Light-related data on step 2.
Step 2: Create intermediate lighting buffer, so include all lights there, and then have it read the necessary info from the thin Gbuffer?
I've done it via packed structured buffer. Each type of light (direct, ambient, point, and spot) will be written to this kind of structure:
struct LightData
{
uint type; //All lights
float3 color; //All lights
float3 dirToLightVS; //Directional, Spot
float innerAngleCos; //Spot
float3 posVS; //Point, Spot
float outerAngleCos; //Spot
}; //48 bytes
StructuredBuffer<LightData> Lights;
The buffer will contain n elements (== light count + 1-last stub "LIGHT_NO" to break a loop).
The shader unpacks the data, calculate amount of light from each light source, and sum it with different light:
for (each pixel/sample)
{
for (uint i = 0; i < 102400; ++i) // i - index of Light in LightBuffer
{
if (lights[i].type == LIGHT_NO)
break; //No more lights
DiffSpec curr = (DiffSpec)0;
switch (lights[i].type)
{
//Sorted by occurrence
case LIGHT_POINT:
curr = CalcPoint(Lights[i], ...);
break;
case LIGHT_SPOT:
curr = CalcSpot(Lights[i], ...);
break;
case LIGHT_DIRECTIONAL:
curr = CalcDirectional(Lights[i], ...);
break;
case LIGHT_AMBIENT:
curr = CalcAmbient(Lights[i], ...);
break;
case LIGHT_SHADOW_PRIM:
curr = CalcDirectional(Lights[i], ...);
break;
}
totalLight += curr;
}