code snippets, stubs and sketches
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

201 lines
5.3 KiB

// ray marching code from
// http://jamie-wong.com/2016/07/15/ray-marching-signed-distance-functions/
// https://www.shadertoy.com/view/Xtd3z7
// SDF shapes from
// https://iquilezles.org/www/articles/distfunctions/distfunctions.htm
#ifdef GL_ES
precision mediump float;
#endif
#define PI 3.1415926535897932384626433832795
#define TWO_PI 6.2831853071795864769252867665590
uniform vec2 u_resolution;
uniform float u_time;
uniform sampler2D u_tex0;
// -------------- RAYMARCHING BOILERPLATE ---------------------------------------
const int MAX_MARCHING_STEPS = 255;
const float MIN_DIST = 0.0;
const float MAX_DIST = 100.0;
const float EPSILON = 0.0001;
float sphereSDF(vec3 p) {
return length(p) - 1.0;
}
float sceneSDF(vec3 samplePoint);
float shortestDistanceToSurface(vec3 eye, vec3 marchingDirection, float start, float end) {
float depth = start;
for (int i = 0; i < MAX_MARCHING_STEPS; i++) {
float dist = sceneSDF(eye + depth * marchingDirection);
if (dist < EPSILON) {
return depth;
}
depth += dist;
if (depth >= end) {
return end;
}
}
return end;
}
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));
}
vec3 estimateNormal(vec3 p) {
return normalize(vec3(
sceneSDF(vec3(p.x + EPSILON, p.y, p.z)) - sceneSDF(vec3(p.x - EPSILON, p.y, p.z)),
sceneSDF(vec3(p.x, p.y + EPSILON, p.z)) - sceneSDF(vec3(p.x, p.y - EPSILON, p.z)),
sceneSDF(vec3(p.x, p.y, p.z + EPSILON)) - sceneSDF(vec3(p.x, p.y, p.z - EPSILON))
));
}
float phongContribForLight(float k_d, vec3 p, vec3 eye, vec3 lightPos) {
vec3 N = estimateNormal(p);
vec3 L = normalize(lightPos - p);
vec3 V = normalize(eye - p);
vec3 R = normalize(reflect(-L, N));
float dotLN = dot(L, N);
float dotRV = dot(R, V);
// Light not visible from this point on the surface
if (dotLN < 0.0) {
return 0.0;
}
// Light reflection in opposite direction as viewer,
// apply only diffuse component
if (dotRV < 0.0) {
return (k_d * dotLN);
}
return (k_d * dotLN );
}
mat4 viewMatrix(vec3 eye, vec3 center, vec3 up) {
// Based on gluLookAt man page
vec3 f = normalize(center - eye);
vec3 s = normalize(cross(f, up));
vec3 u = cross(s, f);
return mat4(
vec4(s, 0.0),
vec4(u, 0.0),
vec4(-f, 0.0),
vec4(0.0, 0.0, 0.0, 1)
);
}
// ------------------- LFOs ------------------------------------
float lfo_ramp( in float speed ){ return fract(u_time*speed); }
float lfo_tri( in float speed ){ return abs( (fract(u_time*speed) * 2.0) - 1.0 ); }
// ------------------- 3D SDFs ---------------------------------
float sdBox( vec3 p, vec3 b )
{
vec3 q = abs(p) - b;
return length(max(q,0.0)) + min(max(q.x,max(q.y,q.z)),0.0);
}
float sdOctahedron( vec3 p, float s)
{
p = abs(p);
return (p.x+p.y+p.z-s)*0.57735027;
}
mat4 rotateY(float theta) {
float c = cos(theta);
float s = sin(theta);
return mat4(
vec4(c, 0.0, s, 0.0),
vec4(0.0, 1.0, 0.0, 0.0),
vec4(-s, 0.0, c, 0.0),
vec4(0.0, 0.0, 0.0, 1.0)
);
}
// ------------------- SHADER ----------------------------------
float sceneSDF(vec3 samplePoint) {
float theta_step = TWO_PI / 64.0;
float sdf = 0.0f;
vec3 sz = vec3( 0.2, 1.8, 0.03);
float stepping = floor(-u_time*2.0) * theta_step;
vec4 rotated = rotateY(stepping) * vec4(samplePoint.xyz, 1.0);
float o0 = sdOctahedron( rotated.xyz, 1.1 );
stepping = floor(8.0 + u_time*8.0) * theta_step;
rotated = rotateY(stepping) * vec4(samplePoint.xyz, 1.0);
float offset = 2.0;
float displace = 8.0;
vec3 p0 = rotated.xyz + vec3(0.0, 0.0, offset);
float b1 = sdBox( p0, sz );
float d1 = sin(displace*p0.x*1.5) * sin(displace*p0.y) * sin(displace*p0.z*0.5);
b1 += d1 * lfo_ramp( 1.0/8.0 )*0.7;
float b2 = sdBox( rotated.xyz + vec3(0.0, 0.0, -offset), sz );
sdf = min( b1, b2 );
sdf = min( sdf, o0 );
return sdf;
}
void main(){
// ------ dither patterns ------
float dark_pattern = step( fract( gl_FragCoord.x/4.0 ), 0.25);
dark_pattern *= step( fract( gl_FragCoord.y/4.0 ), 0.25);
float mid_pattern = step( fract( gl_FragCoord.x/4.0 ), 0.25);
float light_pattern = 1.0;
// ------ raymarching parameters ------
float distance = 65.0;
float K_a = 0.05; // ambient gradient
float K_d = 0.9; // light gradient
vec3 light_pos = vec3(2.0, 4.0, 4.0 );
float clok = u_time * 0.2;
float theta = lfo_ramp( 0.03 ) * TWO_PI;
vec3 camera = vec3( 7.0, 3.0, 7.0);
vec3 look_at = vec3( 0, 0, 0 );
// ------ more raymarching boilerplate ------
vec2 st = gl_FragCoord.xy/u_resolution;
vec3 viewDir = rayDirection(distance, u_resolution.xy, gl_FragCoord.xy);
mat4 viewToWorld = viewMatrix(camera, look_at, vec3(0.0, 1.0, 0.0));
vec3 worldDir = (viewToWorld * vec4(viewDir, 0.0)).xyz;
float dist = shortestDistanceToSurface(camera, worldDir, MIN_DIST, MAX_DIST);
if (dist > MAX_DIST - EPSILON) {
// Didn't hit anything
gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
return;
}
// The closest point on the surface to the eyepoint along the view ray
vec3 p = camera + dist * worldDir;
float c = K_a;
c += phongContribForLight(K_d, p, camera, light_pos );
float shade = step( 0.2, c );
float min = 1.0 - shade;
float light = step( 0.4, c );
shade -= light;
float a = min * dark_pattern + shade * mid_pattern + light * light_pattern;
gl_FragColor = vec4(vec3(a), 1.0);
// pre-dither result
//gl_FragColor = vec4(vec3(c), 1.0);
}