Add option presets and (extremely sexy) dithering.

* I'm soooo happy with how the dithering turned out.
* Also I thought I added the presets last commit, but I managed
  to fail to add them somehow.
master
James T. Martin 2021-12-18 02:26:06 -08:00
parent e73620c588
commit 69ea1d05cd
Signed by: james
GPG Key ID: 4B7F3DA9351E577C
2 changed files with 206 additions and 23 deletions

View File

@ -1,14 +1,112 @@
// All of the interesting code and settings are in Buffer A.
//////// ================================
//////// SETTINGS: Settings
//////// ================================
//////// Most of the interesting code and settings are in Buffer A.
//////// This file consists only of postprocessing.
// The dithering looks *fantastic*. Honestly, at 1440p it makes even
// 3 bits per color channel look *very* convincing. As far as I'm aware
// there are no downsides to leaving it enabled. It would probably help
// reduce color banding and improve color clarity in general.
#define ENABLE_DITHER
// The number of available colors *per channel*.
const uint DITHER_COLORS = uint(1<<8);
// The size of the Bayer matrix is 2^DITHER_BASE, so e.g. 4 is a 16x16 matrix.
// This can't be larger than 16 because dithering is implemented using a 16-bit bit hack.
const uint DITHER_BASE = uint(16);
// Artifically restrict the colors to those specified in DITHER_COLORS.
// If you set DITHER_COLORS to 6 or so and enable DITHER_NEAREST then screenshot
// and convert the image to a GIF* using an appropriate conversion tool,
// it will look exactly the same as it looks on your screen.
// This was the motivating reason for adding dithering, and it looks amazing.
//
// * I pronounce it "yif". Fite me.
//#define DITHER_NEAREST
//////// ================================
//////// IMPL: Implementation
//////// ================================
/// Convert a color from linear RGB to the sRGB color space.
vec3 linear2srgb(vec3 color);
vec4 dither(uvec2 coord, vec4 color);
vec4 nearest_color(vec4 color);
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec2 uv = fragCoord/iResolution.xy;
vec3 color = texture(iChannel0, uv).rgb;
fragColor = vec4(linear2srgb(color), 1.0);
vec2 uv = fragCoord/iResolution.xy;
uvec2 coord = uvec2(fragCoord); // for dithering
vec4 color = texture(iChannel0, uv);
color.rgb = linear2srgb(color.rgb);
#ifdef ENABLE_DITHER
color = dither(coord, color);
#endif
#ifdef DITHER_NEAREST
color = nearest_color(color);
#endif
fragColor = color;
}
//////// --------------------------------
//////// DITHER: Ordered dithering
//////// --------------------------------
//////// https://en.wikipedia.org/wiki/Ordered_dithering
const uint DITHER_SIZE = uint(1)<<DITHER_BASE;
const uint BIT_WIDTH = DITHER_BASE;
vec4 nearest_color(vec4 color) {
return floor(color * float(DITHER_COLORS)) / float(DITHER_COLORS);
}
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;
}
float bayer(uvec2 coord) {
// magic bitwise formula from Wikipedia
uint magic = bit_reverse(bit_interleave(coord.x ^ coord.y, coord.x));
return float(magic+uint(1)) / (float(DITHER_SIZE)*float(DITHER_SIZE)) - 0.5;
}
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));
}
//////// ================================
//////// VENDOR: Vendored code
//////// ================================
@ -31,4 +129,4 @@ vec3 linear2srgb(vec3 linear_rgb) {
#else
return pow(linear_rgb, vec3(1./2.2));
#endif
}
}

View File

