// Based on https://www.shadertoy.com/view/Xt33D8 // // Created by Sam Gondelman on 7/22/2016 // Copyright 2016 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // // Math constants const float PI = 3.14159265359; const float EPS = 0.0001; const float FLT_MAX = 1.0 / 0.000000000001; // hacky but GLSL doesn't have a FLT_MAX by default // Raymarching constants const float TMAX = 100.0; const int MAX_STEPS = 100; const float DIST_THRESHOLD = 0.00001; // Timer uniforms uniform float cubeTime = 0.0; uniform float growTime = 0.0; uniform float distTime = 0.0; // Other constants/helpers const vec3 UP = vec3(0.0, 1.0, 0.0); #define clamp01(a) clamp(a, 0.0, 1.0) struct Ray { vec3 src; vec3 dir; float t; vec3 pos; vec3 nor; int matID; int iter; }; // GLSL default parameters don't seem to work so this is for any call to map // where you don't actually care about the material of what you hit int junkMatID; // Primitives float plane(vec3 p, float y) { return p.y - y; } float box(vec3 p, vec3 dim) { vec3 d = abs(p) - dim; return min(max(d.x,max(d.y,d.z)),0.0) + length(max(d,0.0)); } float cylinder(vec3 p, float r, float h) { vec2 d = abs(vec2(length(p.xz),p.y)) - vec2(r, h); return min(max(d.x,d.y),0.0) + length(max(d,0.0)); } float cone(vec3 p, float r, float h) { float d1 = -p.y - h; float q = p.y - h; float si = 0.5*r/h; float d2 = max(sqrt(dot(p.xz,p.xz)*(1.0-si*si)) + q*si, q); return length(max(vec2(d1,d2),0.0)) + min(max(d1,d2), 0.); } // CSG operations float intersectionDist(float d1, float d2) { return max(d1, d2); } float subtractionDist(float d1, float d2) { return max(d1, -d2); } // Transformations vec3 rX(vec3 p, float a) { float c = cos(radians(a)); float s = sin(radians(a)); vec3 q = p; p.y = c * q.y - s * q.z; p.z = s * q.y + c * q.z; return p; } vec3 rY(vec3 p, float a) { float c = cos(radians(a)); float s = sin(radians(a)); vec3 q = p; p.x = c * q.x + s * q.z; p.z = -s * q.x + c * q.z; return p; } void propose(inout float val, inout int matID, float proposedVal, int proposedMatID) { if (proposedVal < val) { val = proposedVal; matID = proposedMatID; } } float map(vec3 p, inout int matID) { float res = FLT_MAX; propose(res, matID, cylinder(p, 1.0, 0.05), 2); float height = 4.0; float heightOffset = 0.05 * cos(1.1 * iGlobalTime); propose(res, matID, cylinder(p - vec3(0, height/2.0, 0), 0.05, height/2.0), 2); propose(res, matID, box(p - vec3(0, height, 0), vec3(1.0, 0.05, 1.0)), 2); const float CUBE_GROW_TIME = 0.25; float cubeRatio = 1.0 - cubeTime/CUBE_GROW_TIME; propose(res, matID, box(rY(rX(p - vec3(0, (1.75 + heightOffset) * height, 0), 45.0 + iGlobalTime * 20.0), 45.0 + iGlobalTime * 20.0), vec3(0.75*cubeRatio)), 2); const float GROW_TIME = 0.5; float ratio = growTime/GROW_TIME; float outerRing = subtractionDist( cylinder(rX(rY(p - vec3(0, (1.75 + heightOffset) * height, 0), 90.0 + 360.0 * ratio), 90.0), ratio*2.5, 0.05), cylinder(rX(rY(p - vec3(0, (1.75 + heightOffset) * height, 0), 90.0 + 360.0 * ratio), 90.0), ratio*2.1, 0.1)); propose(res, matID, outerRing, 2); float outerRing2 = subtractionDist( cylinder(rX(rY(p - vec3(0, (1.75 + heightOffset) * height, 0), 90.0 - 360.0 * ratio), 90.0), ratio*2.4, 0.1), cylinder(rX(rY(p - vec3(0, (1.75 + heightOffset) * height, 0), 90.0 - 360.0 * ratio), 90.0), ratio*2.2, 0.15)); propose(res, matID, outerRing2, 2); float smallRing1 = subtractionDist( cylinder(rX(rY(p - vec3(0, (1.75 + heightOffset) * height, 0), 90.0 + 360.0 * ratio), 90.0), ratio*2.875, 0.125), cylinder(rX(rY(p - vec3(0, (1.75 + heightOffset) * height, 0), 90.0 + 360.0 * ratio), 90.0), ratio*2.725, 0.15)); float t1 = iGlobalTime / 5.0; float angle1 = 110.0 + 150.0 * abs(cos(5.0*t1)*cos(t1) + sin(2.0*t1)*cos(0.5*t1))/ 2.0; float cone1 = cone(rX(p - vec3(0, (1.0 + heightOffset) * height, 0) - vec3(0, 0.75 * height, 0), angle1), 1.0, 0.75*height); float plane1 = plane(rX(p - vec3(0, (1.0 + heightOffset) * height, 0) - vec3(0, 0.75 * height, 0.0), angle1), 0.0); //propose(res, matID, intersectionDist(cone1, plane1), 2); propose(res, matID, intersectionDist(smallRing1, intersectionDist(cone1, plane1)), 2); float smallRing2 = subtractionDist( cylinder(rX(rY(p - vec3(0, (1.75 + heightOffset) * height, 0), 90.0 + 360.0 * ratio), 90.0), ratio*1.875, 0.125), cylinder(rX(rY(p - vec3(0, (1.75 + heightOffset) * height, 0), 90.0 + 360.0 * ratio), 90.0), ratio*1.725, 0.15)); float t2 = iGlobalTime / 6.5; float angle2 = -110.0 + 230.0 * abs(cos(2.0*t2)*cos(4.5*t2) + sin(2.25*t2)*cos(0.25*t2) + 0.5)/ 2.0; float cone2 = cone(rX(p - vec3(0, (1.0 + heightOffset) * height, 0) - vec3(0, 0.75 * height, 0), angle2), 1.0, 0.75*height); float plane2 = plane(rX(p - vec3(0, (1.0 + heightOffset) * height, 0) - vec3(0, 0.75 * height, 0.0), angle2), 0.0); //propose(res, matID, intersectionDist(cone2, plane2), 2); propose(res, matID, intersectionDist(smallRing2, intersectionDist(cone2, plane2)), 2); float ballBottom = subtractionDist( cylinder(rX(rY(p - vec3(0, (1.75 + heightOffset) * height, 0), 90.0 + 180.0 * ratio), 90.0), ratio*1.5, 0.05), cylinder(rX(rY(p - vec3(0, (1.75 + heightOffset) * height, 0), 90.0 + 180.0 * ratio), 90.0), ratio*0.725, 0.1)); propose(res, matID, intersectionDist(ballBottom, plane(p, (1.75 + heightOffset) * height - 0.1)), 2); float ballTop = subtractionDist( cylinder(rX(rY(p - vec3(0, (1.75 + heightOffset) * height, 0), 90.0 - 180.0 * ratio), 90.0), ratio*1.5, 0.05), cylinder(rX(rY(p - vec3(0, (1.75 + heightOffset) * height, 0), 90.0 - 180.0 * ratio), 90.0), ratio*0.725, 0.1)); propose(res, matID, intersectionDist(ballTop, -plane(p, (1.75 + heightOffset) * height + 0.1)), 2); const float ROT_STEP = 1.5; const float ROT_REST = 1.0; float rotOffset = 180.0 * sin(PI / 2.0 * min(ROT_STEP, mod(distTime, ROT_STEP + ROT_REST)) / ROT_STEP); if (mod(floor(distTime / (ROT_STEP + ROT_REST)), 3.0) < 2.0) { rotOffset = 180.0 + rotOffset; } else { rotOffset = 360.0 - 2.0 * rotOffset; } propose(res, matID, cylinder(rX(rY(p - vec3(0, (1.75 + heightOffset) * height, 0), 90.0 - 720.0 * ratio + rotOffset), 90.0), ratio*0.5, 0.05), 2); return res; } // Lighting float directionalLightDiffuse(vec3 nor, vec3 ldir) { return clamp01(dot(nor, -ldir)); } vec3 LIGHT_DIR = vec3(-0.577); vec3 computeColor(Ray ray) { vec3 col = vec3(0.0); // Default lighting float sunLight = directionalLightDiffuse(ray.nor, LIGHT_DIR); // Switch on matID // a return -> different/no lighting // no return -> default lighting if (ray.matID == 2) { col = vec3(0.345, 1.0, 1.0); col = mix(vec3(0.133, 0.294, 0.784), col, sunLight); } else { discard; } return col; } vec3 calculateNormal(vec3 pos) { const vec3 e = vec3(EPS, 0.0, 0.0); float p = map(pos, junkMatID); return normalize(vec3(map(pos + e.xyy, junkMatID) - p, map(pos + e.yxy, junkMatID) - p, map(pos + e.yyx, junkMatID) - p)); } vec3 render(inout Ray ray) { float h = 1.0; float t = 0.0; for(int i = 0; i < MAX_STEPS; i++) { h = map(ray.src + t*ray.dir, ray.matID); t += h; ray.iter = i; if (t > TMAX || h < DIST_THRESHOLD) break; } ray.t = t; ray.pos = ray.src + ray.t*ray.dir; ray.nor = calculateNormal(ray.pos); int missed = int(step(TMAX, ray.t)); ray.matID = (1- missed) * ray.matID; ray.nor *= float(1-missed); return computeColor(ray); } vec4 getProceduralColor() { Ray ray; ray.pos = vec3(0.0); ray.nor = vec3(0.0); ray.matID = 0; ray.t = 0.0; vec3 worldEye = getEyeWorldPos(); // this will make it movable, scalable, and rotatable vec3 ro = _position.xyz; vec3 eye = (inverse(iWorldOrientation) * (worldEye - iWorldPosition)) / iWorldScale; //vec3 ro = iWorldOrientation * (_position.xyz * iWorldScale) + iWorldPosition; // world position of the current fragment //vec3 eye = worldEye; // fit the map function inside a unit cube ro.x *= 5.4150; ro.y *= 10.1727; ro.z *= 5.4150; ro.x += 0.0; ro.y += 5.0139; ro.z += 0.0; eye.x *= 5.4150; eye.y *= 10.1727; eye.z *= 5.4150; eye.x += 0.0; eye.y += 5.0139; eye.z += 0.0; vec3 rd = normalize(ro - eye); // ray from camera eye to ro ray.src = eye; ray.dir = rd; return vec4(render(ray), 1.0); } float getProceduralColors(inout vec3 diffuse, inout vec3 specular, inout float shininess) { vec4 color = getProceduralColor(); diffuse = color.rgb; specular = color.rgb; shininess = 0.5; return 1.0; }