Hi all!
I'm trying to implement the technique: Light Indexed Deferred Rendering. I modified the original demo:
1) Has removed some UI elements.
2) Has removed View Space light calculation,
3) I fill the Light indices during startup.
4) Optional: I tried to use UBO instead of Texture1D uncomment //#define USE_UBO
My implementation details:
I use the constant buffers instead of Texture1D for storing of the light source information. Instead of OpenGL, I use Direct3D11.
My implementation is divided on following parts:
1) Packing of light indices for each light during startup:
void LightManager::LightManagerImpl::FillLightIndices()
{
int n = static_cast<int>(lights.size());
for (int lightIndex = n - 1; lightIndex >= 0; --lightIndex) {
Vector4D& OutColor = lightIndices.push_back();
// Set the light index color
ubyte convertColor = static_cast<ubyte>(lightIndex + 1);
ubyte redBit = (convertColor & (0x3 << 0)) << 6;
ubyte greenBit = (convertColor & (0x3 << 2)) << 4;
ubyte blueBit = (convertColor & (0x3 << 4)) << 2;
ubyte alphaBit = (convertColor & (0x3 << 6)) << 0;
OutColor = Vector4D(redBit, greenBit, blueBit, alphaBit);
const float divisor = 255.0f;
OutColor /= divisor;
}
}
2) Optional/Test implementation: Update lights positions(animation).
3) Rendering The Light source Geometry into RGBA RenderTarget (Light sources Buffer) using 2 shaders from demo:
Pixel shader:
uniform float4 LightIndex : register(c0);
struct PS {
float4 position : POSITION;
};
float4 psMain(in PS ps) : COLOR {
return LightIndex;
};
Vertex shader:
uniform float4x4 ViewProjMatrix : register(c0);
uniform float4 LightData : register(c4);
struct PS {
float4 position : POSITION;
};
PS vsMain(in float4 position : POSITION) {
PS Out;
Out.position = mul(float4(LightData.xyz + position.xyz * LightData.w, 1.0f), ViewProjMatrix);
return Out;
}
These shaders is compiled in 3DEngine into C++ code.
4) Calculating of the final lighting, using the prepared texture with light indices. The pixel shaders can be found in attached project.
The final shaders:
Pixel:
//
DeclTex2D(tex1, 0); // terrain first texture
DeclTex2D(tex2, 1); // terrain second texture
DeclTex2D(BitPlane, 2); // Light Buffer
struct Light {
float4 posRange; // pos.xyz + w - Radius
float4 colorLightType; // RGB color + light type
};
// The light list
uniform Light lights[NUM_LIGHTS];
struct VS_OUTPUT {
float4 Pos: POSITION;
float2 texCoord: TEXCOORD0;
float3 Normal: TEXCOORD1;
float4 lightProjSpaceLokup : TEXCOORD2;
float3 vVec : TEXCOORD3;
};
// Extract light indices
float4 GetLightIndexImpl(Texture2D BitPlane, SamplerState sBitPlane, float4 projectSpace) {
projectSpace.xy /= projectSpace.w;
projectSpace.y = 1.0f - projectSpace.y;
float4 packedLight = tex2D(BitPlane, projectSpace.xy);
float4 unpackConst = float4(4.0, 16.0, 64.0, 256.0) / 256.0;
float4 floorValues = ceil(packedLight * 254.5);
float4 lightIndex;
for(int i = 0; i < 4; i++) {
packedLight = floorValues * 0.25;
floorValues = floor(packedLight);
float4 fracParts = packedLight - floorValues;
lightIndex[i] = dot(fracParts, unpackConst);
}
return lightIndex;
}
#define GetLightIndex(tex, pos) GetLightIndexImpl(tex, s##tex, pos)
// calculate final lighting
float4 CalculateLighting(float4 color, float3 vVec, float3 Normal, float4 lightIndex)
{
float3 ambient_color = float3(0.2f, 0.2f, 0.2f);
float3 lighting = float3(0.0f, 0.0f, 0.0f);
for (int i = 0; i < 4; ++i) {
float lIndex = 255.0f * lightIndex[i];
// read the light source data from constant buffer
Light light = lights[int(lIndex)];
// Get the vector from the light center to the surface
float3 lightVec = light.posRange.xyz - vVec;
// original from demo doesn't work correctly
#if 0
// Scale based on the light radius
float3 lVec = lightVec / light.posRange.a;
float atten = 1.0f - saturate(dot(lVec, lVec));
#else
float d = length(lightVec) / light.posRange.a;
const float3 ConstantAtten = float3(0.4f, 0.01f, 0.01f);
float atten = 1.0f / (ConstantAtten.x + ConstantAtten.y * d + ConstantAtten.z * d * d);
#endif
lightVec = normalize(lightVec);
float3 H = normalize(lightVec + vVec);
float diffuse = saturate(dot(lightVec, Normal));
float specular = pow(saturate(dot(lightVec, H)), 16.0);
lighting += atten * (diffuse * light.colorLightType.xyz * color.xyz + color.xyz * ambient_color + light.colorLightType.xyz * specular);
}
return float4(lighting.xyz, color.a);
}
float4 psMain(in VS_OUTPUT In) : COLOR
{
float4 Color1 = tex2D(tex1, In.texCoord);
float4 Color2 = tex2D(tex2, In.texCoord);
float4 Color = Color1 * Color2;
float3 Normal = normalize(In.Normal);
// get light indices from Light Buffer
float4 lightIndex = GetLightIndex(BitPlane, In.lightProjSpaceLokup);
// calculate lightung
float4 Albedo = CalculateLighting(Color, In.vVec, Normal, lightIndex);
Color.xyz += Albedo.xyz;
return Color;
}
Vertex Shaders:
//
uniform float4x4 ViewProjMatrix : register(c0);
struct VS_OUTPUT {
float4 Pos: POSITION;
float2 texCoord: TEXCOORD0;
float3 Normal: TEXCOORD1;
float4 lightProjSpaceLokup : TEXCOORD2;
float3 vVec : TEXCOORD3;
};
float4 CalcLightProjSpaceLookup(float4 projectSpace)
{
projectSpace.xy = (projectSpace.xy + float2(projectSpace.w, projectSpace.w)) * 0.5;
return projectSpace;
}
VS_OUTPUT VSmain(float4 Pos: POSITION, float3 Normal: NORMAL, float2 texCoord: TEXCOORD0)
{
VS_OUTPUT Out;
Out.Pos = mul(float4(Pos.xyz, 1.0f), ViewProjMatrix);
Out.texCoord = texCoord;
Out.lightProjSpaceLokup = CalcLightProjSpaceLookup(Out.Pos);
Out.vVec = Pos.xyz;
Out.Normal = Normal;
return Out;
}
The result:
We can show the Light sources Buffer - texture with light indices:(console command: enableshowlightbuffer 1)
If we try to show the light geometry we will see the following result(console enabledrawlights 1)
And my the demo of Light indexed deferred rendering:
https://www.dropbox.com/s/5t9f5vpg83sspfs/3DMove_multilighting_gd.net.7z?dl=0
1) Try to run demo, moving on terrain using W,A,S,D.
2) Try to show light geometry(console command enabledrawlights 1), light buffer(console command: enableshowlightbuffer 1)
What do i do wrong ? how to fix the calculation of lighting ?