Skip to content

Instantly share code, notes, and snippets.

@sudonhim
Created May 22, 2016 00:34
Show Gist options
  • Save sudonhim/9e43c255edcb45519fe6ddc612ab1ac5 to your computer and use it in GitHub Desktop.
Save sudonhim/9e43c255edcb45519fe6ddc612ab1ac5 to your computer and use it in GitHub Desktop.
#ifdef GL_ES
precision mediump float;
#endif
#extension GL_OES_standard_derivatives : enable
uniform float time;
uniform vec2 mouse;
uniform vec2 resolution;
const float speed = 0.5;
const float planetRadius = 60.0;
const float cameraHeightAboveSurface = 5.0;
// Forward declaration of simplex noise function, see bottom of file
float snoise(vec3 v);
bool notEmpty(ivec3 p)
{
vec3 fp = vec3(p);
float x = fp.x;
float theta = atan(fp.y, fp.z);
float r = length(vec3(p));
r -= abs(x);
r += sin(theta*10.0);
return r < planetRadius;
}
// Intersect the scene with a DDA (discrete digital analyzer)
bool intersect(vec3 ro, vec3 rd, out ivec3 hitCube, out float hitDist, out vec3 normal)
{
bool success = false;
ivec3 mapPos = ivec3(floor(ro));
vec3 deltaDist = abs(vec3(length(rd)) / rd);
ivec3 rayStep = ivec3(sign(rd));
vec3 sideDist = (sign(rd) * (vec3(mapPos) - ro) + (sign(rd) * 0.5) + 0.5) * deltaDist;
bvec3 mask;
for (int i = 0; i < 64; i++) {
if (notEmpty(mapPos)) {success = true; break;}
if (sideDist.x < sideDist.y) {
if (sideDist.x < sideDist.z) {
sideDist.x += deltaDist.x;
mapPos.x += rayStep.x;
mask = bvec3(true, false, false);
}
else {
sideDist.z += deltaDist.z;
mapPos.z += rayStep.z;
mask = bvec3(false, false, true);
}
}
else {
if (sideDist.y < sideDist.z) {
sideDist.y += deltaDist.y;
mapPos.y += rayStep.y;
mask = bvec3(false, true, false);
}
else {
sideDist.z += deltaDist.z;
mapPos.z += rayStep.z;
mask = bvec3(false, false, true);
}
}
}
hitCube = mapPos;
hitDist = length((sideDist - deltaDist)*vec3(mask));
normal = -sign(rd)*vec3(mask);
return success;
}
void cameraTransform(inout vec3 ro, inout vec3 rd)
{
// Move over the planet with time
float theta = time*speed/5.0;
float c = cos(theta);
float s = sin(theta);
mat3 rot = mat3(
1.0, 0.0, 0.0,
0.0, c, -s,
0.0, s, c
);
rd *= rot;
ro *= rot;
// Look down a little bit
theta = 0.4;
c = cos(theta);
s = sin(theta);
rot = mat3(
1.0, 0.0, 0.0,
0.0, c, -s,
0.0, s, c
);
rd *= rot;
// Rotate ray direction horizontally a little
theta = snoise(ro/planetRadius+time/16.0);
c = cos(theta);
s = sin(theta);
rot = mat3(
c, 0.0, s,
0.0, 1.0, 0.0,
-s, 0.0, c
);
rd *= rot;
}
// Return how close this point is to a block edge
float edgeDetect(vec3 pos)
{
vec3 dists = min(fract(abs(pos)), 1.0-fract(abs(pos)));
float sum = dists.x + dists.y + dists.z;
return sum - max(dists.x, max(dists.y, dists.z));
}
vec3 hashPosition(ivec3 p)
{
return
sin(
vec3(p.xyz)*294.234
+ vec3(p.yzx)*43.24
+ vec3(p.zxy)*234.22
)*0.5+0.5;
}
// Return the position of a point light source a bit ahead of the camera
vec3 lightPosition(vec3 ro)
{
// Rotate a little futher over the planet
float theta = 0.3;
float c = cos(theta);
float s = sin(theta);
mat3 rot = mat3(
1.0, 0.0, 0.0,
0.0, c, -s,
0.0, s, c
);
return ro * rot;
}
vec3 snoise3(vec3 x)
{
return vec3(
snoise(x),
snoise(x+2323.245),
snoise(x+3984.2423)
);
}
vec3 lightColorFromDirection(vec3 lightDir)
{
lightDir.x += time/6.0;
float i1 = snoise(lightDir*3.0);
float i2 = snoise((lightDir+32.421)*3.0);
float t = mix(i1, i2, 0.5+0.5*sin(time));
t = 0.2 + 0.6*t;
vec3 c1 = vec3(1.0, 1.0, 0.0);
vec3 c2 = vec3(0.3, 0.1, 0.0);
return mix(c1, c2, t);
}
vec3 perturbNormal(vec3 normal, vec3 incident, vec3 props)
{
float freq = 20.0;
float strength = 0.2;
vec3 noise = snoise3(incident*freq);
return normalize(mix(normal, noise, strength));
}
vec3 doLighting(vec3 ro, vec3 rd, float hitDist, ivec3 hitCube, vec3 normal)
{
vec3 incident = ro + hitDist*rd;
float edgeDist = edgeDetect(incident);
float edgeAdj = smoothstep(0.1, 0.0, edgeDist);
vec3 props = hashPosition(hitCube);
normal = perturbNormal(normal, incident, props);
vec3 lightPos = lightPosition(ro);
vec3 lightDisp = incident - lightPos;
vec3 lightDir = normalize(lightDisp);
float brightness = 1.0/(1.0+length(lightDisp));
float diffuse = max(dot(normal, -lightDir), 0.0)*brightness;
float ambient = brightness;
vec3 reflDir = reflect(rd, normal);
float specular = pow(max(dot(reflDir, -lightDir), 0.0), 8.0);
vec3 lightColor = lightColorFromDirection(lightDir);
vec3 color = vec3(0.0);
color += diffuse*lightColor;
color += 0.2*ambient*vec3(1.0);
color += 0.2*specular*lightColor;
color += props*edgeAdj*0.2;
return log(1.0+color)*log(2.0)/log(4.0);
}
bool intersectLightSource(vec3 ro, vec3 rd, out float hitDist)
{
bool success = false;
float radius = 2.0;
vec3 lightPos = lightPosition(ro);
float a = dot(rd, rd);
float b = 2.0 * dot(rd, ro - lightPos);
float c = dot(lightPos, lightPos) + dot(ro, ro) - 2.0 * dot(lightPos, ro) - radius*radius;
float det = b*b-4.0*a*c;
if (det < 0.0) return false;
else
{
hitDist = (-b - sqrt(det))/(2.0*a);
return true;
}
}
vec3 doLightingLightSource(vec3 ro, vec3 hitPos)
{
vec3 lightDir = normalize(hitPos - lightPosition(ro));
return lightColorFromDirection(lightDir);
}
void main( void ) {
vec2 uv = ( gl_FragCoord.xy / resolution.xy );
vec2 p = uv - 0.5;
p.y *= resolution.y/resolution.x;
vec3 ro = vec3(0.0, planetRadius + cameraHeightAboveSurface, 0.0);
vec3 rd = normalize(vec3(p*1.5, 1.0));
cameraTransform(ro, rd);
// Intersect the scene
ivec3 hitCube;
float hitDist;
vec3 normal;
bool hitCubes = intersect(ro, rd, hitCube, hitDist, normal);
// Intersect the light source
float hitDist2;
bool hitLight = intersectLightSource(ro, rd, hitDist2);
// If hit both, show the closest
if (hitCubes && hitLight && (hitDist < hitDist2)) hitLight = false;
vec3 color = vec3(0.0);
if (hitLight) {
color = doLightingLightSource(ro, hitDist2*rd + ro);
} else if (hitCubes) {
color = doLighting(ro, rd, hitDist, hitCube, normal);
} else {
}
gl_FragColor = vec4( color, 1.0 );
}
//
// Description : Array and textureless GLSL 2D/3D/4D simplex
// noise functions.
// 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
//
vec3 mod289(vec3 x) {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec4 mod289(vec4 x) {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec4 permute(vec4 x) {
return mod289(((x*34.0)+1.0)*x);
}
vec4 taylorInvSqrt(vec4 r)
{
return 1.79284291400159 - 0.85373472095314 * r;
}
float snoise(vec3 v)
{
const vec2 C = vec2(1.0/6.0, 1.0/3.0) ;
const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);
// First corner
vec3 i = floor(v + dot(v, C.yyy) );
vec3 x0 = v - i + dot(i, C.xxx) ;
// Other corners
vec3 g = step(x0.yzx, x0.xyz);
vec3 l = 1.0 - g;
vec3 i1 = min( g.xyz, l.zxy );
vec3 i2 = max( g.xyz, l.zxy );
// x0 = x0 - 0.0 + 0.0 * C.xxx;
// x1 = x0 - i1 + 1.0 * C.xxx;
// x2 = x0 - i2 + 2.0 * C.xxx;
// x3 = x0 - 1.0 + 3.0 * C.xxx;
vec3 x1 = x0 - i1 + C.xxx;
vec3 x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y
vec3 x3 = x0 - D.yyy; // -1.0+3.0*C.x = -0.5 = -D.y
// Permutations
i = mod289(i);
vec4 p = permute( permute( permute(
i.z + vec4(0.0, i1.z, i2.z, 1.0 ))
+ i.y + vec4(0.0, i1.y, i2.y, 1.0 ))
+ i.x + vec4(0.0, i1.x, i2.x, 1.0 ));
// Gradients: 7x7 points over a square, mapped onto an octahedron.
// The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294)
float n_ = 0.142857142857; // 1.0/7.0
vec3 ns = n_ * D.wyz - D.xzx;
vec4 j = p - 49.0 * floor(p * ns.z * ns.z); // mod(p,7*7)
vec4 x_ = floor(j * ns.z);
vec4 y_ = floor(j - 7.0 * x_ ); // mod(j,N)
vec4 x = x_ *ns.x + ns.yyyy;
vec4 y = y_ *ns.x + ns.yyyy;
vec4 h = 1.0 - abs(x) - abs(y);
vec4 b0 = vec4( x.xy, y.xy );
vec4 b1 = vec4( x.zw, y.zw );
//vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0;
//vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0;
vec4 s0 = floor(b0)*2.0 + 1.0;
vec4 s1 = floor(b1)*2.0 + 1.0;
vec4 sh = -step(h, vec4(0.0));
vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy ;
vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww ;
vec3 p0 = vec3(a0.xy,h.x);
vec3 p1 = vec3(a0.zw,h.y);
vec3 p2 = vec3(a1.xy,h.z);
vec3 p3 = vec3(a1.zw,h.w);
//Normalise gradients
vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3)));
p0 *= norm.x;
p1 *= norm.y;
p2 *= norm.z;
p3 *= norm.w;
// Mix final noise value
vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);
m = m * m;
return 42.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1),
dot(p2,x2), dot(p3,x3) ) );
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment