GLSL snippets

From vegard.wiki
Revision as of 11:02, 30 December 2021 by Vegard (talk | contribs) (→‎Ray marching: fix rayDirection())
Jump to navigation Jump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.

Signed distance functions

Antialiasing

const float scale = 100.;

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 uv = scale * (fragCoord - .5 * iResolution.xy) / iResolution.y;
    float d = sd...(uv);
    vec3 col = vec3(1) * smoothstep(-0., 1.5 * scale / iResolution.y, d);
    fragColor = vec4(col, 1.);
}

The point here is to convert the length returned by the distance function into a number of pixels, and then we can smooth the outline over the precise number of pixels that we want (here 1.5).

Ray marching

// http://jamie-wong.com/2016/07/15/ray-marching-signed-distance-functions/
vec3 rayDirection(float fieldOfView, vec2 size, vec2 fragCoord) {
    vec2 xy = fragCoord - size / 2.;
    float z = size.y / tan(radians(fieldOfView) / 2.) / 2.;
    return normalize(vec3(xy, -z));
}

Note: The original rayDirection() is missing the divide by 2 when calculating z. This causes the field of view parameter to be incorrect.

// http://jamie-wong.com/2016/07/15/ray-marching-signed-distance-functions/
float castRay(const int scene, vec3 eye, vec3 dir,
    float start, float end, float epsilon, int max_marching_steps,
    out int material)
{
    float depth = start;
    for (int i = 0; i < max_marching_steps; i++) {
        float d = sceneSDF(scene, eye + depth * dir, material);
        if (d < epsilon)
            return depth;

        depth += d;
        if (depth >= end)
            break;
    }

    return end;
}
// http://iquilezles.org/www/articles/normalsSDF/normalsSDF.htm
vec3 estimateNormal( const int scene, const float epsilon, in vec3 p )
{
    int m;
    
    const vec2 k = vec2(1,-1);
    return normalize( k.xyy*sceneSDF( scene, p + k.xyy*epsilon, m ) + 
                      k.yyx*sceneSDF( scene, p + k.yyx*epsilon, m ) + 
                      k.yxy*sceneSDF( scene, p + k.yxy*epsilon, m ) + 
                      k.xxx*sceneSDF( scene, p + k.xxx*epsilon, m ) );
}

Matrix transformations

// https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glRotate.xml
mat4 rotate(float a, vec3 v)
{
    float c = cos(a);
    vec3 ci = (1. - c) * v;
    vec3 s = sin(a) * v;

    return mat4(
        ci.x * v.x + c, ci.x * v.y + s.z, ci.x * v.z - s.y, 0,
        ci.y * v.x - s.z, ci.y * v.y + c, ci.y * v.z + s.x, 0,
        ci.z * v.x + s.y, ci.z * v.y - s.x, ci.z * v.z + c, 0,
        0, 0, 0, 1
    );
}
// https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glTranslate.xml
mat4 translate(vec3 v)
{
    return mat4(
        1, 0, 0, 0,
        0, 1, 0, 0,
        0, 0, 1, 0,
        v.x, v.y, v.z, 1
    );
}

Noise

// https://stackoverflow.com/questions/12964279/whats-the-origin-of-this-glsl-rand-one-liner
float rand(vec2 co)
{
    return fract(sin(dot(co.xy, vec2(12.9898, 78.233))) * 43758.5453);
}

Colours

HSV

Plain HSV

hsv2rgb()
// https://github.com/hughsk/glsl-hsv2rgb/blob/master/index.glsl
vec3 hsv2rgb(vec3 c) {
    vec4 K = vec4(3. / 3., 2. / 3., 1. / 3., 3.);
    vec3 p = abs(fract(c.xxx + K.xyz) * 6. - K.www);
    return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}

Smooth variant

hsv2rgb2()
// https://www.shadertoy.com/view/wlsSRB
vec3 hsv2rgb2(vec3 c, float k) {
    return smoothstep(0. + k, 1. - k,
                      .5 + .5 * cos((vec3(c.x) + vec3(3., 2., 1.) / 3.) * radians(360.)));
}

Gamma

Main article: Gamma
const float gamma = 2.2;

// convert from sRGB to RGB
vec3 col = pow(col, vec3(gamma));

// blending, etc.

// gamma correction (RGB to sRGB)
col = pow(col, vec3(1. / gamma));

Dithering

/* https://en.wikipedia.org/wiki/Ordered_dithering */
const float bayer_matrix[64] = float[64](
     -0.500000,  0.250000, -0.312500,  0.437500, -0.453125,  0.296875, -0.265625,  0.484375,
      0.000000, -0.250000,  0.187500, -0.062500,  0.046875, -0.203125,  0.234375, -0.015625,
     -0.375000,  0.375000, -0.437500,  0.312500, -0.328125,  0.421875, -0.390625,  0.359375,
      0.125000, -0.125000,  0.062500, -0.187500,  0.171875, -0.078125,  0.109375, -0.140625,
     -0.468750,  0.281250, -0.281250,  0.468750, -0.484375,  0.265625, -0.296875,  0.453125,
      0.031250, -0.218750,  0.218750, -0.031250,  0.015625, -0.234375,  0.203125, -0.046875,
     -0.343750,  0.406250, -0.406250,  0.343750, -0.359375,  0.390625, -0.421875,  0.328125,
      0.156250, -0.093750,  0.093750, -0.156250,  0.140625, -0.109375,  0.078125, -0.171875
);

float dither(vec2 uv, float levels, float sharpness, float intensity)
{
    int x = int(floor(uv.x)) & 7;
    int y = int(floor(uv.y)) & 7;
    float threshold = bayer_matrix[8 * y + x];
#if 0 // full dither
    return round(levels * intensity + threshold) / levels;
#else // respect sharpness
    float major = floor(levels * intensity);
    float minor = float(fract(levels * intensity) > .5 + sharpness * threshold);
    return (major + minor) / levels;
#endif
}

Post-processing effects

Volumetric light scattering (God rays)

Example of volumetric light scattering post-processing effect (with dithering).
// https://developer.nvidia.com/gpugems/gpugems3/part-ii-light-and-shadows/chapter-13-volumetric-light-scattering-post-process
vec3 godrays(in vec2 ScreenLightPos, in vec2 fragCoord, out vec4 fragColor)
{
    const int NUM_SAMPLES = 16;
    const float Density = .7;
    const float Weight = .3;
    const float Decay = .88;
    const float Exposure = .4;

    vec2 texCoord = fragCoord / iResolution.xy;
    vec2 deltaTexCoord = (texCoord - ScreenLightPos.xy);
    deltaTexCoord *= 1.0f / float(NUM_SAMPLES) * Density;

    vec3 color = texture(iChannel0, texCoord).rgb;
    float illuminationDecay = 1.0f;

    for (int i = 0; i < NUM_SAMPLES; i++) {
        texCoord -= deltaTexCoord;

        float dither = rand(fragCoord + vec2(200. * float(i), 23. * float(i)));
        vec3 sample_ = texture(iChannel0, texCoord + deltaTexCoord * dither).rgb;
        sample_ *= illuminationDecay * Weight;
        color += sample_;
        illuminationDecay *= Decay;
    }

    return color * Exposure;
}

Dithering suggested by Jessica Mak.