@ -2,6 +2,18 @@
//////// SETTINGS: Settings
//////// ================================
//
// If you don't feel like tweaking settings, try one of these presets:
//
// 0: Maximize FPS at the cost of image quality. (DEFAULT)
// 1: A balance between image quality and FPS.
// 2: Just render a pretty static image.
// 3: Demonstrate off the weird tiling render feature.
//
// The user settings below will override whatever preset you use.
//
#define PRESET 0
//////// --------------------------------
//////// User settings
//////// --------------------------------
@ -17,10 +29,10 @@
// image quality, reducing graininess and preventing overly-bright pixels ("fireflies").
// However, how much GPU power you need to render a frame scales linearly with
// the number of samples.
#define SAMPLES 1
//#define SAMPLES 1
// The maximum number of times light can reflect or scatter before it is extinguished.
#define PATH_SEGMENTS 14
//#define PATH_SEGMENTS 14
// If a pixel color is too bright for fit in sRGB, there are two ways to handle it:
//
@ -43,16 +55,16 @@
// This shader natively uses a square (circular?) aspect ratio. With ASPECT_RATIO_CROP
// enabled, if you use a wide aspect ratio, the frame will have its height
// cropped so that the image can take up the full width of the screen.
#define ASPECT_RATIO_CROP 1
//#define ASPECT_RATIO_CROP 1
// This setting affects how far you zoom in on the scene.
// Greater values = more zoom. Fractional values zoom out. Negative values mirror the scene.
#define FOV 1.5
//#define FOV 1.5
// Camera position and angle. (Feel free to reference `time` here.)
#define CAMERA_POS vec3(0.)
//#define CAMERA_POS vec3(0.)
// (Don't worry, we call `normalize` for you.
#define CAMERA_DIR vec3(0., 0., 1.)
//#define CAMERA_DIR vec3(0., 0., 1.)
///
/// TILE_PERSPECTIVE and CLAMP_PERSPECTIVE are only relevant if you zoom out
@ -66,17 +78,17 @@
// This tiling is infinite. You might want to combine this with an IMAGE_OFFSET of
// (-1, 0) so that you can see two whole hemispheres instead of one whole hemisphere
// and two halves on opposite sides.
#define TILE_PERSPECTIVE 0
//#define TILE_PERSPECTIVE 0
// Points on the screen outside of the unit circle (within a tile) are clamped
// to the nearest point on the unit circle. This doesn't look very good, but
// might be preferable to just rendering black?
#define CLAMP_PERSPECTIVE 0
//#define CLAMP_PERSPECTIVE 0
// Slide the image around on the screen. Each time is `2x2` centered on the
// origin, so an offset of e.g. (2,0) with TILE_PERSPECTIVE enabled
// will show you the portion of the scene *behind* you.
#define IMAGE_OFFSET vec2(0., 0.)
//#define IMAGE_OFFSET vec2(0., 0.)
////
//// Simulation settings
@ -84,13 +96,13 @@
// The maximum number of steps a ray can take during marching before giving up
// and colliding with nothing. This prevents scenes from taking infinite time to render.
#define MAX_STEPS 200
//#define MAX_STEPS 200
// The maximum distance a ray can travel before we give up and just say it collides
// with nothing. This helps prevent the background from appearing warped by the foreground
// due to rays which march close to a foreground object run out of steps before
// reaching their destination when slightly farther rays do reach their target.
#define MAX_DIST 20.
//#define MAX_DIST 20.
// Average the color across frames by storing them in the buffer.
// This is like supersampling, but across frames instead of within a pixel,
@ -116,7 +128,7 @@
// appear to jerk back and forth, so this probably shouldn't be any higher
// than (the reciprocal of) your average framerate. Comment this out to
// remove any cap on the amount of motion blur.
#define MAX_TAA_DIFF (1./30.)
//#define MAX_TAA_DIFF (1./30.)
//////// --------------------------------
@ -142,11 +154,13 @@
//
// I expect that a minimum distance of 2^(-9) would work until about 10km from the origin
// with 32-bit floating point before starting to break down, but I have not tested it.
#define MIN_DIST (0.001953125/8.)
//#define MIN_DIST (0.001953125)
// The distance between samples when estimating a surface's normal. Smaller values result
// in more precise calculations, but are more sensitive to numerical imprecision.
// This should probably be less than MIN_DIST.
#define NORMAL_DELTA (MIN_DIST/4.)
//#define NORMAL_DELTA (MIN_DIST/4.)
// Only march this much of MIN_DIST at a time to account for imprecision in the distance
// calculations. Chosen by experimentation. If you have to set this low, that often means
// that there's a bug somewhere (e.g. you forgot to call `normalize`).
@ -154,17 +168,82 @@
// Right now, the simulation is numerically stable and I don't have to use it at all!
// But I often find that it's necessary to set this to around ~0.92 when debugging
// numerical issues.
#define IMPRECISION_FACTOR 1.
//#define IMPRECISION_FACTOR 1.
//////// --------------------------------
//////// Default settings
//////// Default settings & presets
//////// --------------------------------
//////// So you can restore a setting to its default value by commenting it out.
#ifndef PRESET
#define PRESET 0
#endif
//// PRESET 1
#if PRESET == 1
#ifndef SAMPLES
#define SAMPLES 6
#endif
#ifndef PATH_SEGMENTS
#define PATH_SEGMENTS 16
#endif
#ifndef MAX_TAA_DIFF
#define MAX_TAA_DIFF (1./30.)
#endif
//// PRESET 2
#elif PRESET == 2
#ifndef PATH_SEGMENTS
#define PATH_SEGMENTS 16
#endif
#ifndef MAX_STEPS
#define MAX_STEPS 300
#endif
#ifndef MAX_DIST
#define MAX_DIST 100.
#endif
#ifndef MIN_DIST
#define MIN_DIST (0.001953125/256.)
#endif
#ifndef MAX_TAA_DIFF
#define MAX_TAA_DIFF 0.
#endif
#ifndef AVERAGE_FRAMES
#define AVERAGE_FRAMES 1
#endif
#ifndef FREEZE_TIME
#define FREEZE_TIME 2.75
#endif
#ifndef SATURATION_CORRECTION
#define SATURATION_CORRECTION 0
#endif
//// PRESET 3
#elif PRESET == 3
#ifndef FOV
#define FOV 0.5
#endif
#ifndef TILE_PERSPECTIVE
#define TILE_PERSPECTIVE 1
#endif
#ifndef CLAMP_PERSPECTIVE
#define CLAMP_PERSPECTIVE 1
#endif
#ifndef IMAGE_OFFSET
#define IMAGE_OFFSET vec2(0., 0.)
#endif
#endif
//// PRESET 0 (default values)
#ifndef SAMPLES
#define SAMPLES 1
#endif
#ifndef PATH_SEGMENTS
#define PATH_SEGMENTS 14
#define PATH_SEGMENTS 10
#endif
#ifndef SATURATION_CORRECTION
@ -178,6 +257,12 @@
#ifndef FOV
#define FOV 1.5
#endif
#ifndef CAMERA_POS
#define CAMERA_POS vec3(0.)
#endif
#ifndef CAMERA_DIR
#define CAMERA_DIR vec3(0., 0., 1.)
#endif
#ifndef ASPECT_RATIO_CROP
#define ASPECT_RATIO_CROP 1
#endif
@ -589,7 +674,7 @@ ray camera(ray r) {
vec3 d = normalize(CAMERA_DIR);
// point projection relative to direction
// this really ought to be simplified,
// TODO: this really ought to be simplified,
// but I don't know the math to understand how to do it.
vec3 up = vec3(0., 1., 0.);
//vec3 x = cross(up, d);