Skip to content

Instantly share code, notes, and snippets.

@ShiftedClock
Created April 10, 2019 23:51
Show Gist options
  • Select an option

  • Save ShiftedClock/49bd98a3cddea9f0315855f7a69c6e04 to your computer and use it in GitHub Desktop.

Select an option

Save ShiftedClock/49bd98a3cddea9f0315855f7a69c6e04 to your computer and use it in GitHub Desktop.
Raymarching Refraction Shader from: https://www.noirbear.com/toys/marching/refraction.html
#define M_PI 3.1415926535897932384626433832795
#define M_2PI 6.2831853071795864769252867665590
#define SKY_R 60.0 // inner radius of sky sphere
#define TRANS_MAT 2.0 // largest material ID for transparent materials
// Many, many thanks to IQ for providing the incredibly helpful sample
// code for getting started with ray marching: https://www.shadertoy.com/view/Xds3zN
precision mediump float;
uniform float time;
uniform vec2 uResolution;
uniform vec2 uMouse;
uniform sampler2D uFloorSampler;
uniform sampler2D uSkySampler;
const float maxDepth = 128.0;
const int maxIterations = 256;
const float epsilon = 0.001;
// polynomial smooth min
float smin( float a, float b, float k )
{
float h = clamp( 0.5+0.5*(b-a)/k, 0.0, 1.0 );
return mix( b, a, h ) - k*h*(1.0-h);
}
float length8(vec2 p)
{
return pow(pow(p.x,0.125) + pow(p.y,0.125), 0.125);
}
float plane( vec3 p )
{
return p.y;
}
float sphere(vec3 p, float s)
{
return (length(p)-s);
}
float wigglesphere(vec3 p, float s)
{
return 0.02*sin(35.0*p.y + 8.*time) + 0.1*cos(5.*p.z + 2.*time) + sphere(p, s);
}
float wigglesphere1(vec3 p, float s)
{
return 0.02*sin(65.*p.z*p.x + 20.*time) + 0.01*cos(120.*p.x - 25.*time) - 0.02*sin(30.*p.z + time) + sphere(p, s);
}
float udRoundBox( vec3 p, vec3 b, float r )
{
return length(max(abs(p)-b,0.0))-r;
}
float sdTorus( vec3 p, vec2 t )
{
vec2 q = vec2(length(p.xy)-t.x,p.z);
return length(q)-t.y;
}
vec2 opU(vec2 d1, vec2 d2)
{
return (d1.x < d2.x) ? d1 : d2;
}
float opBlend( float d1, float d2 )
{
return smin( d1, d2, 0.1 );
}
float opTwist( vec3 p )
{
float c = cos(20.0*p.y);
float s = sin(20.0*p.y);
mat2 m = mat2(c,-s,s,c);
vec3 q = vec3(m*p.xz,p.y);
return sdTorus(q, vec2(0.4, 0.1));
}
float opBend( vec3 p )
{
float c = 0.6*cos(0.5*p.x)+0.2*sin(0.3*p.z);
float s = 0.1*sin(0.08*p.z*p.x);
mat2 m = mat2(c,-s,s,c);
vec3 q = vec3(m*p.xy,p.y);
return plane(vec3(p.x, p.y+c+s, p.z));//, vec3(0.62, 0.32, 0.5), 0.25);
}
vec2 map(in vec3 p)
{
vec2 res = opU(opU( vec2(opBend(vec3(p.x, p.y + 0.65, p.z)), 4.0), // floor plane
vec2(max(sphere(vec3(p.x, p.y + 0.65, p.z), SKY_R), - sphere(vec3(p.x, p.y + 0.65, p.z), SKY_R)), 3.0)), // sky sphere
vec2(opBlend(sdTorus(vec3(p.x+1.0, p.y-0.1, p.z), vec2(0.4,0.15)), udRoundBox(vec3(p.x, p.y, p.z), vec3(0.62, 0.32, 0.2), 0.3)), 1.0)
//vec2(wigglesphere1(p, 0.62), 1.0) // wiggly blob
);
//res = opU(res, vec2(wigglesphere1(p, 0.55), 1.0)); // for debugging
return res;
}
vec3 calcNormal( in vec3 pos )
{
vec3 eps = vec3( 0.001, 0.0, 0.0 );
vec3 nor = vec3(
map(pos+eps.xyy).x - map(pos-eps.xyy).x,
map(pos+eps.yxy).x - map(pos-eps.yxy).x,
map(pos+eps.yyx).x - map(pos-eps.yyx).x );
return normalize(nor);
}
float softshadow( in vec3 ro, in vec3 rd, in float mint, in float maxt, in float k )
{
float res = 1.0;
float t = mint;
for( int i=0; i<30; i++ )
{
if( t<maxt )
{
float h = map( ro + rd*t ).x;
res = min( res, k*h/t );
t += 0.02;
}
}
return clamp( res, 0.3, 1.0 );
}
float calcAO( in vec3 pos, in vec3 nor )
{
float total = 0.0;
float sca = 1.0;
for( int aoi=0; aoi<5; aoi++ )
{
float hr = 0.01 + 0.05*float(aoi);
vec3 aopos = nor * hr + pos;
float dd = map( aopos ).x;
total += -(dd-hr)*sca;
sca *= 0.75;
}
return clamp( 1.0 - 4.0*total, 0.0, 1.0 );
}
vec3 getColorAt(in vec3 pos, in float mat, in float dist, in vec3 rd)
{
vec3 col = vec3(0.5);
if( dist < maxDepth ) // raymarch converged after dist steps
{
vec3 normal = calcNormal( pos );
vec3 light = normalize( vec3(-1.0, 1.5, -1.1) );
float ao = calcAO(pos, normal);
if (mat > 3.0) // floor has mat == 4.0
{
float amb = clamp( 0.5+0.5*normal.y - (dist / maxDepth), 0.0, 1.0 );
float diffuse = clamp(dot(normal, light), 0.0, 1.0);
float bac = clamp( dot( normal, normalize(vec3(-light.x,0.0,-light.z))), 0.0, 1.0 )*clamp( 1.0-pos.y,0.0,1.0);
vec3 brdf = vec3(0.0);
float sh = softshadow( pos, light, 0.02, 2.0, 1.0 );
float pp = clamp( dot( reflect(rd,normal), light ), 0.0, 1.0 );
float spe = sh*pow(pp,16.0);
vec3 tex = texture2D(uFloorSampler, vec2(pos.x*0.1, pos.z*0.1)).rgb;
diffuse *= sh;
brdf += amb*vec3(0.99215, 0.77647, 0.38431);
brdf += vec3(0.4, 0.2, 0.0) * clamp(dot(normal, light), 0.0, 1.0) * (1.0 / pow(distance(pos, light),4.0))*0.2;
brdf += 0.2*bac*vec3(0.15, 0.15, 0.15);
brdf += 1.2*diffuse*vec3(1.0, 0.9, 0.7);
col = brdf * tex * ao*ao + vec3(0.1)*spe;
}
else if (mat> 2.0) // sky has mat == 3.0
{
vec3 np = normalize(pos);
vec2 nnp = normalize(pos.xz);
float arc = (atan(nnp.x, nnp.y) - atan(-1.0, -1.0) + M_2PI) * SKY_R;
float tx = arc / (M_2PI * SKY_R);
col = texture2D(uSkySampler, vec2(tx, -np.y-0.05)).rgb;
}
else if (mat > 0.0) // scene objects
{
float diffuse = clamp(dot(normal, light), 0.3, 1.0);
//col = vec3(dif, dif, dif);
col = vec3(0.2*diffuse, 0.3*diffuse, 0.8*diffuse) * (1.0 / pow(distance(pos, light),3.0));
}
}
return vec3( clamp(col,0.0,1.0) );
}
vec3 castRay( in vec3 ro, in vec3 rd, in float maxd )
{
float h=epsilon*2.0; // step size
vec2 result; // result of distance check
float t = 0.0; // distance travelled
float m = -1.0; // material
vec3 color = vec3(1.0);
vec3 pos = ro+rd*t;
for( int i=0; i<maxIterations; i++ )
{
if( abs(h)<epsilon||t>maxd ) continue;//break;
t += h;
pos = ro+rd*t;
result = map(pos);
h = result.x;
m = result.y;
}
if ((t < maxd) && (m < 3.0)) // hit transparent surface; march again
{
pos = ro+t*rd;
vec3 new_ro = pos;
vec3 rd_ref = refract(rd, calcNormal(pos), 0.75);
color *= getColorAt(pos, m, t, rd); // color @ surface
//t = 0.0;
// do refractive ray
h = epsilon*1000.0;
for( int i=0; i<maxIterations; i++ )
{
if( abs(h)<epsilon||t>maxd ) continue;//break;
t += h;
pos = new_ro+t*rd_ref;
result = map(pos);
h = result.x;
m = result.y;
}
color += getColorAt(pos, m, t, rd_ref); // color from refracted ray
// do penetrative ray
h = epsilon*1000.0;
for ( int i=0; i<maxIterations; i++)
{
if( abs(h) < epsilon||t>maxd ) continue; //break;
t += h;
pos = new_ro+t*rd;
result = map(pos);
h = result.x;
m = result.y;
}
}
return color*getColorAt(pos, m, t, rd);
}
vec3 render( in vec3 ro, in vec3 rd )
{
return castRay(ro,rd,maxDepth);
}
void main(void)
{
vec2 q = gl_FragCoord.xy/uResolution.xy;
vec2 p = -1.0+2.0*q;
p.x *= uResolution.x/uResolution.y;
vec2 mo = uMouse.xy/uResolution.xy;
// camera
vec3 rorigin = vec3(3.2*cos(6.0*mo.x), 2.0*mo.y, 3.2*sin(6.0*mo.x) );
vec3 ta = vec3( -0.5, -0.4, 0.5 );
// camera tx
vec3 cw = normalize( ta-rorigin );
vec3 cp = vec3( 0.0, 1.0, 0.0 );
vec3 cu = normalize( cross(cw,cp) );
vec3 cv = normalize( cross(cu,cw) );
vec3 rdir = normalize( p.x*cu + p.y*cv + 2.5*cw );
vec3 color = render( rorigin, rdir );
gl_FragColor=vec4( color, 1.0 );
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment