2D lighting creates distinct layers

My 2D lighting is basically this: https://gmjosack.github.io/posts/my-first-2d-pixel-shaders-part-3/ .

The problem is the light have very distinct “layers” which makes them look worse. Here is a picture:


How would I go about making it smoother? I have tried to use a large light texture but that doesnt make a difference.
Thanks beforehand!

That effect is called ‘Banding’ and professional games (such as Fallout 3) have actually been published with it visible in the lighting, and especially in the sky when transitioning from day to night and back.

Its caused by rounding a light value too early, typically to the range of 0 to 255. You’d think that since the colours on screen are all 0 to 255 this shouldn’t matter, but it does, since the rounding happens multiple times creating these visible bands of colours.

Without seeing your code, helping you more is difficult.

However, going by that tutorial, you can consider switching the lightmap (called ‘lightsTarget’ in the tutorial) to use floats, i.e. create it using SurfaceFormat.Single rather than the default, which is SurfaceFormat.Color. Then performing the lighting calculations using floats throughout the shader, that allows the final draw that combines them into the colour image to make your lighting much smoother since its grabbing a float value for the light, rather than a rounded integer.

BUT, that is a little more complicated than the tutorial you’ve linked as it requires custom shaders.

Umm… whole reason behind this case is using low color range (for gradient, thus intensity of light source itself) for large radius light. I mean there is literally no color he could be displaying to soften transition given intensity of light source. No HDR mapping is going to help here, there is NO more color in 8bit per channel (also considering that he is pretty much just using shades of grey here as the color of light is white and no background or whatever to help it blend).

But seriously apart from adding some actual objects into scene or increasing intensity of light / decreasing attenuation there is absolutely not magic that can be done.

Look at three values near light source, it is 38,38,38 to 37,37,37 to 36,36,36… what do you want to HDR map here, what rounding error is happening here according to you given we are obviously going to be stuck with 8bit per channels monitors (well… realistically not even that for most of us).

Thanks for the answer!

What do you mean by using floats throughout the shader calculation? Isn’t that how it’s already working?

This still happens when there are small lights. Since the lights are going from white to black doesnt that mean I have 255 colors to use? It doesnt look like there are 255 different in the lights. I guess i might have to redo the lights to not rely on textures and only shaders.

Light you posted has intensity set in way that near light color is rgb(38,38,38), that means you have 38 shades of grey (excluding black obviously) to use over your whole gradient, that means smooth gradient only over 38 pixels while your radius is much, much bigger.

32 bit Float is used in shader, definitely, he meant rendering into RenderTarget… let me elaborate here, I honestly don´t think he knows what he is talking about (sorry about that, no offense, but I don´t know how to prevent future confusion). You are obviously rendering straight into backbuffer (right on the screen) which will always be at best 8 bits per channel. What he suggested is relevant only when you are rendering into render target which is being processed further steps. For example HDR mapping in which case you definitely want to use float precision Surface Format, but definitely not Single… that´s one channel buffer (often used for custom Depth buffer) and since it is expected for lights to have color it wont be ideal solution, you would be using most likely halfVec or Vec format (16/32 bit) with 3 or 4 channels. This is useful for instance for advanced bloom effects, light shafts extraction and so on while of course provided possibility of mapping result in nonlinear manner (HDR tone mapping - getting range above 1.0 into 0.0 - 1.0 range). I think tho those are information that are not relevant at all to your current problem and might lead to nothing but confusion at this point. But if you would like to know more later on let me know.

Edit: I´ve took quick look on that tutorial, heh, limitation itself comes also from that texture. Using texture for “light” has EXTREMELY limited use. Anyway what I´ve wrote still applies, there is no postprocessing that would be affected precision of render target higher than 8bits per channel. Thing is that while using texture other limiting factor will be also resolution of that map. I am not sure what you are going for exactly, what´s your goal, I would like to suggest bit more complex approach and definitely generating light map completely through shader without using texture.

Thanks alot for your help! I understand the problem now and I guess I have to do something different.

Point is with background it should not matter, it shouldn´t be visible or annoying. What you can also do is to change curve of falloff. Right now it looks to me like you are using gaussian blurr on circle. Light as a energy source should follow inverse square law but depending on your use you can cheat a bit and just use inverse function, it will look bit strange but it will help push gradient of falloff towards smaller area further away from light source (thus large are around source will be of constant color). I recommend you to worry about this later, unless as I´ve said you plan more complex lightning in which case I recommend completely change your approach on lights.