You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
168 lines
6.2 KiB
GLSL
168 lines
6.2 KiB
GLSL
//////// ================================
|
|
//////// SETTINGS
|
|
//////// ================================
|
|
//////// Check the Common tab for settings.
|
|
|
|
//////// ================================
|
|
//////// DOCS: Declarations & documentation
|
|
//////// ================================
|
|
|
|
// Retrieve a frame from Buffer A and then apply postprocessing.
|
|
void mainImage(out vec4 fragColor, in vec2 fragCoord);
|
|
|
|
/// Given a color which clips outside the color space (some channel is >1.0),
|
|
/// reduce the brightness (without affecting hue or saturation) until it no
|
|
/// longer clips. (The default behavior without doing this is just clipping,
|
|
/// which affects the saturation of the color dramatically, often turning colors
|
|
/// into 100% white pixels.)
|
|
vec3 correct_saturation(vec3 color);
|
|
|
|
/// Round to the nearest color in DITHER_COLORS.
|
|
vec4 nearest_color(vec4 color);
|
|
|
|
/// The length of the edge of the Bayer matrix used for dithering.
|
|
const uint DITHER_SIZE = uint(1)<<uint(DITHER_BASE);
|
|
|
|
/// Apply ordered dithering, which reduces color banding and produces the appearance
|
|
/// of more colors when in a limited color space (e.g. when making a GIF*).
|
|
vec4 dither(uvec2 coord, vec4 color);
|
|
|
|
/// Directly index into a DITHER_SIZE^2 Bayer Matrix, returning a number from -0.5 to 0.5.
|
|
float bayer(uvec2 coord);
|
|
|
|
/// The width in bits of a single coordinate into the Bayer matrix
|
|
/// for bitwise operations (log2(DITHER_SIZE) i.e. just DITHER_BASE).
|
|
const uint BIT_WIDTH = uint(DITHER_BASE);
|
|
|
|
/// Bitwise reverse the lower BIT_WIDTH bits of the integer.
|
|
/// e.g. with bit width 5, 11010 --> 01011.
|
|
uint bit_reverse(uint x);
|
|
|
|
/// Bitwise interleave two integers of length BIT_WIDTH into a single
|
|
/// 2*BIT_WIDTH integer.
|
|
///
|
|
/// example interleave:
|
|
///
|
|
/// x = 0 1 0 0 1
|
|
/// y = 1 0 0 1 1
|
|
/// ----------
|
|
/// r = 0110000111
|
|
uint bit_interleave(uint x, uint y);
|
|
|
|
//////// ================================
|
|
//////// IMPL: Implementation
|
|
//////// ================================
|
|
|
|
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
vec2 uv = fragCoord/iResolution.xy;
|
|
vec4 color = texture(iChannel0, uv);
|
|
|
|
// NOTE: it is possible for this renderer to emit colors brighter than 1.0,
|
|
// for example if you use very bright or many light sources. These colors will be
|
|
// displayed incorrectly, appearing desaturated and having their brightness
|
|
// clamped to whatever color output is supported.
|
|
//
|
|
// This is common in particular if you have very bright lights in a scene,
|
|
// which is sometimes necessary for objects to be clearly visible. The result
|
|
// will be you seeing flashes of over-bright white pixels where you should
|
|
// see color. One way to mitigate this is by increasing the number of samples per
|
|
// pixel; the average brightness per pixel is generally less than 1.0 when averaged
|
|
// out with the (more common) black pixels when no light source is encountered.
|
|
//
|
|
// Another mitigation approach is to do color correction, where instead of
|
|
// trying to preserve the brightness by clamping the RGB values and losing saturation,
|
|
// you try to preserve the saturation by scaling down the brightness until the
|
|
// full saturation of the colors is visible (or at least part of it).
|
|
|
|
#if SATURATION_CORRECTION == 1
|
|
color.rgb = correct_saturation(color.rgb);
|
|
#else
|
|
// The default behavior if you don't do anything.
|
|
//color = clamp(vec4(0.), color, vec4(1.));
|
|
#endif
|
|
|
|
color.rgb = linear2srgb(color.rgb);
|
|
|
|
#if ENABLE_DITHER == 1
|
|
// Dithering after sRGB conversion is slightly worse because the bayer matrix
|
|
// is linear whereas sRGB is non-linear, but if you do it *before* conversion,
|
|
// then adjusted colors won't be *quite* close enough to nearest_color that they
|
|
// should be closest to, which creates horrible artifacts after calling
|
|
// `nearest_color` (and is just slightly wrong in general).
|
|
uvec2 coord = uvec2(fragCoord); // for dithering
|
|
color = dither(coord, color);
|
|
#endif
|
|
|
|
#if DITHER_NEAREST == 1
|
|
color = nearest_color(color);
|
|
#endif
|
|
|
|
fragColor = color;
|
|
}
|
|
|
|
vec3 correct_saturation(vec3 color) {
|
|
// TODO: I'm sure there's a way to do this directly without having to
|
|
// convert between color spaces twice. This was just more convenient in the moment.
|
|
|
|
// Convert to HSV so we can correct value (brightness) without affecting
|
|
// hue or saturation.
|
|
color.xyz = rgb2hsv(color.rgb);
|
|
color.z = min(color.z, 1.);
|
|
color.rgb = hsv2rgb(color.xyz);
|
|
return color;
|
|
}
|
|
|
|
//////// --------------------------------
|
|
//////// DITHER: Ordered dithering
|
|
//////// --------------------------------
|
|
//////// https://en.wikipedia.org/wiki/Ordered_dithering
|
|
|
|
vec4 nearest_color(vec4 color) {
|
|
return floor(color * float(DITHER_COLORS)) / float(DITHER_COLORS);
|
|
}
|
|
|
|
vec4 dither(uvec2 coord, vec4 color) {
|
|
if (DITHER_SIZE < uint(2)) return color;
|
|
coord %= DITHER_SIZE;
|
|
vec4 bias = vec4(
|
|
bayer(coord),
|
|
bayer(uvec2(uint(DITHER_SIZE) - coord.x - uint(1), coord.y)),
|
|
bayer(uvec2(coord.x, uint(DITHER_SIZE) - coord.y - uint(1))),
|
|
bayer(uvec2(uint(DITHER_SIZE) - coord.x - uint(1), uint(DITHER_SIZE) - coord.y - uint(1)))
|
|
);
|
|
return color + (bias / float(DITHER_COLORS));
|
|
}
|
|
|
|
float bayer(uvec2 coord) {
|
|
// Magic bitwise formula from Wikipedia produces values from 0 to (DITHER_SIZE^2)-1.
|
|
uint magic = bit_reverse(bit_interleave(coord.x ^ coord.y, coord.x));
|
|
// Convert to normalized float from -0.5 to 0.5.
|
|
return float(magic+uint(1)) / (float(DITHER_SIZE)*float(DITHER_SIZE)) - 0.5;
|
|
}
|
|
|
|
uint bit_reverse(uint x) {
|
|
uint hi = uint(1 << BIT_WIDTH-uint(1));
|
|
uint lo = uint(1);
|
|
for (uint i = uint(0); i < BIT_WIDTH/uint(2); i++) {
|
|
uint bit_hi = x & hi;
|
|
uint bit_lo = x & lo;
|
|
x &= ~hi & ~lo;
|
|
if (bit_hi > uint(0)) x |= lo;
|
|
if (bit_lo > uint(0)) x |= hi;
|
|
hi >>= 1;
|
|
lo >>= 1;
|
|
}
|
|
return x;
|
|
}
|
|
|
|
uint bit_interleave(uint x, uint y) {
|
|
uint mask = uint(1) << BIT_WIDTH-uint(1);
|
|
uint acc = uint(0);
|
|
for (uint i = uint(0); i < BIT_WIDTH; i++) {
|
|
acc |= (x & mask) << uint(2)*i + uint(1);
|
|
acc |= (y & mask) << uint(2)*i;
|
|
mask >>= 1;
|
|
}
|
|
return acc;
|
|
}
|