Difference between revisions of "GLSL snippets"
Jump to navigation
Jump to search
(→Ray marching: fix rayDirection()) |
|||
(16 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
− | + | __TOC__ | |
− | === | + | == Signed distance functions == |
− | < | + | |
+ | === Antialiasing === | ||
+ | |||
+ | <source lang="GLSL"> | ||
+ | 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.); | ||
+ | } | ||
+ | </source> | ||
+ | |||
+ | 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 == | ||
+ | |||
+ | <source lang="GLSL"> | ||
+ | // 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)); | ||
+ | } | ||
+ | </source> | ||
+ | |||
+ | Note: The original <tt>rayDirection()</tt> is missing the divide by 2 when calculating z. This causes the field of view parameter to be incorrect. | ||
+ | |||
+ | <source lang="GLSL"> | ||
+ | // 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; | ||
+ | } | ||
+ | </source> | ||
+ | |||
+ | <source lang="GLSL"> | ||
+ | // 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 ) ); | ||
+ | } | ||
+ | </source> | ||
+ | |||
+ | == Matrix transformations == | ||
+ | |||
+ | <source lang="GLSL"> | ||
// https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glRotate.xml | // https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glRotate.xml | ||
mat4 rotate(float a, vec3 v) | mat4 rotate(float a, vec3 v) | ||
Line 17: | Line 84: | ||
); | ); | ||
} | } | ||
− | </ | + | </source> |
− | = | + | <source lang="GLSL"> |
− | |||
// https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glTranslate.xml | // https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glTranslate.xml | ||
mat4 translate(vec3 v) | mat4 translate(vec3 v) | ||
Line 31: | Line 97: | ||
); | ); | ||
} | } | ||
− | </ | + | </source> |
== Noise == | == Noise == | ||
− | = | + | <source lang="GLSL"> |
− | |||
− | |||
// https://stackoverflow.com/questions/12964279/whats-the-origin-of-this-glsl-rand-one-liner | // https://stackoverflow.com/questions/12964279/whats-the-origin-of-this-glsl-rand-one-liner | ||
float rand(vec2 co) | float rand(vec2 co) | ||
Line 43: | Line 107: | ||
return fract(sin(dot(co.xy, vec2(12.9898, 78.233))) * 43758.5453); | return fract(sin(dot(co.xy, vec2(12.9898, 78.233))) * 43758.5453); | ||
} | } | ||
− | </ | + | </source> |
+ | |||
+ | == Colours == | ||
+ | |||
+ | === HSV === | ||
+ | |||
+ | ==== Plain HSV ==== | ||
+ | |||
+ | [[File:hsv2rgb.png|420px|thumb|<tt>hsv2rgb()</tt>]] | ||
+ | |||
+ | <source lang="GLSL"> | ||
+ | // 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); | ||
+ | } | ||
+ | </source> | ||
+ | |||
+ | ==== Smooth variant ==== | ||
+ | |||
+ | [[File:hsv2rgb2.png|420px|thumb|<tt>hsv2rgb2()</tt>]] | ||
+ | |||
+ | <source lang="GLSL"> | ||
+ | // 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.))); | ||
+ | } | ||
+ | </source> | ||
+ | |||
+ | === Gamma === | ||
+ | |||
+ | {{Main|Gamma}} | ||
+ | |||
+ | <source lang="GLSL"> | ||
+ | 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)); | ||
+ | </source> | ||
+ | |||
+ | == Dithering == | ||
+ | |||
+ | <source lang="GLSL"> | ||
+ | /* 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 | ||
+ | } | ||
+ | </source> | ||
+ | |||
+ | == Post-processing effects == | ||
+ | |||
+ | === Volumetric light scattering (God rays) === | ||
+ | |||
+ | [[File:godrays.png|420px|thumb|Example of volumetric light scattering post-processing effect (with dithering).]] | ||
+ | |||
+ | <source lang="GLSL"> | ||
+ | // 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; | ||
+ | } | ||
+ | </source> | ||
+ | |||
+ | Dithering suggested by Jessica Mak. | ||
+ | |||
+ | [[Category:Graphics programming]] |
Revision as of 11:02, 30 December 2021
Contents
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
// 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
// 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)
// 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.