//////////////////////////////////////////////////////////////// // // HG_SDF // // GLSL LIBRARY FOR BUILDING SIGNED DISTANCE BOUNDS // // version 2021-07-28 // // Check https://mercury.sexy/hg_sdf for updates // and usage examples. Send feedback to spheretracing@mercury.sexy. // // Brought to you by MERCURY https://mercury.sexy/ // // // // Released dual-licensed under // Creative Commons Attribution-NonCommercial (CC BY-NC) // or // MIT License // at your choice. // // SPDX-License-Identifier: MIT OR CC-BY-NC-4.0 // // ///// //////////////////////////////////////////////////////////////// // // HELPER FUNCTIONS/MACROS // //////////////////////////////////////////////////////////////// const float PI = 3.14159265; const float TAU = (2. * PI); const float PHI = sqrt(5.) * 0.5 + 0.5; vec3 applyFog(vec3 col, float t, vec3 rd, vec3 lightDir, float b) { float fogAmount = 1.0 - exp(-t * b); float sunAmount = max(dot(rd, lightDir), 0.); vec3 fogColor = mix(vec3(0.1529, 0.1137, 0.2), // blue vec3(0.2588, 0.1765, 0.3529), // yellow pow(sunAmount, 1.0)); return mix(col, fogColor, fogAmount); } mat2 scale(vec2 scale) { return mat2(1. / scale.x, 0.0, 0.0, 1. / scale.y); } // Sign function that doesn't return 0 float sgn(float x) { return (x < 0.) ? -1. : 1.; } vec2 sgn(vec2 v) { return vec2((v.x < 0.) ? -1. : 1., (v.y < 0.) ? -1. : 1.); } float square(float x) { return x * x; } vec2 square(vec2 x) { return x * x; } vec3 square(vec3 x) { return x * x; } float lengthSqr(vec3 x) { return dot(x, x); } // Maximum/minumum elements of a vector float vmax(vec2 v) { return max(v.x, v.y); } float vmax(vec3 v) { return max(max(v.x, v.y), v.z); } float vmax(vec4 v) { return max(max(v.x, v.y), max(v.z, v.w)); } float vmin(vec2 v) { return min(v.x, v.y); } float vmin(vec3 v) { return min(min(v.x, v.y), v.z); } float vmin(vec4 v) { return min(min(v.x, v.y), min(v.z, v.w)); } // Hexagonal prism, circumcircle variant float fHexagonCircumcircle(vec3 p, vec2 h) { vec3 q = abs(p); return max(q.y - h.y, max(q.x * sqrt(3.) * 0.5 + q.z * 0.5, q.z) - h.x); //this is mathematically equivalent to this line, but less efficient: //return max(q.y - h.y, max(dot(vec2(cos(PI/3), sin(PI/3)), q.zx), q.z) - h.x); } //////////////////////////////////////////////////////////////// // // PRIMITIVE DISTANCE FUNCTIONS // //////////////////////////////////////////////////////////////// // // Conventions: // // Everything that is a distance function is called fSomething. // The first argument is always a point in 2 or 3-space called
.
// Unless otherwise noted, (if the object has an intrinsic "up"
// side or direction) the y axis is "up" and the object is
// centered at the origin.
//
////////////////////////////////////////////////////////////////
float fSphere(vec3 p, float r) {
return length(p) - r;
}
// Plane with normal n (n is normalized) at some distance from the origin
float fPlane(vec3 p, vec3 n, float distanceFromOrigin) {
return dot(p, n) + distanceFromOrigin;
}
// Cheap Box: distance to corners is overestimated
float fBoxCheap(vec3 p, vec3 b) { //cheap box
return vmax(abs(p) - b);
}
// Box: correct distance to corners
float fBox(vec3 p, vec3 b) {
vec3 d = abs(p) - b;
return length(max(d, vec3(0))) + vmax(min(d, vec3(0)));
}
// Same as above, but in two dimensions (an endless box)
float fBox2Cheap(vec2 p, vec2 b) {
return vmax(abs(p) - b);
}
float fBox2(vec2 p, vec2 b) {
vec2 d = abs(p) - b;
return length(max(d, vec2(0.))) + vmax(min(d, vec2(0.)));
}
// Endless "corner"
float fCorner(vec2 p) {
return length(max(p, vec2(0.))) + vmax(min(p, vec2(0.)));
}
// Cylinder standing upright on the xz plane
float fCylinder(vec3 p, float r, float height) {
float d = length(p.xz) - r;
d = max(d, abs(p.y) - height);
return d;
}
// Capsule: A Cylinder with round caps on both sides
float fCapsule(vec3 p, float r, float c) {
return mix(length(p.xz) - r, length(vec3(p.x, abs(p.y) - c, p.z)) - r, step(c, abs(p.y)));
}
// Distance to line segment between and , used for fCapsule() version 2below
float fLineSegment(vec3 p, vec3 a, vec3 b) {
vec3 ab = b - a;
float t = clamp(dot(p - a, ab) / dot(ab, ab), 0., 1.);
return length((ab * t + a) - p);
}
// Capsule version 2: between two end points and with radius r
float fCapsule(vec3 p, vec3 a, vec3 b, float r) {
return fLineSegment(p, a, b) - r;
}
// Torus in the XZ-plane
float fTorus(vec3 p, float smallRadius, float largeRadius) {
return length(vec2(length(p.xz) - largeRadius, p.y)) - smallRadius;
}
// A circle line. Can also be used to make a torus by subtracting the smaller radius of the torus.
float fCircle(vec3 p, float r) {
float l = length(p.xz) - r;
return length(vec2(p.y, l));
}
// A circular disc with no thickness (i.e. a cylinder with no height).
// Subtract some value to make a flat disc with rounded edge.
float fDisc(vec3 p, float r) {
float l = length(p.xz) - r;
return l < 0. ? abs(p.y) : length(vec2(p.y, l));
}
// Hexagonal prism, incircle variant
float fHexagonIncircle(vec3 p, vec2 h) {
return fHexagonCircumcircle(p, vec2(h.x * sqrt(3.) * 0.5, h.y));
}
// Cone with correct distances to tip and base circle. Y is up, 0 is in the middle of the base.
float fCone(vec3 p, float radius, float height) {
vec2 q = vec2(length(p.xz), p.y);
vec2 tip = q - vec2(0, height);
vec2 mantleDir = normalize(vec2(height, radius));
float mantle = dot(tip, mantleDir);
float d = max(mantle, -q.y);
float projected = dot(tip, vec2(mantleDir.y, -mantleDir.x));
// distance to tip
if((q.y > height) && (projected < 0.)) {
d = max(d, length(tip));
}
// distance to base ring
if((q.x > radius) && (projected > length(vec2(height, radius)))) {
d = max(d, length(q - vec2(radius, 0)));
}
return d;
}
////////////////////////////////////////////////////////////////
//
// DOMAIN MANIPULATION OPERATORS
//
////////////////////////////////////////////////////////////////
//
// Conventions:
//
// Everything that modifies the domain is named pSomething.
//
// Many operate only on a subset of the three dimensions. For those,
// you must choose the dimensions that you want manipulated
// by supplying e.g. is unchanged and cells
// are centered on the origin so objects don't have to be moved to fit.
//
//
////////////////////////////////////////////////////////////////
// Rotate around a coordinate axis (i.e. in a plane perpendicular to that axis) by angle .
// Read like this: R(p.xz, a) rotates "x towards z".
// This is fast if is a compile-time constant and slower (but still practical) if not.
void pR(inout vec2 p, float a) {
p = cos(a) * p + sin(a) * vec2(p.y, -p.x);
}
// Shortcut for 45-degrees rotation
void pR45(inout vec2 p) {
p = (p + vec2(p.y, -p.x)) * sqrt(0.5);
}
// Repeat space along one axis. Use like this to repeat along the x axis:
//