(Path March v4) Split stuff (esp. settings) into common.frag
parent
69ea1d05cd
commit
12b4129fb0
|
@ -1,132 +0,0 @@
|
||||||
//////// ================================
|
|
||||||
//////// 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;
|
|
||||||
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
|
|
||||||
//////// ================================
|
|
||||||
|
|
||||||
////
|
|
||||||
//// AUTHOR: unknown
|
|
||||||
////
|
|
||||||
|
|
||||||
vec3 linear2srgb(vec3 linear_rgb) {
|
|
||||||
// I believe the first version is technically more accurate,
|
|
||||||
// but the difference is usually negligable in practice.
|
|
||||||
#if 1
|
|
||||||
// copied from somewhere on the internet
|
|
||||||
bvec3 cutoff = lessThan(linear_rgb, vec3(0.0031308));
|
|
||||||
vec3 higher = vec3(1.055)*pow(linear_rgb, vec3(1.0/2.4)) - vec3(0.055);
|
|
||||||
vec3 lower = linear_rgb * vec3(12.92);
|
|
||||||
|
|
||||||
return mix(higher, lower, cutoff);
|
|
||||||
// end copied from somewhere on the internet
|
|
||||||
#else
|
|
||||||
return pow(linear_rgb, vec3(1./2.2));
|
|
||||||
#endif
|
|
||||||
}
|
|
|
@ -1,300 +1,7 @@
|
||||||
//////// ================================
|
//////// ================================
|
||||||
//////// SETTINGS: Settings
|
//////// SETTINGS
|
||||||
//////// ================================
|
//////// ================================
|
||||||
|
//////// Check the Common tab for 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
|
|
||||||
//////// --------------------------------
|
|
||||||
//////// Tweak these according to your preferences and the power of your graphics card.
|
|
||||||
//////// Comment out a setting to restore it to its default value.
|
|
||||||
|
|
||||||
|
|
||||||
////
|
|
||||||
//// Sample settings
|
|
||||||
////
|
|
||||||
|
|
||||||
// The number of color samples taken per pixel. Increasing this has a dramatic effect on
|
|
||||||
// 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
|
|
||||||
|
|
||||||
// The maximum number of times light can reflect or scatter before it is extinguished.
|
|
||||||
//#define PATH_SEGMENTS 14
|
|
||||||
|
|
||||||
// If a pixel color is too bright for fit in sRGB, there are two ways to handle it:
|
|
||||||
//
|
|
||||||
// 1. Clamp the pixel within the limits of sRGB, resulting in (near-)maximum
|
|
||||||
// brightness at the cost of the color's saturation. (If it's too bright, it'll
|
|
||||||
// become entirely white.)
|
|
||||||
// 2. Reduce the brightness of the color until it fits within sRGB, preserving
|
|
||||||
// the color's saturation, but losing even *more* brightness.
|
|
||||||
//
|
|
||||||
// Correction for saturation generally looks better, but isn't usually necessary
|
|
||||||
// for more than five or so samples (because the bright pixels will average out
|
|
||||||
// with the dark pixels and fall back within sRGB), so this is *on* by default
|
|
||||||
// with 1-2 samples and *off* by default with 5+ samples.
|
|
||||||
//#define SATURATION_CORRECTION 1
|
|
||||||
|
|
||||||
////
|
|
||||||
//// Perspective settings
|
|
||||||
////
|
|
||||||
|
|
||||||
// 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
|
|
||||||
|
|
||||||
// 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
|
|
||||||
|
|
||||||
// Camera position and angle. (Feel free to reference `time` here.)
|
|
||||||
//#define CAMERA_POS vec3(0.)
|
|
||||||
// (Don't worry, we call `normalize` for you.
|
|
||||||
//#define CAMERA_DIR vec3(0., 0., 1.)
|
|
||||||
|
|
||||||
///
|
|
||||||
/// TILE_PERSPECTIVE and CLAMP_PERSPECTIVE are only relevant if you zoom out
|
|
||||||
/// (e.g. an FOV < ~1.15). For more information on how and why these settings
|
|
||||||
/// behave the way they do, see their extended descriptions in the `project` function.
|
|
||||||
///
|
|
||||||
|
|
||||||
// Points on the screen >1 or <-1 show the portion of the scene *behind* you,
|
|
||||||
// mirrored so that the edges of each adjacent tile lines up (e.g. tiles above
|
|
||||||
// and below are mirrored vertically, to the left and right horizontally).
|
|
||||||
// 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
|
|
||||||
|
|
||||||
// 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
|
|
||||||
|
|
||||||
// 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.)
|
|
||||||
|
|
||||||
////
|
|
||||||
//// Simulation settings
|
|
||||||
////
|
|
||||||
|
|
||||||
// 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
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
|
|
||||||
// Average the color across frames by storing them in the buffer.
|
|
||||||
// This is like supersampling, but across frames instead of within a pixel,
|
|
||||||
// which lets you render with thousands of samples without crashing.
|
|
||||||
// It's strongly advised that you enable FREEZE_TIME when this is enabled!
|
|
||||||
// This uses iFrame, so if you want to enable this, make sure you hit the
|
|
||||||
// "reset time" function or things will get screwed up.
|
|
||||||
//#define AVERAGE_FRAMES 1
|
|
||||||
|
|
||||||
// Set a time in seconds. The simulation will be frozen at this point in time every frame.
|
|
||||||
// Comment this out to allow time to pass normally.
|
|
||||||
//#define FREEZE_TIME 2.75
|
|
||||||
|
|
||||||
// Loop time over an interval of this duration, beginning at FREEZE_TIME,
|
|
||||||
// or 0, if FREEZE_TIME is not set.
|
|
||||||
//#define LOOP_TIME 0.
|
|
||||||
|
|
||||||
// Set the maximum duration of temporal antialiasing (i.e. how much time
|
|
||||||
// motion blur smears across). Note that this is a *maximum* time, and motion
|
|
||||||
// blur will never be greater than the duration of a frame. That said, when rendering
|
|
||||||
// a still image with FREEZE_TIME you probably want this set to 0., and if you're
|
|
||||||
// stuttering a lot, the large variance in frame times can make objects in the image
|
|
||||||
// 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.)
|
|
||||||
|
|
||||||
|
|
||||||
//////// --------------------------------
|
|
||||||
//////// Internal settings
|
|
||||||
//////// --------------------------------
|
|
||||||
//////// If you're just viewing the shader, you shouldn't usually need to tweak these.
|
|
||||||
|
|
||||||
// The minimum distance between two points before they are considered the same point.
|
|
||||||
// Setting lower values increases the sharpness of the image at the cost of performance
|
|
||||||
// and rounding errors at objects very far from 0.
|
|
||||||
//
|
|
||||||
// Ray marching halves the distance to the surface of an object each iteration, but the
|
|
||||||
// end goal of ray marching is to pass slightly *inside* the object. Setting a minimum
|
|
||||||
// distance prevents zeno's paradox. This also serves as a optimization
|
|
||||||
// because the number of steps increases logarithmically as you decrease the minimum distance.
|
|
||||||
//
|
|
||||||
// Chosen to be 2^(-9), or about ~2mm, because that's the largest you can set it before
|
|
||||||
// the quality of the image is significantly effected. You can set it as low as about
|
|
||||||
// 2^(-19) before things begin to break. It's good to experiment with both high and low
|
|
||||||
// values to help find bugs in the numerical precision of the light simulation.
|
|
||||||
// If you have precision bugs, the simulation ends up getting affected pretty dramatically
|
|
||||||
// by changes to MIN_DIST, whereas a numerically stable simulation is not affected much at all.
|
|
||||||
//
|
|
||||||
// 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)
|
|
||||||
|
|
||||||
// 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.)
|
|
||||||
|
|
||||||
// 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`).
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
|
|
||||||
//////// --------------------------------
|
|
||||||
//////// 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 10
|
|
||||||
|
|
||||||
#endif
|
|
||||||
#ifndef SATURATION_CORRECTION
|
|
||||||
#if SAMPLES > 5
|
|
||||||
#define SATURATION_CORRECTION 0
|
|
||||||
#else
|
|
||||||
#define SATURATION_CORRECTION 1
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#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
|
|
||||||
#ifndef TILE_PERSPECTIVE
|
|
||||||
#define TILE_PERSPECTIVE 0
|
|
||||||
#endif
|
|
||||||
#ifndef CLAMP_PERSPECTIVE
|
|
||||||
#define CLAMP_PERSPECTIVE 0
|
|
||||||
#endif
|
|
||||||
#ifndef IMAGE_OFFSET
|
|
||||||
#define IMAGE_OFFSET vec2(0., 0.)
|
|
||||||
#endif
|
|
||||||
#ifndef MAX_STEPS
|
|
||||||
#define MAX_STEPS 200
|
|
||||||
#endif
|
|
||||||
#ifndef MAX_DIST
|
|
||||||
#define MAX_DIST 20.
|
|
||||||
#endif
|
|
||||||
#ifndef AVERAGE_FRAMES
|
|
||||||
#define AVERAGE_FRAMES 0
|
|
||||||
#endif
|
|
||||||
// FREEZE_TIME, LOOP_TIME, and MAX_TAA_DIFF are *undefined* by default.
|
|
||||||
|
|
||||||
#ifndef MIN_DIST
|
|
||||||
#define MIN_DIST (0.001953125/8.)
|
|
||||||
#endif
|
|
||||||
#ifndef NORMAL_DELTA
|
|
||||||
#define NORMAL_DELTA (MIN_DIST/4.)
|
|
||||||
#endif
|
|
||||||
#ifndef IMPRECISION_FACTOR
|
|
||||||
#define IMPRECISION_FACTOR 1.
|
|
||||||
#endif
|
|
||||||
|
|
||||||
//////// ================================
|
//////// ================================
|
||||||
//////// DOCS: Declarations & documentation
|
//////// DOCS: Declarations & documentation
|
||||||
|
@ -425,22 +132,11 @@ int medium(vec3 pos);
|
||||||
//////// UTIL: Utility functions (e.g. random number generation)
|
//////// UTIL: Utility functions (e.g. random number generation)
|
||||||
//////// --------------------------------
|
//////// --------------------------------
|
||||||
|
|
||||||
// Convenience definitions
|
|
||||||
#define INF (1./0.)
|
|
||||||
// NOTE: I used to use `sqrt(-1)`, but apparently that doesn't evaluate to NaN????
|
|
||||||
// This makes me wonder if NaN isn't portable due to constant folding or something.
|
|
||||||
#define NAN (0./0.)
|
|
||||||
#define NULL_RAY ray(vec3(0.), vec3(0.))
|
#define NULL_RAY ray(vec3(0.), vec3(0.))
|
||||||
|
|
||||||
/// Used to forcibly set the pixel color from functions. Used for debugging.
|
/// Used to forcibly set the pixel color from functions. Used for debugging.
|
||||||
vec3 _bug = vec3(NAN);
|
vec3 _bug = vec3(NAN);
|
||||||
|
|
||||||
/// Return a random number between 0 and 1 (with uniform distribution);
|
|
||||||
float rand();
|
|
||||||
|
|
||||||
/// Use the fragment coordinate and current frame to seed the random number generator.
|
|
||||||
void seed_randoms(vec2 fragCoord);
|
|
||||||
|
|
||||||
/// Randomly select a direction on a hemisphere facing the direction of the normal,
|
/// Randomly select a direction on a hemisphere facing the direction of the normal,
|
||||||
/// with a bias toward directions close to the normal.
|
/// with a bias toward directions close to the normal.
|
||||||
/// Specifically, the bias is by `dot(norm, dir)`, making this a cosine-weighted
|
/// Specifically, the bias is by `dot(norm, dir)`, making this a cosine-weighted
|
||||||
|
@ -448,9 +144,24 @@ void seed_randoms(vec2 fragCoord);
|
||||||
/// rendering equation.
|
/// rendering equation.
|
||||||
vec3 cosine_direction(vec3 norm);
|
vec3 cosine_direction(vec3 norm);
|
||||||
|
|
||||||
// Convert between RGB and HSV. Used for SATURATION_CORRECTION.
|
/// Create a translation + rotation matrix from a position and direction.
|
||||||
vec3 rgb2hsv(vec3 c);
|
mat4 translate(vec3 pos, vec3 forward, vec3 up);
|
||||||
vec3 hsv2rgb(vec3 c);
|
mat4 translate(vec3 pos, vec3 forward);
|
||||||
|
mat4 translate(vec3 pos);
|
||||||
|
|
||||||
|
/// Rotate a vector angle using a matrix.
|
||||||
|
vec3 apply_rotate(mat4 r, vec3 d);
|
||||||
|
|
||||||
|
/// Get the direction of a matrix.
|
||||||
|
/// (Get the translated direction of (0, 0, 1).)
|
||||||
|
vec3 apply_rotate(mat4 r);
|
||||||
|
|
||||||
|
/// Translate a vector position by a matrix.
|
||||||
|
vec3 apply_translate(mat4 r, vec3 p);
|
||||||
|
|
||||||
|
/// Get the position offset of a matrix.
|
||||||
|
/// (Get the translated position of (0, 0, 0).)
|
||||||
|
vec3 apply_translate(mat4 r);
|
||||||
|
|
||||||
//////// ================================
|
//////// ================================
|
||||||
//////// IMPL: Implementation
|
//////// IMPL: Implementation
|
||||||
|
@ -462,7 +173,7 @@ vec3 hsv2rgb(vec3 c);
|
||||||
//////// --------------------------------
|
//////// --------------------------------
|
||||||
|
|
||||||
void mainImage(out vec4 fragColor, vec2 fragCoord) {
|
void mainImage(out vec4 fragColor, vec2 fragCoord) {
|
||||||
seed_randoms(fragCoord);
|
seed_randoms(vec3(fragCoord, float(iFrame)));
|
||||||
|
|
||||||
// Supersampling. Sample the color within each pixel multiple times and take the average.
|
// Supersampling. Sample the color within each pixel multiple times and take the average.
|
||||||
// In raster-based rendering, this is used mostly to prevent jagged edges;
|
// In raster-based rendering, this is used mostly to prevent jagged edges;
|
||||||
|
@ -521,33 +232,6 @@ void mainImage(out vec4 fragColor, vec2 fragCoord) {
|
||||||
|
|
||||||
if (!any(isnan(_bug))) { color = vec4(_bug, 1.); }
|
if (!any(isnan(_bug))) { color = vec4(_bug, 1.); }
|
||||||
|
|
||||||
// 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
|
|
||||||
// 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.
|
|
||||||
color.xyz = rgb2hsv(color.rgb);
|
|
||||||
color.z = min(color.z, 1.); // clamp value to 1
|
|
||||||
color.rgb = hsv2rgb(color.xyz);
|
|
||||||
#else
|
|
||||||
//color = clamp(vec4(0.), color, vec4(1.));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if AVERAGE_FRAMES
|
#if AVERAGE_FRAMES
|
||||||
vec2 uv = fragCoord/iResolution.xy;
|
vec2 uv = fragCoord/iResolution.xy;
|
||||||
vec4 rest = texture(iChannel0, uv).rgba;
|
vec4 rest = texture(iChannel0, uv).rgba;
|
||||||
|
@ -673,23 +357,9 @@ ray camera(ray r) {
|
||||||
// camera direction (faces forward, not up)
|
// camera direction (faces forward, not up)
|
||||||
vec3 d = normalize(CAMERA_DIR);
|
vec3 d = normalize(CAMERA_DIR);
|
||||||
|
|
||||||
// point projection relative to direction
|
mat4 trans = translate(pos, d);
|
||||||
// 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);
|
|
||||||
vec3 x = vec3(d.z, 0., -d.x);
|
|
||||||
//vec3 y = cross(d, x);
|
|
||||||
vec3 y = vec3(-d.y*d.x, d.z*d.z+d.x*d.x, -d.y*d.z);
|
|
||||||
|
|
||||||
mat3 rot = mat3(
|
return ray(apply_translate(trans, r.pos), apply_rotate(trans, r.dir));
|
||||||
x.x, y.x, d.x,
|
|
||||||
x.y, y.y, d.y,
|
|
||||||
x.z, y.z, d.z
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
return ray(r.pos + pos, normalize(rot * r.dir));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//////// --------------------------------
|
//////// --------------------------------
|
||||||
|
@ -839,6 +509,9 @@ transmission transmit(ray r) {
|
||||||
// don't bother reflecting off the lights.
|
// don't bother reflecting off the lights.
|
||||||
return transmission(NULL_RAY, INF);
|
return transmission(NULL_RAY, INF);
|
||||||
//return transmission(ray(np, cosine_direction(norm)), 0.);
|
//return transmission(ray(np, cosine_direction(norm)), 0.);
|
||||||
|
|
||||||
|
case 4: // generic diffuse material
|
||||||
|
return transmission(ray(np, cosine_direction(norm)), 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
return transmission(ray(vec3(0.), vec3(0.)), 1.);
|
return transmission(ray(vec3(0.), vec3(0.)), 1.);
|
||||||
|
@ -864,8 +537,13 @@ vec3 emit(ray i, ray o, vec3 color) {
|
||||||
switch (m) {
|
switch (m) {
|
||||||
|
|
||||||
case 0: // air material
|
case 0: // air material
|
||||||
if (any(isinf(o.pos))) return vec3(0.);
|
if (any(isinf(o.pos))) {
|
||||||
color += sqrt(distance(i.pos, o.pos)) * 0.0005;
|
#if SCENE == 2
|
||||||
|
return vec3(1.);
|
||||||
|
#else
|
||||||
|
return vec3(0.);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
return color;
|
return color;
|
||||||
|
|
||||||
case 1: // floor material
|
case 1: // floor material
|
||||||
|
@ -884,6 +562,9 @@ vec3 emit(ray i, ray o, vec3 color) {
|
||||||
color += vec3(5.) * dot(-i.dir, normal(i.pos, m));
|
color += vec3(5.) * dot(-i.dir, normal(i.pos, m));
|
||||||
return color;
|
return color;
|
||||||
|
|
||||||
|
case 4: // generic diffuse material
|
||||||
|
return color;
|
||||||
|
|
||||||
}
|
}
|
||||||
// unknown material
|
// unknown material
|
||||||
return vec3(1., 0., 1.);
|
return vec3(1., 0., 1.);
|
||||||
|
@ -965,28 +646,6 @@ vec3 nudge(vec3 pos, int m) {
|
||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
////
|
|
||||||
//// Combinators for creating signed distance functions and modelling objects.
|
|
||||||
////
|
|
||||||
|
|
||||||
float dist_plane(vec3 p, float y) {
|
|
||||||
return distance(p.y, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
// a plane with infinite depth
|
|
||||||
float dist_floor(vec3 p, float y) {
|
|
||||||
return p.y - y;
|
|
||||||
}
|
|
||||||
|
|
||||||
float dist_sphere(vec3 p, vec3 pos, float r) {
|
|
||||||
return distance(p, pos) - r;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Model difference/subtraction (objects in `a` but not in `b`).
|
|
||||||
float sub(float a, float b) {
|
|
||||||
return max(a, -b);
|
|
||||||
}
|
|
||||||
|
|
||||||
////
|
////
|
||||||
//// Cheat and calculate distance-related stuff with globals
|
//// Cheat and calculate distance-related stuff with globals
|
||||||
//// to reduce the amount of argument-passing we have to do.
|
//// to reduce the amount of argument-passing we have to do.
|
||||||
|
@ -1079,11 +738,46 @@ void update_scene(vec3 pos, int focus) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////
|
||||||
|
//// Combinators for creating signed distance functions and modelling objects.
|
||||||
|
////
|
||||||
|
|
||||||
|
float dist_plane(vec3 pos, mat4 trans) {
|
||||||
|
vec4 p1w = trans * vec4(0., 0., 0., 1.);
|
||||||
|
vec4 p2w = trans * vec4(1., 0., 0., 1.);
|
||||||
|
vec4 p3w = trans * vec4(0., 0., 1., 1.);
|
||||||
|
vec3 p1 = p1w.xyz / p1w.w;
|
||||||
|
vec3 p2 = p2w.xyz / p2w.w;
|
||||||
|
vec3 p3 = p3w.xyz / p3w.w;
|
||||||
|
vec3 n = normalize(cross(p2 - p1, p3 - p1));
|
||||||
|
vec3 rp = pos - p1;
|
||||||
|
return dot(rp, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
// a plane with infinite depth
|
||||||
|
float dist_floor(vec3 p, float y) {
|
||||||
|
return p.y - y;
|
||||||
|
}
|
||||||
|
|
||||||
|
float dist_sphere(vec3 p, vec3 pos, float r) {
|
||||||
|
return distance(p, pos) - r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Model difference/subtraction (objects in `a` but not in `b`).
|
||||||
|
float sub(float a, float b) {
|
||||||
|
return max(a, -b);
|
||||||
|
}
|
||||||
|
|
||||||
////
|
////
|
||||||
//// SCENE: Objects in the scene
|
//// SCENE: Objects in the scene
|
||||||
////
|
////
|
||||||
|
|
||||||
void scene(vec3 p) {
|
void scene(vec3 p) {
|
||||||
|
/// -------------
|
||||||
|
/// SCENE 1
|
||||||
|
/// -------------
|
||||||
|
#if SCENE == 1
|
||||||
|
|
||||||
// the floor
|
// the floor
|
||||||
ud(1, dist_floor(p, -1.));
|
ud(1, dist_floor(p, -1.));
|
||||||
|
|
||||||
|
@ -1096,43 +790,73 @@ void scene(vec3 p) {
|
||||||
// the light sources
|
// the light sources
|
||||||
ud(3, dist_sphere(p, vec3(1.5, 1.2, 7.), 0.7));
|
ud(3, dist_sphere(p, vec3(1.5, 1.2, 7.), 0.7));
|
||||||
ud(3, dist_sphere(p, vec3(-1.2, 0.5, 5.0), 0.5));
|
ud(3, dist_sphere(p, vec3(-1.2, 0.5, 5.0), 0.5));
|
||||||
|
|
||||||
|
|
||||||
|
/// -------------
|
||||||
|
/// SCENE 2
|
||||||
|
/// -------------
|
||||||
|
#elif SCENE == 2
|
||||||
|
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Rotate a vector angle using a matrix.
|
||||||
|
vec3 apply_rotate(mat4 r, vec3 d) {
|
||||||
|
return normalize((r*vec4(d, 0.)).xyz);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 apply_rotate(mat4 r) {
|
||||||
|
return apply_rotate(r, vec3(0., 0., 1.));
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 apply_translate(mat4 r, vec3 p) {
|
||||||
|
vec4 p_ = r*vec4(p, 1.);
|
||||||
|
return p_.xyz/p_.w;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 apply_translate(mat4 r) {
|
||||||
|
return apply_translate(r, vec3(0.));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
mat4 translate(vec3 p, vec3 z, vec3 up) {
|
||||||
|
z = normalize(z);
|
||||||
|
up = normalize(up);
|
||||||
|
vec3 x = cross(up, z);
|
||||||
|
vec3 y = cross(z, x);
|
||||||
|
|
||||||
|
// GLSL matrices are backwards!!!
|
||||||
|
// Argh, it took me *two hours* to figure out what was going wrong!!
|
||||||
|
// What's worse is that I've been bitten by this before and *forgot*. Fuck.
|
||||||
|
/*
|
||||||
|
return mat4(
|
||||||
|
x.x, y.x, z.x, p.x,
|
||||||
|
x.y, y.y, z.y, p.y,
|
||||||
|
x.z, y.z, z.z, p.z,
|
||||||
|
0.0, 0.0, 0.0, 1.
|
||||||
|
);
|
||||||
|
*/
|
||||||
|
|
||||||
|
return mat4(
|
||||||
|
x.x, x.y, x.z, 0.,
|
||||||
|
y.x, y.y, y.z, 0.,
|
||||||
|
z.x, z.y, z.z, 0.,
|
||||||
|
p.x, p.y, p.z, 1.
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
mat4 translate(vec3 p, vec3 z) {
|
||||||
|
return translate(p, z, vec3(0., 1., 0.));
|
||||||
|
}
|
||||||
|
|
||||||
|
mat4 translate(vec3 p) {
|
||||||
|
return translate(p, vec3(0., 0., 1.));
|
||||||
}
|
}
|
||||||
|
|
||||||
//////// ================================
|
//////// ================================
|
||||||
//////// VENDOR: Vendored code
|
//////// VENDOR: Vendored code
|
||||||
//////// ================================
|
//////// ================================
|
||||||
|
|
||||||
////
|
|
||||||
//// AUTHOR: iq
|
|
||||||
////
|
|
||||||
|
|
||||||
// Randoms (https://www.shadertoy.com/view/4sfGzS))
|
|
||||||
// oldschool rand() from Visual Studio
|
|
||||||
int seed = 1;
|
|
||||||
int irand(void) { seed = seed*0x343fd+0x269ec3; return (seed>>16)&32767; }
|
|
||||||
float rand(void) { return float(irand())/32767.0; }
|
|
||||||
// hash to initialize the random sequence (copied from Hugo Elias)
|
|
||||||
int hash( int n )
|
|
||||||
{
|
|
||||||
n = (n << 13) ^ n;
|
|
||||||
return n * (n * n * 15731 + 789221) + 1376312589;
|
|
||||||
}
|
|
||||||
|
|
||||||
void seed_randoms(vec2 fragCoord) {
|
|
||||||
ivec2 q = ivec2(fragCoord);
|
|
||||||
seed = hash(q.x+hash(q.y+hash(iFrame)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// HSV (https://www.shadertoy.com/view/MsS3Wc), via nmz
|
|
||||||
vec3 hsv2rgb( in vec3 c )
|
|
||||||
{
|
|
||||||
vec3 rgb = clamp( abs(mod(c.x*6.0+vec3(0.0,4.0,2.0),6.0)-3.0)-1.0, 0.0, 1.0 );
|
|
||||||
|
|
||||||
rgb = rgb*rgb*(3.0-2.0*rgb); // cubic smoothing
|
|
||||||
|
|
||||||
return c.z * mix( vec3(1.0), rgb, c.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
////
|
////
|
||||||
//// AUTHOR: fizzer, via iq: http://www.amietia.com/lambertnotangent.html
|
//// AUTHOR: fizzer, via iq: http://www.amietia.com/lambertnotangent.html
|
||||||
////
|
////
|
||||||
|
@ -1145,18 +869,3 @@ vec3 cosine_direction(vec3 norm) {
|
||||||
u = 2.0*u - 1.0;
|
u = 2.0*u - 1.0;
|
||||||
return normalize(norm + vec3(sqrt(1.0 - u*u)*vec2(cos(a), sin(a)), u));
|
return normalize(norm + vec3(sqrt(1.0 - u*u)*vec2(cos(a), sin(a)), u));
|
||||||
}
|
}
|
||||||
|
|
||||||
////
|
|
||||||
//// AUTHOR: Sam Hocevar, via nmz (http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl)
|
|
||||||
////
|
|
||||||
|
|
||||||
vec3 rgb2hsv(vec3 c)
|
|
||||||
{
|
|
||||||
vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
|
|
||||||
vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
|
|
||||||
vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
|
|
||||||
|
|
||||||
float d = q.x - min(q.w, q.y);
|
|
||||||
float e = 1.0e-10;
|
|
||||||
return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
|
|
||||||
}
|
|
|
@ -0,0 +1,504 @@
|
||||||
|
//////// ================================
|
||||||
|
//////// 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 2
|
||||||
|
|
||||||
|
//
|
||||||
|
// Scenes:
|
||||||
|
//
|
||||||
|
// 1: A glossy rotating orange ball with two divets and blue floor.
|
||||||
|
// 2:
|
||||||
|
//
|
||||||
|
#define SCENE 1
|
||||||
|
|
||||||
|
//////// --------------------------------
|
||||||
|
//////// Scene settings
|
||||||
|
//////// --------------------------------
|
||||||
|
|
||||||
|
// 0: circle around the origin
|
||||||
|
// 1: portrait
|
||||||
|
// 2: profile
|
||||||
|
// 3: 3/4
|
||||||
|
//#define SCENE2_CAMERA 0
|
||||||
|
|
||||||
|
//////// --------------------------------
|
||||||
|
//////// User settings
|
||||||
|
//////// --------------------------------
|
||||||
|
//////// Tweak these according to your preferences and the power of your graphics card.
|
||||||
|
//////// Comment out a setting to restore it to its default value.
|
||||||
|
|
||||||
|
//// ********************************
|
||||||
|
//// Sample settings
|
||||||
|
//// ********************************
|
||||||
|
|
||||||
|
// The number of color samples taken per pixel. Increasing this has a dramatic effect on
|
||||||
|
// 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
|
||||||
|
|
||||||
|
// The maximum number of times light can reflect or scatter before it is extinguished.
|
||||||
|
//#define PATH_SEGMENTS 14
|
||||||
|
|
||||||
|
//// ********************************
|
||||||
|
//// Postprocessing settings
|
||||||
|
//// ********************************
|
||||||
|
|
||||||
|
// If a pixel color is too bright for fit in sRGB, there are two ways to handle it:
|
||||||
|
//
|
||||||
|
// 1. Clamp the pixel within the limits of sRGB, resulting in (near-)maximum
|
||||||
|
// brightness at the cost of the color's saturation. (If it's too bright, it'll
|
||||||
|
// become entirely white.)
|
||||||
|
// 2. Reduce the brightness of the color until it fits within sRGB, preserving
|
||||||
|
// the color's saturation, but losing even *more* brightness.
|
||||||
|
//
|
||||||
|
// Correction for saturation generally looks better, but isn't usually necessary
|
||||||
|
// for more than five or so samples (because the bright pixels will average out
|
||||||
|
// with the dark pixels and fall back within sRGB).
|
||||||
|
//#define SATURATION_CORRECTION 1
|
||||||
|
|
||||||
|
// The dithering looks pretty good. 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 1
|
||||||
|
|
||||||
|
// The number of available colors *per channel*.
|
||||||
|
//define DITHER_COLORS 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.
|
||||||
|
//#define DITHER_BASE 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.
|
||||||
|
//
|
||||||
|
// * Pronounced "yif". Fite me.
|
||||||
|
//#define DITHER_NEAREST 0
|
||||||
|
|
||||||
|
//// ********************************
|
||||||
|
//// Perspective settings
|
||||||
|
//// ********************************
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
// Camera position and angle. (Feel free to reference `time` here.)
|
||||||
|
//#define CAMERA_POS vec3(0.)
|
||||||
|
// (Don't worry, we call `normalize` for you.
|
||||||
|
//#define CAMERA_DIR vec3(0., 0., 1.)
|
||||||
|
|
||||||
|
///
|
||||||
|
/// TILE_PERSPECTIVE and CLAMP_PERSPECTIVE are only relevant if you zoom out
|
||||||
|
/// (e.g. an FOV < ~1.15). For more information on how and why these settings
|
||||||
|
/// behave the way they do, see their extended descriptions in the `project` function.
|
||||||
|
///
|
||||||
|
|
||||||
|
// Points on the screen >1 or <-1 show the portion of the scene *behind* you,
|
||||||
|
// mirrored so that the edges of each adjacent tile lines up (e.g. tiles above
|
||||||
|
// and below are mirrored vertically, to the left and right horizontally).
|
||||||
|
// 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
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
// 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.)
|
||||||
|
|
||||||
|
//// ********************************
|
||||||
|
//// Simulation settings
|
||||||
|
//// ********************************
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// Average the color across frames by storing them in the buffer.
|
||||||
|
// This is like supersampling, but across frames instead of within a pixel,
|
||||||
|
// which lets you render with thousands of samples without crashing.
|
||||||
|
// It's strongly advised that you enable FREEZE_TIME when this is enabled!
|
||||||
|
// This uses iFrame, so if you want to enable this, make sure you hit the
|
||||||
|
// "reset time" function or things will get screwed up.
|
||||||
|
//#define AVERAGE_FRAMES 1
|
||||||
|
|
||||||
|
// Set a time in seconds. The simulation will be frozen at this point in time every frame.
|
||||||
|
// Comment this out to allow time to pass normally.
|
||||||
|
//#define FREEZE_TIME 2.75
|
||||||
|
|
||||||
|
// Loop time over an interval of this duration, beginning at FREEZE_TIME,
|
||||||
|
// or 0, if FREEZE_TIME is not set.
|
||||||
|
//#define LOOP_TIME 0.
|
||||||
|
|
||||||
|
// Set the maximum duration of temporal antialiasing (i.e. how much time
|
||||||
|
// motion blur smears across). Note that this is a *maximum* time, and motion
|
||||||
|
// blur will never be greater than the duration of a frame. That said, when rendering
|
||||||
|
// a still image with FREEZE_TIME you probably want this set to 0., and if you're
|
||||||
|
// stuttering a lot, the large variance in frame times can make objects in the image
|
||||||
|
// 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.)
|
||||||
|
|
||||||
|
|
||||||
|
//////// --------------------------------
|
||||||
|
//////// Internal settings
|
||||||
|
//////// --------------------------------
|
||||||
|
//////// If you're just viewing the shader, you shouldn't usually need to tweak these.
|
||||||
|
|
||||||
|
// The minimum distance between two points before they are considered the same point.
|
||||||
|
// Setting lower values increases the sharpness of the image at the cost of performance
|
||||||
|
// and rounding errors at objects very far from 0.
|
||||||
|
//
|
||||||
|
// Ray marching halves the distance to the surface of an object each iteration, but the
|
||||||
|
// end goal of ray marching is to pass slightly *inside* the object. Setting a minimum
|
||||||
|
// distance prevents zeno's paradox. This also serves as a optimization
|
||||||
|
// because the number of steps increases logarithmically as you decrease the minimum distance.
|
||||||
|
//
|
||||||
|
// Chosen to be 2^(-9), or about ~2mm, because that's the largest you can set it before
|
||||||
|
// the quality of the image is significantly effected. You can set it as low as about
|
||||||
|
// 2^(-19) before things begin to break. It's good to experiment with both high and low
|
||||||
|
// values to help find bugs in the numerical precision of the light simulation.
|
||||||
|
// If you have precision bugs, the simulation ends up getting affected pretty dramatically
|
||||||
|
// by changes to MIN_DIST, whereas a numerically stable simulation is not affected much at all.
|
||||||
|
//
|
||||||
|
// 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)
|
||||||
|
|
||||||
|
// 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.)
|
||||||
|
|
||||||
|
// 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`).
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
//////// --------------------------------
|
||||||
|
//////// Default settings & presets
|
||||||
|
//////// --------------------------------
|
||||||
|
//////// So you can restore a setting to its default value by commenting it out.
|
||||||
|
|
||||||
|
#ifndef PRESET
|
||||||
|
#define PRESET 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SCENE
|
||||||
|
#define SCENE 2
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//// ********************************
|
||||||
|
//// Scene 2
|
||||||
|
//// ********************************
|
||||||
|
#if SCENE == 2
|
||||||
|
#ifndef SCENE2_CAMERA
|
||||||
|
#define SCENE2_CAMERA 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef FOV
|
||||||
|
#define FOV 1.2
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if SCENE2_CAMERA == 1
|
||||||
|
|
||||||
|
#ifndef CAMERA_POS
|
||||||
|
#define CAMERA_POS vec3(0., -0.1, -0.5)
|
||||||
|
#endif
|
||||||
|
#ifndef CAMERA_DIR
|
||||||
|
#define CAMERA_DIR vec3(0., 0., 1.)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#elif SCENE2_CAMERA == 2
|
||||||
|
|
||||||
|
#ifndef CAMERA_POS
|
||||||
|
#define CAMERA_POS vec3(0.5, -0.1, 0.0)
|
||||||
|
#endif
|
||||||
|
#ifndef CAMERA_DIR
|
||||||
|
#define CAMERA_DIR vec3(-1.0, 0.0, 0.0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#elif SCENE2_CAMERA == 3
|
||||||
|
|
||||||
|
#ifndef CAMERA_POS
|
||||||
|
#define CAMERA_POS vec3(-sqrt(0.5/4.), -0.1, -sqrt(0.5/4.))
|
||||||
|
#endif
|
||||||
|
#ifndef CAMERA_DIR
|
||||||
|
#define CAMERA_DIR vec3(sqrt(0.5), 0., sqrt(0.5))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#ifndef CAMERA_POS
|
||||||
|
#define CAMERA_POS vec3(sin(-time)*0.5, -0.1, cos(-time)*0.5)
|
||||||
|
#endif
|
||||||
|
#ifndef CAMERA_DIR
|
||||||
|
#define CAMERA_DIR vec3(-sin(-time), 0., -cos(-time))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#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 10
|
||||||
|
#endif
|
||||||
|
#ifndef MAX_STEPS
|
||||||
|
#define MAX_STEPS 300
|
||||||
|
#endif
|
||||||
|
#ifndef MAX_DIST
|
||||||
|
#define MAX_DIST 50.
|
||||||
|
#endif
|
||||||
|
#ifndef MIN_DIST
|
||||||
|
#define MIN_DIST (0.001953125/128.)
|
||||||
|
#endif
|
||||||
|
#ifndef MAX_TAA_DIFF
|
||||||
|
#define MAX_TAA_DIFF 0.
|
||||||
|
#endif
|
||||||
|
#ifndef AVERAGE_FRAMES
|
||||||
|
#define AVERAGE_FRAMES 1
|
||||||
|
#endif
|
||||||
|
#ifndef FREEZE_TIME
|
||||||
|
#if SCENE == 1
|
||||||
|
#define FREEZE_TIME 2.75
|
||||||
|
#elif SCENE == 2
|
||||||
|
#define FREEZE_TIME 2.3
|
||||||
|
#else
|
||||||
|
#define FREEZE_TIME 0.
|
||||||
|
#endif
|
||||||
|
#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 (defaults)
|
||||||
|
//// ********************************
|
||||||
|
#ifndef SAMPLES
|
||||||
|
#define SAMPLES 1
|
||||||
|
#endif
|
||||||
|
#ifndef PATH_SEGMENTS
|
||||||
|
#define PATH_SEGMENTS 10
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SATURATION_CORRECTION
|
||||||
|
#define SATURATION_CORRECTION 1
|
||||||
|
#endif
|
||||||
|
#ifndef ENABLE_DITHER
|
||||||
|
#define ENABLE_DITHER 1
|
||||||
|
#endif
|
||||||
|
#ifndef DITHER_BASE
|
||||||
|
#define DITHER_BASE 16
|
||||||
|
#endif
|
||||||
|
#ifndef DITHER_COLORS
|
||||||
|
#define DITHER_COLORS (1<<8)
|
||||||
|
#endif
|
||||||
|
#ifndef DITHER_NEAREST
|
||||||
|
#define DITHER_NEAREST 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#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
|
||||||
|
#ifndef TILE_PERSPECTIVE
|
||||||
|
#define TILE_PERSPECTIVE 0
|
||||||
|
#endif
|
||||||
|
#ifndef CLAMP_PERSPECTIVE
|
||||||
|
#define CLAMP_PERSPECTIVE 0
|
||||||
|
#endif
|
||||||
|
#ifndef IMAGE_OFFSET
|
||||||
|
#define IMAGE_OFFSET vec2(0., 0.)
|
||||||
|
#endif
|
||||||
|
#ifndef MAX_STEPS
|
||||||
|
#define MAX_STEPS 200
|
||||||
|
#endif
|
||||||
|
#ifndef MAX_DIST
|
||||||
|
#define MAX_DIST 20.
|
||||||
|
#endif
|
||||||
|
#ifndef AVERAGE_FRAMES
|
||||||
|
#define AVERAGE_FRAMES 0
|
||||||
|
#endif
|
||||||
|
// FREEZE_TIME, LOOP_TIME, and MAX_TAA_DIFF are *undefined* by default.
|
||||||
|
|
||||||
|
#ifndef MIN_DIST
|
||||||
|
#define MIN_DIST (0.001953125/8.)
|
||||||
|
#endif
|
||||||
|
#ifndef NORMAL_DELTA
|
||||||
|
#define NORMAL_DELTA (MIN_DIST/4.)
|
||||||
|
#endif
|
||||||
|
#ifndef IMPRECISION_FACTOR
|
||||||
|
#define IMPRECISION_FACTOR 1.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//////// ================================
|
||||||
|
//////// DOCS: Declarations & documentation
|
||||||
|
//////// ================================
|
||||||
|
|
||||||
|
/// Convert a color from linear RGB to the sRGB color space.
|
||||||
|
vec3 linear2srgb(vec3 color);
|
||||||
|
|
||||||
|
/// Convert a color from RGB (Red/Green/Blue) to HSV (Hue/Saturation/Value).
|
||||||
|
vec3 rgb2hsv(vec3 rgb);
|
||||||
|
|
||||||
|
/// Convert a color from HSV (Hue/Saturation/Value) to RGB (Red/Green/Blue).
|
||||||
|
vec3 hsv2rgb(vec3 hsv);
|
||||||
|
|
||||||
|
/// Return a random number between 0 and 1 (with uniform distribution);
|
||||||
|
float rand();
|
||||||
|
|
||||||
|
/// Use the fragment coordinate and current frame to seed the random number generator.
|
||||||
|
void seed_randoms(vec3 seed);
|
||||||
|
|
||||||
|
// Convenience definitions
|
||||||
|
#define INF (1./0.)
|
||||||
|
// NOTE: I used to use `sqrt(-1)`, but apparently that doesn't evaluate to NaN????
|
||||||
|
// This makes me wonder if NaN isn't portable due to constant folding or something.
|
||||||
|
#define NAN (0./0.)
|
||||||
|
|
||||||
|
//////// ================================
|
||||||
|
//////// VENDOR: Vendored code
|
||||||
|
//////// ================================
|
||||||
|
|
||||||
|
////
|
||||||
|
//// AUTHOR: unknown
|
||||||
|
////
|
||||||
|
|
||||||
|
vec3 linear2srgb(vec3 linear_rgb) {
|
||||||
|
// I believe the first version is technically more accurate,
|
||||||
|
// but the difference is usually negligable in practice.
|
||||||
|
#if 1
|
||||||
|
// copied from somewhere on the internet
|
||||||
|
bvec3 cutoff = lessThan(linear_rgb, vec3(0.0031308));
|
||||||
|
vec3 higher = vec3(1.055)*pow(linear_rgb, vec3(1.0/2.4)) - vec3(0.055);
|
||||||
|
vec3 lower = linear_rgb * vec3(12.92);
|
||||||
|
|
||||||
|
return mix(higher, lower, cutoff);
|
||||||
|
// end copied from somewhere on the internet
|
||||||
|
#else
|
||||||
|
return pow(linear_rgb, vec3(1./2.2));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
////
|
||||||
|
//// AUTHOR: Sam Hocevar (http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl)
|
||||||
|
////
|
||||||
|
|
||||||
|
vec3 rgb2hsv(vec3 c) {
|
||||||
|
vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
|
||||||
|
vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
|
||||||
|
vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
|
||||||
|
|
||||||
|
float d = q.x - min(q.w, q.y);
|
||||||
|
float e = 1.0e-10;
|
||||||
|
return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 hsv2rgb(vec3 c) {
|
||||||
|
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
|
||||||
|
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
|
||||||
|
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
////
|
||||||
|
//// AUTHOR: iq
|
||||||
|
////
|
||||||
|
|
||||||
|
// Randoms (https://www.shadertoy.com/view/4sfGzS))
|
||||||
|
// oldschool rand() from Visual Studio
|
||||||
|
int _seed = 1;
|
||||||
|
int irand(void) { _seed = _seed*0x343fd+0x269ec3; return (_seed>>16)&32767; }
|
||||||
|
float rand(void) { return float(irand())/32767.0; }
|
||||||
|
// hash to initialize the random sequence (copied from Hugo Elias)
|
||||||
|
int hash( int n )
|
||||||
|
{
|
||||||
|
n = (n << 13) ^ n;
|
||||||
|
return n * (n * n * 15731 + 789221) + 1376312589;
|
||||||
|
}
|
||||||
|
|
||||||
|
void seed_randoms(vec3 s) {
|
||||||
|
ivec3 q = ivec3(s);
|
||||||
|
_seed = hash(q.x+hash(q.y+hash(q.z)));
|
||||||
|
}
|
|
@ -0,0 +1,167 @@
|
||||||
|
//////// ================================
|
||||||
|
//////// 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;
|
||||||
|
}
|
Loading…
Reference in New Issue