I'm trying to implement tile based light culling in opengl using a compute shader. I am using this as a guide. The lighting calculations work fine but the point light culling doesn't seem to work any where near correctly. I can't tell if my planes are calculated wrong or its my culling loop. To test what tile has a light affecting it I draw it in gray, as i move the camera away the gray tiles fade to the right no matter where the light is placed.
Compute shader:
#version 430
#define MAX_POINT_LIGHTS 1024
#define MAX_LIGHTS_PER_TILE 40
#define WORK_GROUP_SIZE 16
struct Attenuation
{
float constant;
float linear;
float exponent;
};
struct Light
{
vec3 color;
float intensity;
};
struct DirectionalLight
{
Light light;
vec3 direction;
};
struct PointLight
{
Light light;
Attenuation atten;
vec3 position;
float radius;
};
vec4 calcLight(Light light, vec3 direction, vec3 normal, vec3 fragPos, vec3 specular, vec3 camPos)
{
float diffuseFactor = dot(normal, -direction);
vec4 diffuseColor = vec4(0.0, 0.0, 0.0, 0.0);
vec4 specularColor = vec4(0.0, 0.0, 0.0, 0.0);
if (diffuseFactor > 0)
{
// might need to be 0.0----------| |
diffuseColor = vec4(light.color, 1.0) * light.intensity * diffuseFactor;
vec3 directionToEye = normalize(camPos - fragPos);
vec3 halfDirection = normalize(directionToEye - direction);
float specularFactor = dot(halfDirection, normal);
// maybe not 32?
specularFactor = pow(specularFactor, 32);
if (specularFactor > 0)
{
// maybe 0.0 again
specularColor = vec4(light.color, 1.0) * vec4(specular, 1.0) * specularFactor;
}
}
return diffuseColor + specularColor;
}
vec4 calcDirectionalLight(DirectionalLight dirLight, vec3 pos, vec3 normal, vec3 specular, vec3 camPos)
{
return calcLight(dirLight.light, dirLight.direction, normal, pos, specular, camPos);
}
vec4 calcPointLight(PointLight pointLight, vec3 pos, vec3 normal, vec3 specular, vec3 camPos)
{
vec3 lightDirection = pos - pointLight.position;
float distanceToPoint = length(lightDirection);
lightDirection = normalize(lightDirection);
vec4 color = calcLight(pointLight.light, lightDirection, normal, pos, specular, camPos);
float atten = pointLight.atten.constant + pointLight.atten.linear * distanceToPoint + pointLight.atten.exponent * distanceToPoint * distanceToPoint + 0.0001;
return color / atten;
}
layout (binding = 1, rgba32f) uniform image2D writeonly finalImage;
layout (binding = 2, rgba32f) uniform readonly image2D geometryPosition;
layout (binding = 3, rgba32f) uniform readonly image2D geometryDiffuse;
layout (binding = 4, rgba32f) uniform readonly image2D geometryNormal;
uniform int numActiveLights;
uniform PointLight pointLights[MAX_POINT_LIGHTS];
uniform Light ambientLight;
uniform DirectionalLight directionalLight;
uniform vec3 cameraPosition;
uniform vec2 resolution;
uniform mat4 projection;
uniform mat4 view;
shared uint minDepth;
shared uint maxDepth;
shared uint pointLightCount;
shared uint pointLightIndex[MAX_POINT_LIGHTS];
layout (local_size_x = WORK_GROUP_SIZE, local_size_y = WORK_GROUP_SIZE) in;
void main()
{
minDepth = uint(0xFFFFFFFF);
maxDepth = 0;
pointLightCount = 0;
barrier();
ivec2 pixel = ivec2(gl_GlobalInvocationID.xy);
vec3 position = imageLoad(geometryPosition, pixel).xyz;
float d = position.z;
uint depth = uint(d * uint(0xFFFFFFFF));
atomicMin(minDepth, depth);
atomicMax(maxDepth, depth);
barrier();
float minDepthZ = float(minDepth / float(0xFFFFFFFF));
float maxDepthZ = float(maxDepth / float(0xFFFFFFFF));
vec2 tileScale = vec2(resolution * (1.0 / float(2 * WORK_GROUP_SIZE)));
vec2 tileBias = tileScale = vec2(gl_WorkGroupID.xy);
vec4 col1 = vec4(-projection[0][0] * tileScale.x, projection[0][1], tileBias.x, projection[0][3]);
vec4 col2 = vec4(projection[1][0], -projection[1][1] * tileScale.y, tileBias.y, projection[1][3]);
vec4 col4 = vec4(projection[3][0], projection[3][1], -1.0, projection[3][3]);
//vec4 col1 = vec4(-projection[0][0] * tileScale.x, 0.0f, tileBias.x, 0.0f);
//vec4 col2 = vec4(0.0f, -projection[1][1] * tileScale.y, tileBias.y, 0.0f);
//vec4 col4 = vec4(0.0, 0.0, 1.0, 0.0);
vec4 frustumPlanes[6];
frustumPlanes[0] = col4 + col1;
frustumPlanes[1] = col4 - col1;
frustumPlanes[2] = col4 - col2;
frustumPlanes[3] = col4 + col2;
frustumPlanes[4] = vec4(0.0, 0.0, -1.0, -minDepthZ);
frustumPlanes[5] = vec4(0.0, 0.0, -1.0, maxDepth);
for (int i = 0; i < 4; i++)
{
frustumPlanes[i] *= 1.0 / length(frustumPlanes[i].xyz);
}
uint threadCount = WORK_GROUP_SIZE * WORK_GROUP_SIZE;
uint passCount = (numActiveLights + threadCount - 1) / threadCount;
for (int passIt = 0; passIt < passCount; ++passIt)
{
uint lightIndex = passIt * threadCount + gl_LocalInvocationIndex;
lightIndex = min(lightIndex, numActiveLights);
PointLight p = pointLights[lightIndex];
vec4 pos = view * vec4(p.position, 1.0);
float rad = p.radius / pos.w;
if (pointLightCount < MAX_LIGHTS_PER_TILE)
{
bool inFrustum = true;
for (int i = 3; i >= 0 && inFrustum; i--)
{
float dist = dot(frustumPlanes[i], pos);
inFrustum = (-rad <= dist);
}
if (inFrustum)
{
uint id = atomicAdd(pointLightCount, 1);
pointLightIndex[id] = lightIndex;
}
}
}
barrier();
vec3 normal = imageLoad(geometryNormal, pixel).xyz;
vec4 diffuse = imageLoad(geometryDiffuse, pixel);
vec3 specular = vec3(0.1, 0.1, 0.1);
vec4 color = diffuse;
color *= vec4((ambientLight.color * ambientLight.intensity), 1.0);
color += calcDirectionalLight(directionalLight, position, normal, specular, cameraPosition);
for (int i = 0; i < pointLightCount; i++)
{
//color += calcPointLight(pointLights[pointLightIndex[i]], position, normal, specular, cameraPosition);
color = vec4(0.5);
}
barrier();
imageStore(finalImage, pixel, color);
}