GLSL snippets

From vegard.wiki
Jump to navigation Jump to search

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.0;
    float z = size.y / tan(radians(fieldOfView) / 2.0);
    return normalize(vec3(xy, -z));
}
// 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.