// Taken from: https://www.shadertoy.com/view/ldy3DR // // Created by Sam Gondelman on 6/9/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 // // Raymarching constants const float TMAX = 250.0; const int MAX_STEPS = 200; const float DIST_THRESHOLD = 0.00001; // 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 vec3 SUN_DIR = normalize(vec3(-0.5, -1.0, -1.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 // http://iquilezles.org/www/articles/distfunctions/distfunctions.htm float sphere(vec3 p, float r) { return length(p) - r; } 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 roundedBox(vec3 p, vec3 dim, float r) { vec3 d = abs(p) - dim; return length(max(d,vec3(0.0))) - r; } float coneSection(vec3 p, float h, float r1, float r2) { float d1 = -p.y - h; float q = p.y - h; float si = 0.5*(r1-r2)/h; float d2 = max(sqrt(dot(p.xz,p.xz)*(1.0-si*si)) + q*si - r2, q); return length(max(vec2(d1,d2),0.0)) + min(max(d1,d2), 0.0); } float subtraction(float d1, float d2) { return max(-d1,d2); } float blend(float a, float b, float k) { float h = clamp(mix(1.0, (b-a)/k, 0.5), 0.0, 1.0); return mix(b, a, h) - k*h*(1.0-h); } 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, blend(coneSection(p-vec3(0.0, 0.0, 0.0), 0.3, 1.0, 0.5), coneSection(p-vec3(0.0, 0.6, 0.0), 0.3, 0.8, 1.0), 0.5), 1); propose(res, matID, coneSection(p-vec3(0.0, 1.2, 0.0), 0.6, 1.0, 0.5), 2); return res; } 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)); } // Lighting float directionalLightDiffuse(vec3 nor, vec3 ldir) { return clamp01(dot(nor, -ldir)); } // Coloring different materials vec3 grey(Ray ray) { return vec3(0.8); } vec3 glass(Ray ray) { return vec3(0.5, 0.0, 0.0); } vec4 computeColor(Ray ray) { vec3 col = vec3(0.0); float alpha = 1.0; // Switch on matID if (ray.matID == 0) { discard; // transparent background } else if (ray.matID == 1){ col = grey(ray); } else if (ray.matID == 2){ col = glass(ray); alpha = 0.5; } // Default lighting float sunLight = directionalLightDiffuse(ray.nor, SUN_DIR); col = col * (0.8 * sunLight + 0.1); return vec4(col, alpha); } vec4 raymarch(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 render(Ray ray, inout float dist) { vec4 result = raymarch(ray); dist = ray.t; return clamp01(result); } vec4 getProceduralColor(inout float dist) { 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; vec3 rd = normalize(ro - eye); // ray from camera eye to ro ray.src = eye; ray.dir = rd; return render(ray, dist); } float getProceduralColors(inout vec3 diffuse, inout vec3 specular, inout float shininess) { float dist; vec4 color = getProceduralColor(dist); diffuse = color.rgb; specular = color.rgb; shininess = 0.5; return color.a; }