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.
 
 
 
 
 
 

325 lines
9.2 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;
// hash based 3d value noise
// function taken from https://www.shadertoy.com/view/XslGRr
// Created by inigo quilez - iq/2013
// License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
float vnhash( float n )
{
return fract(sin(n)*43758.5453);
}
float vnoise( vec3 x )
{
// The noise function returns a value in the range -1.0f -> 1.0f
vec3 p = floor(x);
vec3 f = fract(x);
f = f*f*(3.0-2.0*f);
float n = p.x + p.y*57.0 + 113.0*p.z;
return mix(mix(mix( vnhash(n+0.0), vnhash(n+1.0),f.x),
mix( vnhash(n+57.0), vnhash(n+58.0),f.x),f.y),
mix(mix( vnhash(n+113.0), vnhash(n+114.0),f.x),
mix( vnhash(n+170.0), vnhash(n+171.0),f.x),f.y),f.z);
}
//
// Description : GLSL 2D simplex noise function
// Author : Ian McEwan, Ashima Arts
// Maintainer : ijm
// Lastmod : 20110822 (ijm)
// License :
// Copyright (C) 2011 Ashima Arts. All rights reserved.
// Distributed under the MIT License. See LICENSE file.
// https://github.com/ashima/webgl-noise
//
// Some useful functions
vec3 mod289(vec3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
vec2 mod289(vec2 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
vec3 permute(vec3 x) { return mod289(((x*34.0)+1.0)*x); }
float noise(vec2 v) {
// Precompute values for skewed triangular grid
const vec4 C = vec4(0.211324865405187,
// (3.0-sqrt(3.0))/6.0
0.366025403784439,
// 0.5*(sqrt(3.0)-1.0)
-0.577350269189626,
// -1.0 + 2.0 * C.x
0.024390243902439);
// 1.0 / 41.0
// First corner (x0)
vec2 i = floor(v + dot(v, C.yy));
vec2 x0 = v - i + dot(i, C.xx);
// Other two corners (x1, x2)
vec2 i1 = vec2(0.0);
i1 = (x0.x > x0.y)? vec2(1.0, 0.0):vec2(0.0, 1.0);
vec2 x1 = x0.xy + C.xx - i1;
vec2 x2 = x0.xy + C.zz;
// Do some permutations to avoid
// truncation effects in permutation
i = mod289(i);
vec3 p = permute(
permute( i.y + vec3(0.0, i1.y, 1.0))
+ i.x + vec3(0.0, i1.x, 1.0 ));
vec3 m = max(0.5 - vec3(
dot(x0,x0),
dot(x1,x1),
dot(x2,x2)
), 0.0);
m = m*m ;
m = m*m ;
// Gradients:
// 41 pts uniformly over a line, mapped onto a diamond
// The ring size 17*17 = 289 is close to a multiple
// of 41 (41*7 = 287)
vec3 x = 2.0 * fract(p * C.www) - 1.0;
vec3 h = abs(x) - 0.5;
vec3 ox = floor(x + 0.5);
vec3 a0 = x - ox;
// Normalise gradients implicitly by scaling m
// Approximation of: m *= inversesqrt(a0*a0 + h*h);
m *= 1.79284291400159 - 0.85373472095314 * (a0*a0+h*h);
// Compute final noise value at P
vec3 g = vec3(0.0);
g.x = a0.x * x0.x + h.x * x0.y;
g.yz = a0.yz * vec2(x1.x,x2.x) + h.yz * vec2(x1.y,x2.y);
return 130.0 * dot(m, g);
}
// code ported from alex-charlton.com/posts/Dithering_on_the_GPU/
// adapted for running on GLSL ES 2 ----
const mat4 dither_matrix = mat4(
vec4( 0, 8, 2, 10),
vec4( 12, 4, 14, 6),
vec4( 3, 11, 1, 9),
vec4( 15, 7, 13, 5)
);
float dither_4x4( float x, vec2 coord ){
int ix = int(mod(coord.x, 4.0));
int iy = int(mod(coord.y, 4.0));
float d = dither_matrix[iy][ix] / 16.0;
float closest = step( x, 0.5 );
float distance = abs(closest - x);
float branch = step( distance, d );
return 2.0*branch*closest + 1.0 - closest - branch;
}
// -------------------------------------
// -------------- 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)
);
}
// ------------------- FUNCTIONS -------------------------------
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 ); }
float lfo_sin( in float speed ){ return (sin(u_time*speed*TWO_PI)*0.5 + 0.5); }
// ------------------- 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 sdEllipsoid( vec3 p, vec3 r ){
float k0 = length(p/r);
float k1 = length(p/(r*r));
return k0*(k0-1.0)/k1;
}
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 ----------------------------------
void main(){
// ------ raymarching parameters ------
float distance = 70.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, 0.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 );
gl_FragColor = vec4(vec3(c), 1.0);
// dithering
c *= 1.4;
float a = dither_4x4( c, gl_FragCoord.xy );
gl_FragColor = vec4(vec3(a), 1.0);
}
#define FPS 8.0
// 8 sec for a complete rotation
float sceneSDF(vec3 samplePoint) {
float theta_step = TWO_PI / 64.0;
float sdf = 0.0f;
float stepping = floor(-u_time*FPS) * theta_step;
vec4 rotated = rotateY(stepping) * vec4(samplePoint.xyz, 1.0);
float o0 = sdBox( rotated.xyz, vec3( 0.05, 1.8, 0.5) );
float displace = abs( fract( u_time*0.125 + 0.5 )*2.0 -1.0) * 0.25;
float oadd = o0 + noise( vec2(rotated.z*2.0, rotated.y*3.5 + stepping ) ) * displace;
sdf = min( o0, oadd );
float offset = 1.8;
stepping = floor(8.0 + u_time*FPS) * theta_step;
for ( int i=0; i<8; ++i ){
float r = stepping + float(i) * TWO_PI*0.125;
float r2 = stepping + float(i) * TWO_PI*0.5;
rotated = rotateY( r ) * vec4(samplePoint.xyz, 1.0);
vec3 p0 = rotated.xyz + vec3(0.0, 0.0, offset);
rotated.y += sin( r*2.0 + r2 ) * 0.2;
float b = sdEllipsoid( rotated.xyz + vec3(0.0, 0.0, -offset), vec3(0.2, 0.08, 0.08 ) );
sdf = min( b, sdf );
}
return sdf;
}