// 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);
|
|
}
|
|
|
|
|
|
// -------------- 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); }
|
|
|
|
float rand(vec2 st, float t){
|
|
return fract(sin(dot(st.xy + fract(t*0.0013) ,vec2(12.9898,78.233))) * 43758.5453);
|
|
}
|
|
|
|
float rand( in float i, in float t ){
|
|
return fract(sin(dot( vec2(i, t), vec2(12.9898,78.233))) * 43758.5453);
|
|
}
|
|
|
|
mat2 rotate2d( in float _angle ){
|
|
return mat2(cos(_angle),-sin(_angle), sin(_angle),cos(_angle));
|
|
}
|
|
|
|
vec2 rotated( vec2 _st, in float _angle ){
|
|
_st -= 0.5;
|
|
_st *= rotate2d( _angle );
|
|
_st += 0.5;
|
|
return _st;
|
|
}
|
|
|
|
// ------------------- 3D SDFs ---------------------------------
|
|
float sdPyramid( vec3 p, float h){
|
|
float m2 = h*h + 0.25;
|
|
|
|
p.xz = abs(p.xz);
|
|
p.xz = (p.z>p.x) ? p.zx : p.xz;
|
|
p.xz -= 0.5;
|
|
|
|
vec3 q = vec3( p.z, h*p.y - 0.5*p.x, h*p.x + 0.5*p.y);
|
|
|
|
float s = max(-q.x,0.0);
|
|
float t = clamp( (q.y-0.5*p.z)/(m2+0.25), 0.0, 1.0 );
|
|
|
|
float a = m2*(q.x+s)*(q.x+s) + q.y*q.y;
|
|
float b = m2*(q.x+0.5*t)*(q.x+0.5*t) + (q.y-m2*t)*(q.y-m2*t);
|
|
|
|
float d2 = min(q.y,-q.x*m2-q.y*0.5) > 0.0 ? 0.0 : min(a,b);
|
|
|
|
return sqrt( (d2+q.z*q.z)/m2 ) * sign(max(q.z,-p.y));
|
|
}
|
|
|
|
mat4 rotateX(float theta) {
|
|
float c = cos(theta);
|
|
float s = sin(theta);
|
|
|
|
return mat4(
|
|
vec4(1.0, 0.0, 0.0, 0.0),
|
|
vec4(0.0, c, -s, 0.0),
|
|
vec4(0.0, s, c, 0.0),
|
|
vec4(0.0, 0.0, 0.0, 1.0)
|
|
);
|
|
}
|
|
|
|
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)
|
|
);
|
|
}
|
|
|
|
mat4 rotateZ(float theta) {
|
|
float c = cos(theta);
|
|
float s = sin(theta);
|
|
|
|
return mat4(
|
|
vec4(c, -s, 0.0, 0.0),
|
|
vec4(s, c, 0.0, 0.0),
|
|
vec4(0.0, 0.0, 1.0, 0.0),
|
|
vec4(0.0, 0.0, 0.0, 1.0)
|
|
);
|
|
}
|
|
|
|
// ------------ 2D SDFs ----------------------------------------
|
|
float circle_sdf(vec2 st) {
|
|
return length(st-.5)*2.;
|
|
}
|
|
|
|
float stroke(float x, float d, float w){
|
|
float r = step(d,x+w*.5) - step(d,x-w*.5);
|
|
return clamp(r, 0., 1.);
|
|
}
|
|
|
|
// ------------------- SHADER ----------------------------------
|
|
#define FPS 8.0
|
|
// 8 sec for a complete rotation
|
|
|
|
float graphics_2D( vec2 st );
|
|
|
|
float custom_dither( float x ) {
|
|
float theta_step = TWO_PI / 64.0;
|
|
float stepping = floor(-u_time*FPS) * theta_step;
|
|
float sin_theta = sin(stepping);
|
|
float cos_theta = cos(stepping);
|
|
mat2 rot_mat = mat2( cos_theta,-sin_theta, sin_theta, cos_theta);
|
|
vec2 rot = gl_FragCoord.xy * rot_mat;
|
|
float darkpat = step( 0.9-x, fract(rot.x*0.25))
|
|
* step(0.75, fract(rot.y/4.0))*0.5;
|
|
|
|
float bars = rand( vec2( gl_FragCoord.x, stepping), 0.0 );
|
|
float lightpat = step( 1.0-x*1.1, bars);
|
|
|
|
float th = step( 0.06, x );
|
|
float dit = (1.0-th) * darkpat + th * lightpat;
|
|
|
|
return dit;
|
|
}
|
|
|
|
void main(){
|
|
float a3d = 0.0;
|
|
float dit = 0.0;
|
|
|
|
// ------ 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, 0.0, 4.0 );
|
|
|
|
float clok = u_time * 0.2;
|
|
float theta = lfo_ramp( 0.03 ) * TWO_PI;
|
|
vec3 camera = vec3( 5.5, 0.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) {
|
|
vec3 p = camera + dist * worldDir;
|
|
a3d = K_a;
|
|
a3d += phongContribForLight(K_d, p, camera, light_pos );
|
|
dit = custom_dither( a3d );
|
|
}
|
|
|
|
float a2d = graphics_2D( st );
|
|
|
|
gl_FragColor = vec4(vec3(dit+a2d), 1.0 );
|
|
}
|
|
|
|
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 pira = sdPyramid( vec3(rotated.x, rotated.y + 0.5, rotated.z), 1.25 );
|
|
|
|
sdf = pira;
|
|
|
|
return sdf;
|
|
}
|
|
|
|
float graphics_2D( vec2 st ){
|
|
float theta_step = TWO_PI / 64.0;
|
|
float stepping = floor(-u_time*FPS) * theta_step;
|
|
|
|
vec2 cst = rotated( st, stepping ) - 0.5;
|
|
float c = length(cst)*2.0;
|
|
|
|
float s = stroke( c, 0.75, 0.04 );
|
|
s *= step(0.98, rand( st, stepping ));
|
|
s *= step( TWO_PI * 0.09, abs(atan( cst.x / cst.y )) );
|
|
|
|
return s;
|
|
}
|