311 lines
No EOL
12 KiB
GLSL
311 lines
No EOL
12 KiB
GLSL
// 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;
|
|
} |