content/hifi-content/samuel/lavalamp.fs
2022-02-14 02:04:11 +01:00

186 lines
No EOL
5 KiB
GLSL

// 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;
}