Preventing banding by including truncated color depth as dithering
| An image with and without dither rounding |
| The same image with one band shaded a different color. |
NOTE: The stripey pattern on some of the cubes is an artifact of half-implemented SSAO. It is not an artifact of dither rounding.
In a deferred-renderer: Intermediate images are created and combined to form what the user sees each frame. In my case, one of these intermediate images uses 16bit floats for each color channel. This is really nice for a lot of reasons.
One reason for this is to allow certain pixels to be "brighter than white" (https://learnopengl.com/Advanced-Lighting/HDR). Another reason is to retain enough precision to produce any color when converting from linear color space to sRGB.
This post is about yet another good reason to have that high-bit-depth image: The extra color information can be included in the final image as dithering! This prevents the otherwise unavoidable banding that comes from having only 256 brightness levels for any one color.
The algorithm: Before truncating a high-bit-depth pixel color component, consider the fractional shade that would otherwise be discarded as a value between 0 and 1. Then, consider the pixel's index in the image as an element in a row-major array. Multiply the fractional shade by the pixel's index, and if the fractional part of the product is less than the fractional shade: Ceiling-round the pixel color component. Otherwise let truncation floor-round it.
Conceptually, this is deciding whether to round each color component up or down (instead of truncating--which is just down) based on whether its pixel position is white in a black-and-white dithering of the image area where the dithering approximates the gray of value: 'fractional shade'.
I came up with this algorithm on my own, but this domain is well-trodden--so I figure it already has a name, and that there are many better approaches. In particular, I think my dithering approach is naive.
Comments
Post a Comment