#version 450 const int light_count = 4098; struct PointLight { vec3 color; float luminance; vec3 position; }; layout(location = 0) in vec3 in_color; layout(location = 1) in vec2 in_uv; layout(location = 2) in vec3 in_normal; layout(location = 3) in vec3 in_position; layout(location = 4) in vec3 in_tangent; layout(location = 5) in vec3 in_bitangent; layout(early_fragment_tests) in; layout(binding = 0) uniform CameraUBO { mat4 projection; mat4 view; vec3 position; } camera; layout(push_constant) uniform ViewportConstants { vec4 ambient_light; ivec2 viewport_size; ivec2 tile_size; uint horizontal_tile_count; uint vertical_tile_count; uint max_lights_per_tile; uint max_lights; } viewport_constants; layout(set = 2, binding = 0) uniform sampler2D texture_sampler; struct LightCullingData { uint visible_light_indices[1024]; uint total_visible_lights; }; layout(set = 3, binding = 0) buffer PointLightSSBO { PointLight lights[light_count]; } point_light_SSBO; layout(set = 3, binding = 1) buffer readonly VisibleLights { LightCullingData visible_lights[]; }; layout(location = 0) out vec4 out_color; void main() { //Get the tile based on current fragment. ivec2 tile = ivec2(gl_FragCoord.xy / viewport_constants.tile_size); uint tile_index = tile.y * viewport_constants.horizontal_tile_count + tile.x; //Base color and base illuminance from texture and ambient light. vec4 diffuse_map = texture(texture_sampler, in_uv); vec3 base_illuminance = diffuse_map.rgb * (viewport_constants.ambient_light.rgb * viewport_constants.ambient_light.a); vec3 illuminance = base_illuminance; //Iterate through the visible lights in the current tile. for(int i = 0; i < visible_lights[tile_index].total_visible_lights; ++i) { //Extract the light based on the compute shader culled storage buffer. PointLight light = point_light_SSBO.lights[visible_lights[tile_index].visible_light_indices[i]]; vec3 light_direction = normalize(light.position - in_position); //If the light points away, fragment not affected. float lambertian = max(dot(light_direction, in_normal), 0.0); if(lambertian > 0.0f) { //if the distance to light is smaller than light radius, fragment not affected. float light_distance = distance(light.position, in_position); if (light_distance < light.luminance) { //Fake specular vec3 camera_direction = normalize(camera.position - in_position); vec3 H = normalize(light_direction + camera_direction); float theta = max(dot(H, in_normal), 0.0); float specular = pow(theta, 256.0f); float attenuation = clamp(1.0 - light_distance * light_distance / (light.luminance * light.luminance), 0.0, 1.0); illuminance += light.color * attenuation * (lambertian * diffuse_map.rgb + specular); } } } out_color = vec4(illuminance, 1.0f); }