//////// ================================ //////// 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))); }