The attenuation equation you posted, strictly speaking, does not allow for any finite light radius, since the attenuation will not go to zero at any finite distance.
However, in practical terms there will be some distance at which the light becomes too dim to see. If you define a luminance threshold, you can plug this into the attenuation formula and solve to see where the light crosses this threshold. In pseudocode:
// Get light's luminance using Rec 709 luminance formula
lightLuminance = dot(lightColor, vec3(0.2126, 0.7152, 0.0722))
// Minimum luminance threshold - tweak to taste
minLuminance = 0.01
// Solve attenuation equation to get radius where it falls to minLuminance
lightRadius = solveQuadratic(kc - lightLuminance / minLuminance, kl, kq)
In an HDR context, you might have variable exposure - in that case, minLuminance
should probably be scaled inversely by exposure so that it represents a consistent display-referred luminance. This means lights will have different radius at different exposures though, which might be undesirable (e.g. you're putting them into a spatial subdivision structure).
On the other hand, you might want to tweak the attenuation so that it actually falls off to zero at a finite distance. A common way to do this is define an inner radius and outer radius, and multiply the attenuation function by a linear ramp that fades the light down to zero between the inner and outer radius, such as
attenuation *= saturate((radiusOuter - d) / (radiusOuter - radiusInner));
(In the shader, of course you should transform this into just saturate(d * a + b)
and precalculate a
and b
to match the previous formula, rather than doing the subtractions and divide per pixel.)