A little toolkit for single-quad fragment shader demos
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

791 lines
25 KiB

  1. ////////////////////////////////////////////////////////////////
  2. //
  3. // HG_SDF
  4. //
  5. // GLSL LIBRARY FOR BUILDING SIGNED DISTANCE BOUNDS
  6. //
  7. // version 2016-01-10
  8. //
  9. // Check http://mercury.sexy/hg_sdf for updates
  10. // and usage examples. Send feedback to spheretracing@mercury.sexy.
  11. //
  12. // Brought to you by MERCURY http://mercury.sexy
  13. //
  14. //
  15. //
  16. // Released as Creative Commons Attribution-NonCommercial (CC BY-NC)
  17. //
  18. ////////////////////////////////////////////////////////////////
  19. //
  20. // How to use this:
  21. //
  22. // 1. Build some system to #include glsl files in each other.
  23. // Include this one at the very start. Or just paste everywhere.
  24. // 2. Build a sphere tracer. See those papers:
  25. // * "Sphere Tracing" http://graphics.cs.illinois.edu/sites/default/files/zeno.pdf
  26. // * "Enhanced Sphere Tracing" http://lgdv.cs.fau.de/get/2234
  27. // The Raymnarching Toolbox Thread on pouet can be helpful as well
  28. // http://www.pouet.net/topic.php?which=7931&page=1
  29. // and contains links to many more resources.
  30. // 3. Use the tools in this library to build your distance bound f().
  31. // 4. ???
  32. // 5. Win a compo.
  33. //
  34. // (6. Buy us a beer or a good vodka or something, if you like.)
  35. //
  36. ////////////////////////////////////////////////////////////////
  37. //
  38. // Table of Contents:
  39. //
  40. // * Helper functions and macros
  41. // * Collection of some primitive objects
  42. // * Domain Manipulation operators
  43. // * Object combination operators
  44. //
  45. ////////////////////////////////////////////////////////////////
  46. //
  47. // Why use this?
  48. //
  49. // The point of this lib is that everything is structured according
  50. // to patterns that we ended up using when building geometry.
  51. // It makes it more easy to write code that is reusable and that somebody
  52. // else can actually understand. Especially code on Shadertoy (which seems
  53. // to be what everybody else is looking at for "inspiration") tends to be
  54. // really ugly. So we were forced to do something about the situation and
  55. // release this lib ;)
  56. //
  57. // Everything in here can probably be done in some better way.
  58. // Please experiment. We'd love some feedback, especially if you
  59. // use it in a scene production.
  60. //
  61. // The main patterns for building geometry this way are:
  62. // * Stay Lipschitz continuous. That means: don't have any distance
  63. // gradient larger than 1. Try to be as close to 1 as possible -
  64. // Distances are euclidean distances, don't fudge around.
  65. // Underestimating distances will happen. That's why calling
  66. // it a "distance bound" is more correct. Don't ever multiply
  67. // distances by some value to "fix" a Lipschitz continuity
  68. // violation. The invariant is: each fSomething() function returns
  69. // a correct distance bound.
  70. // * Use very few primitives and combine them as building blocks
  71. // using combine opertors that preserve the invariant.
  72. // * Multiply objects by repeating the domain (space).
  73. // If you are using a loop inside your distance function, you are
  74. // probably doing it wrong (or you are building boring fractals).
  75. // * At right-angle intersections between objects, build a new local
  76. // coordinate system from the two distances to combine them in
  77. // interesting ways.
  78. // * As usual, there are always times when it is best to not follow
  79. // specific patterns.
  80. //
  81. ////////////////////////////////////////////////////////////////
  82. //
  83. // FAQ
  84. //
  85. // Q: Why is there no sphere tracing code in this lib?
  86. // A: Because our system is way too complex and always changing.
  87. // This is the constant part. Also we'd like everyone to
  88. // explore for themselves.
  89. //
  90. // Q: This does not work when I paste it into Shadertoy!!!!
  91. // A: Yes. It is GLSL, not GLSL ES. We like real OpenGL
  92. // because it has way more features and is more likely
  93. // to work compared to browser-based WebGL. We recommend
  94. // you consider using OpenGL for your productions. Most
  95. // of this can be ported easily though.
  96. //
  97. // Q: How do I material?
  98. // A: We recommend something like this:
  99. // Write a material ID, the distance and the local coordinate
  100. // p into some global variables whenever an object's distance is
  101. // smaller than the stored distance. Then, at the end, evaluate
  102. // the material to get color, roughness, etc., and do the shading.
  103. //
  104. // Q: I found an error. Or I made some function that would fit in
  105. // in this lib. Or I have some suggestion.
  106. // A: Awesome! Drop us a mail at spheretracing@mercury.sexy.
  107. //
  108. // Q: Why is this not on github?
  109. // A: Because we were too lazy. If we get bugged about it enough,
  110. // we'll do it.
  111. //
  112. // Q: Your license sucks for me.
  113. // A: Oh. What should we change it to?
  114. //
  115. // Q: I have trouble understanding what is going on with my distances.
  116. // A: Some visualization of the distance field helps. Try drawing a
  117. // plane that you can sweep through your scene with some color
  118. // representation of the distance field at each point and/or iso
  119. // lines at regular intervals. Visualizing the length of the
  120. // gradient (or better: how much it deviates from being equal to 1)
  121. // is immensely helpful for understanding which parts of the
  122. // distance field are broken.
  123. //
  124. ////////////////////////////////////////////////////////////////
  125. ////////////////////////////////////////////////////////////////
  126. //
  127. // HELPER FUNCTIONS/MACROS
  128. //
  129. ////////////////////////////////////////////////////////////////
  130. #define PI 3.14159265
  131. #define TAU (2*PI)
  132. #define PHI (sqrt(5)*0.5 + 0.5)
  133. // Clamp to [0,1] - this operation is free under certain circumstances.
  134. // For further information see
  135. // http://www.humus.name/Articles/Persson_LowLevelThinking.pdf and
  136. // http://www.humus.name/Articles/Persson_LowlevelShaderOptimization.pdf
  137. #define saturate(x) clamp(x, 0, 1)
  138. // Sign function that doesn't return 0
  139. float sgn(float x) {
  140. return (x<0)?-1.:1.;
  141. }
  142. vec2 sgn(vec2 v) {
  143. return vec2((v.x<0)?-1:1, (v.y<0)?-1:1);
  144. }
  145. float square (float x) {
  146. return x*x;
  147. }
  148. vec2 square (vec2 x) {
  149. return x*x;
  150. }
  151. vec3 square (vec3 x) {
  152. return x*x;
  153. }
  154. float lengthSqr(vec3 x) {
  155. return dot(x, x);
  156. }
  157. // Maximum/minumum elements of a vector
  158. float vmax(vec2 v) {
  159. return max(v.x, v.y);
  160. }
  161. float vmax(vec3 v) {
  162. return max(max(v.x, v.y), v.z);
  163. }
  164. float vmax(vec4 v) {
  165. return max(max(v.x, v.y), max(v.z, v.w));
  166. }
  167. float vmin(vec2 v) {
  168. return min(v.x, v.y);
  169. }
  170. float vmin(vec3 v) {
  171. return min(min(v.x, v.y), v.z);
  172. }
  173. float vmin(vec4 v) {
  174. return min(min(v.x, v.y), min(v.z, v.w));
  175. }
  176. ////////////////////////////////////////////////////////////////
  177. //
  178. // PRIMITIVE DISTANCE FUNCTIONS
  179. //
  180. ////////////////////////////////////////////////////////////////
  181. //
  182. // Conventions:
  183. //
  184. // Everything that is a distance function is called fSomething.
  185. // The first argument is always a point in 2 or 3-space called <p>.
  186. // Unless otherwise noted, (if the object has an intrinsic "up"
  187. // side or direction) the y axis is "up" and the object is
  188. // centered at the origin.
  189. //
  190. ////////////////////////////////////////////////////////////////
  191. float fSphere(vec3 p, float r) {
  192. return length(p) - r;
  193. }
  194. // Plane with normal n (n is normalized) at some distance from the origin
  195. float fPlane(vec3 p, vec3 n, float distanceFromOrigin) {
  196. return dot(p, n) + distanceFromOrigin;
  197. }
  198. // Cheap Box: distance to corners is overestimated
  199. float fBoxCheap(vec3 p, vec3 b) { //cheap box
  200. return vmax(abs(p) - b);
  201. }
  202. // Box: correct distance to corners
  203. float fBox(vec3 p, vec3 b) {
  204. vec3 d = abs(p) - b;
  205. return length(max(d, vec3(0))) + vmax(min(d, vec3(0)));
  206. }
  207. // Same as above, but in two dimensions (an endless box)
  208. float fBox2Cheap(vec2 p, vec2 b) {
  209. return vmax(abs(p)-b);
  210. }
  211. float fBox2(vec2 p, vec2 b) {
  212. vec2 d = abs(p) - b;
  213. return length(max(d, vec2(0))) + vmax(min(d, vec2(0)));
  214. }
  215. // Endless "corner"
  216. float fCorner (vec2 p) {
  217. return length(max(p, vec2(0))) + vmax(min(p, vec2(0)));
  218. }
  219. // Blobby ball object. You've probably seen it somewhere. This is not a correct distance bound, beware.
  220. float fBlob(vec3 p) {
  221. p = abs(p);
  222. if (p.x < max(p.y, p.z)) p = p.yzx;
  223. if (p.x < max(p.y, p.z)) p = p.yzx;
  224. float b = max(max(max(
  225. dot(p, normalize(vec3(1, 1, 1))),
  226. dot(p.xz, normalize(vec2(PHI+1, 1)))),
  227. dot(p.yx, normalize(vec2(1, PHI)))),
  228. dot(p.xz, normalize(vec2(1, PHI))));
  229. float l = length(p);
  230. return l - 1.5 - 0.2 * (1.5 / 2)* cos(min(sqrt(1.01 - b / l)*(PI / 0.25), PI));
  231. }
  232. // Cylinder standing upright on the xz plane
  233. float fCylinder(vec3 p, float r, float height) {
  234. float d = length(p.xz) - r;
  235. d = max(d, abs(p.y) - height);
  236. return d;
  237. }
  238. // Capsule: A Cylinder with round caps on both sides
  239. float fCapsule(vec3 p, float r, float c) {
  240. return mix(length(p.xz) - r, length(vec3(p.x, abs(p.y) - c, p.z)) - r, step(c, abs(p.y)));
  241. }
  242. // Distance to line segment between <a> and <b>, used for fCapsule() version 2below
  243. float fLineSegment(vec3 p, vec3 a, vec3 b) {
  244. vec3 ab = b - a;
  245. float t = saturate(dot(p - a, ab) / dot(ab, ab));
  246. return length((ab*t + a) - p);
  247. }
  248. // Capsule version 2: between two end points <a> and <b> with radius r
  249. float fCapsule(vec3 p, vec3 a, vec3 b, float r) {
  250. return fLineSegment(p, a, b) - r;
  251. }
  252. // Torus in the XZ-plane
  253. float fTorus(vec3 p, float smallRadius, float largeRadius) {
  254. return length(vec2(length(p.xz) - largeRadius, p.y)) - smallRadius;
  255. }
  256. // A circle line. Can also be used to make a torus by subtracting the smaller radius of the torus.
  257. float fCircle(vec3 p, float r) {
  258. float l = length(p.xz) - r;
  259. return length(vec2(p.y, l));
  260. }
  261. // A circular disc with no thickness (i.e. a cylinder with no height).
  262. // Subtract some value to make a flat disc with rounded edge.
  263. float fDisc(vec3 p, float r) {
  264. float l = length(p.xz) - r;
  265. return l < 0 ? abs(p.y) : length(vec2(p.y, l));
  266. }
  267. // Hexagonal prism, circumcircle variant
  268. float fHexagonCircumcircle(vec3 p, vec2 h) {
  269. vec3 q = abs(p);
  270. return max(q.y - h.y, max(q.x*sqrt(3)*0.5 + q.z*0.5, q.z) - h.x);
  271. //this is mathematically equivalent to this line, but less efficient:
  272. //return max(q.y - h.y, max(dot(vec2(cos(PI/3), sin(PI/3)), q.zx), q.z) - h.x);
  273. }
  274. // Hexagonal prism, incircle variant
  275. float fHexagonIncircle(vec3 p, vec2 h) {
  276. return fHexagonCircumcircle(p, vec2(h.x*sqrt(3)*0.5, h.y));
  277. }
  278. // Cone with correct distances to tip and base circle. Y is up, 0 is in the middle of the base.
  279. float fCone(vec3 p, float radius, float height) {
  280. vec2 q = vec2(length(p.xz), p.y);
  281. vec2 tip = q - vec2(0, height);
  282. vec2 mantleDir = normalize(vec2(height, radius));
  283. float mantle = dot(tip, mantleDir);
  284. float d = max(mantle, -q.y);
  285. float projected = dot(tip, vec2(mantleDir.y, -mantleDir.x));
  286. // distance to tip
  287. if ((q.y > height) && (projected < 0)) {
  288. d = max(d, length(tip));
  289. }
  290. // distance to base ring
  291. if ((q.x > radius) && (projected > length(vec2(height, radius)))) {
  292. d = max(d, length(q - vec2(radius, 0)));
  293. }
  294. return d;
  295. }
  296. //
  297. // "Generalized Distance Functions" by Akleman and Chen.
  298. // see the Paper at https://www.viz.tamu.edu/faculty/ergun/research/implicitmodeling/papers/sm99.pdf
  299. //
  300. // This set of constants is used to construct a large variety of geometric primitives.
  301. // Indices are shifted by 1 compared to the paper because we start counting at Zero.
  302. // Some of those are slow whenever a driver decides to not unroll the loop,
  303. // which seems to happen for fIcosahedron und fTruncatedIcosahedron on nvidia 350.12 at least.
  304. // Specialized implementations can well be faster in all cases.
  305. //
  306. const vec3 GDFVectors[19] = vec3[](
  307. normalize(vec3(1, 0, 0)),
  308. normalize(vec3(0, 1, 0)),
  309. normalize(vec3(0, 0, 1)),
  310. normalize(vec3(1, 1, 1 )),
  311. normalize(vec3(-1, 1, 1)),
  312. normalize(vec3(1, -1, 1)),
  313. normalize(vec3(1, 1, -1)),
  314. normalize(vec3(0, 1, PHI+1)),
  315. normalize(vec3(0, -1, PHI+1)),
  316. normalize(vec3(PHI+1, 0, 1)),
  317. normalize(vec3(-PHI-1, 0, 1)),
  318. normalize(vec3(1, PHI+1, 0)),
  319. normalize(vec3(-1, PHI+1, 0)),
  320. normalize(vec3(0, PHI, 1)),
  321. normalize(vec3(0, -PHI, 1)),
  322. normalize(vec3(1, 0, PHI)),
  323. normalize(vec3(-1, 0, PHI)),
  324. normalize(vec3(PHI, 1, 0)),
  325. normalize(vec3(-PHI, 1, 0))
  326. );
  327. // Version with variable exponent.
  328. // This is slow and does not produce correct distances, but allows for bulging of objects.
  329. float fGDF(vec3 p, float r, float e, int begin, int end) {
  330. float d = 0;
  331. for (int i = begin; i <= end; ++i)
  332. d += pow(abs(dot(p, GDFVectors[i])), e);
  333. return pow(d, 1/e) - r;
  334. }
  335. // Version with without exponent, creates objects with sharp edges and flat faces
  336. float fGDF(vec3 p, float r, int begin, int end) {
  337. float d = 0;
  338. for (int i = begin; i <= end; ++i)
  339. d = max(d, abs(dot(p, GDFVectors[i])));
  340. return d - r;
  341. }
  342. // Primitives follow:
  343. float fOctahedron(vec3 p, float r, float e) {
  344. return fGDF(p, r, e, 3, 6);
  345. }
  346. float fDodecahedron(vec3 p, float r, float e) {
  347. return fGDF(p, r, e, 13, 18);
  348. }
  349. float fIcosahedron(vec3 p, float r, float e) {
  350. return fGDF(p, r, e, 3, 12);
  351. }
  352. float fTruncatedOctahedron(vec3 p, float r, float e) {
  353. return fGDF(p, r, e, 0, 6);
  354. }
  355. float fTruncatedIcosahedron(vec3 p, float r, float e) {
  356. return fGDF(p, r, e, 3, 18);
  357. }
  358. float fOctahedron(vec3 p, float r) {
  359. return fGDF(p, r, 3, 6);
  360. }
  361. float fDodecahedron(vec3 p, float r) {
  362. return fGDF(p, r, 13, 18);
  363. }
  364. float fIcosahedron(vec3 p, float r) {
  365. return fGDF(p, r, 3, 12);
  366. }
  367. float fTruncatedOctahedron(vec3 p, float r) {
  368. return fGDF(p, r, 0, 6);
  369. }
  370. float fTruncatedIcosahedron(vec3 p, float r) {
  371. return fGDF(p, r, 3, 18);
  372. }
  373. ////////////////////////////////////////////////////////////////
  374. //
  375. // DOMAIN MANIPULATION OPERATORS
  376. //
  377. ////////////////////////////////////////////////////////////////
  378. //
  379. // Conventions:
  380. //
  381. // Everything that modifies the domain is named pSomething.
  382. //
  383. // Many operate only on a subset of the three dimensions. For those,
  384. // you must choose the dimensions that you want manipulated
  385. // by supplying e.g. <p.x> or <p.zx>
  386. //
  387. // <inout p> is always the first argument and modified in place.
  388. //
  389. // Many of the operators partition space into cells. An identifier
  390. // or cell index is returned, if possible. This return value is
  391. // intended to be optionally used e.g. as a random seed to change
  392. // parameters of the distance functions inside the cells.
  393. //
  394. // Unless stated otherwise, for cell index 0, <p> is unchanged and cells
  395. // are centered on the origin so objects don't have to be moved to fit.
  396. //
  397. //
  398. ////////////////////////////////////////////////////////////////
  399. // Rotate around a coordinate axis (i.e. in a plane perpendicular to that axis) by angle <a>.
  400. // Read like this: R(p.xz, a) rotates "x towards z".
  401. // This is fast if <a> is a compile-time constant and slower (but still practical) if not.
  402. void pR(inout vec2 p, float a) {
  403. p = cos(a)*p + sin(a)*vec2(p.y, -p.x);
  404. }
  405. // Shortcut for 45-degrees rotation
  406. void pR45(inout vec2 p) {
  407. p = (p + vec2(p.y, -p.x))*sqrt(0.5);
  408. }
  409. // Repeat space along one axis. Use like this to repeat along the x axis:
  410. // <float cell = pMod1(p.x,5);> - using the return value is optional.
  411. float pMod1(inout float p, float size) {
  412. float halfsize = size*0.5;
  413. float c = floor((p + halfsize)/size);
  414. p = mod(p + halfsize, size) - halfsize;
  415. return c;
  416. }
  417. // Same, but mirror every second cell so they match at the boundaries
  418. float pModMirror1(inout float p, float size) {
  419. float halfsize = size*0.5;
  420. float c = floor((p + halfsize)/size);
  421. p = mod(p + halfsize,size) - halfsize;
  422. p *= mod(c, 2.0)*2 - 1;
  423. return c;
  424. }
  425. // Repeat the domain only in positive direction. Everything in the negative half-space is unchanged.
  426. float pModSingle1(inout float p, float size) {
  427. float halfsize = size*0.5;
  428. float c = floor((p + halfsize)/size);
  429. if (p >= 0)
  430. p = mod(p + halfsize, size) - halfsize;
  431. return c;
  432. }
  433. // Repeat only a few times: from indices <start> to <stop> (similar to above, but more flexible)
  434. float pModInterval1(inout float p, float size, float start, float stop) {
  435. float halfsize = size*0.5;
  436. float c = floor((p + halfsize)/size);
  437. p = mod(p+halfsize, size) - halfsize;
  438. if (c > stop) { //yes, this might not be the best thing numerically.
  439. p += size*(c - stop);
  440. c = stop;
  441. }
  442. if (c <start) {
  443. p += size*(c - start);
  444. c = start;
  445. }
  446. return c;
  447. }
  448. // Repeat around the origin by a fixed angle.
  449. // For easier use, num of repetitions is use to specify the angle.
  450. float pModPolar(inout vec2 p, float repetitions) {
  451. float angle = 2*PI/repetitions;
  452. float a = atan(p.y, p.x) + angle/2.;
  453. float r = length(p);
  454. float c = floor(a/angle);
  455. a = mod(a,angle) - angle/2.;
  456. p = vec2(cos(a), sin(a))*r;
  457. // For an odd number of repetitions, fix cell index of the cell in -x direction
  458. // (cell index would be e.g. -5 and 5 in the two halves of the cell):
  459. if (abs(c) >= (repetitions/2)) c = abs(c);
  460. return c;
  461. }
  462. // Repeat in two dimensions
  463. vec2 pMod2(inout vec2 p, vec2 size) {
  464. vec2 c = floor((p + size*0.5)/size);
  465. p = mod(p + size*0.5,size) - size*0.5;
  466. return c;
  467. }
  468. // Same, but mirror every second cell so all boundaries match
  469. vec2 pModMirror2(inout vec2 p, vec2 size) {
  470. vec2 halfsize = size*0.5;
  471. vec2 c = floor((p + halfsize)/size);
  472. p = mod(p + halfsize, size) - halfsize;
  473. p *= mod(c,vec2(2))*2 - vec2(1);
  474. return c;
  475. }
  476. // Same, but mirror every second cell at the diagonal as well
  477. vec2 pModGrid2(inout vec2 p, vec2 size) {
  478. vec2 c = floor((p + size*0.5)/size);
  479. p = mod(p + size*0.5, size) - size*0.5;
  480. p *= mod(c,vec2(2))*2 - vec2(1);
  481. p -= size/2;
  482. if (p.x > p.y) p.xy = p.yx;
  483. return floor(c/2);
  484. }
  485. // Repeat in three dimensions
  486. vec3 pMod3(inout vec3 p, vec3 size) {
  487. vec3 c = floor((p + size*0.5)/size);
  488. p = mod(p + size*0.5, size) - size*0.5;
  489. return c;
  490. }
  491. // Mirror at an axis-aligned plane which is at a specified distance <dist> from the origin.
  492. float pMirror (inout float p, float dist) {
  493. float s = sgn(p);
  494. p = abs(p)-dist;
  495. return s;
  496. }
  497. // Mirror in both dimensions and at the diagonal, yielding one eighth of the space.
  498. // translate by dist before mirroring.
  499. vec2 pMirrorOctant (inout vec2 p, vec2 dist) {
  500. vec2 s = sgn(p);
  501. pMirror(p.x, dist.x);
  502. pMirror(p.y, dist.y);
  503. if (p.y > p.x)
  504. p.xy = p.yx;
  505. return s;
  506. }
  507. // Reflect space at a plane
  508. float pReflect(inout vec3 p, vec3 planeNormal, float offset) {
  509. float t = dot(p, planeNormal)+offset;
  510. if (t < 0) {
  511. p = p - (2*t)*planeNormal;
  512. }
  513. return sgn(t);
  514. }
  515. ////////////////////////////////////////////////////////////////
  516. //
  517. // OBJECT COMBINATION OPERATORS
  518. //
  519. ////////////////////////////////////////////////////////////////
  520. //
  521. // We usually need the following boolean operators to combine two objects:
  522. // Union: OR(a,b)
  523. // Intersection: AND(a,b)
  524. // Difference: AND(a,!b)
  525. // (a and b being the distances to the objects).
  526. //
  527. // The trivial implementations are min(a,b) for union, max(a,b) for intersection
  528. // and max(a,-b) for difference. To combine objects in more interesting ways to
  529. // produce rounded edges, chamfers, stairs, etc. instead of plain sharp edges we
  530. // can use combination operators. It is common to use some kind of "smooth minimum"
  531. // instead of min(), but we don't like that because it does not preserve Lipschitz
  532. // continuity in many cases.
  533. //
  534. // Naming convention: since they return a distance, they are called fOpSomething.
  535. // The different flavours usually implement all the boolean operators above
  536. // and are called fOpUnionRound, fOpIntersectionRound, etc.
  537. //
  538. // The basic idea: Assume the object surfaces intersect at a right angle. The two
  539. // distances <a> and <b> constitute a new local two-dimensional coordinate system
  540. // with the actual intersection as the origin. In this coordinate system, we can
  541. // evaluate any 2D distance function we want in order to shape the edge.
  542. //
  543. // The operators below are just those that we found useful or interesting and should
  544. // be seen as examples. There are infinitely more possible operators.
  545. //
  546. // They are designed to actually produce correct distances or distance bounds, unlike
  547. // popular "smooth minimum" operators, on the condition that the gradients of the two
  548. // SDFs are at right angles. When they are off by more than 30 degrees or so, the
  549. // Lipschitz condition will no longer hold (i.e. you might get artifacts). The worst
  550. // case is parallel surfaces that are close to each other.
  551. //
  552. // Most have a float argument <r> to specify the radius of the feature they represent.
  553. // This should be much smaller than the object size.
  554. //
  555. // Some of them have checks like "if ((-a < r) && (-b < r))" that restrict
  556. // their influence (and computation cost) to a certain area. You might
  557. // want to lift that restriction or enforce it. We have left it as comments
  558. // in some cases.
  559. //
  560. // usage example:
  561. //
  562. // float fTwoBoxes(vec3 p) {
  563. // float box0 = fBox(p, vec3(1));
  564. // float box1 = fBox(p-vec3(1), vec3(1));
  565. // return fOpUnionChamfer(box0, box1, 0.2);
  566. // }
  567. //
  568. ////////////////////////////////////////////////////////////////
  569. // The "Chamfer" flavour makes a 45-degree chamfered edge (the diagonal of a square of size <r>):
  570. float fOpUnionChamfer(float a, float b, float r) {
  571. return min(min(a, b), (a - r + b)*sqrt(0.5));
  572. }
  573. // Intersection has to deal with what is normally the inside of the resulting object
  574. // when using union, which we normally don't care about too much. Thus, intersection
  575. // implementations sometimes differ from union implementations.
  576. float fOpIntersectionChamfer(float a, float b, float r) {
  577. return max(max(a, b), (a + r + b)*sqrt(0.5));
  578. }
  579. // Difference can be built from Intersection or Union:
  580. float fOpDifferenceChamfer (float a, float b, float r) {
  581. return fOpIntersectionChamfer(a, -b, r);
  582. }
  583. // The "Round" variant uses a quarter-circle to join the two objects smoothly:
  584. float fOpUnionRound(float a, float b, float r) {
  585. vec2 u = max(vec2(r - a,r - b), vec2(0));
  586. return max(r, min (a, b)) - length(u);
  587. }
  588. float fOpIntersectionRound(float a, float b, float r) {
  589. vec2 u = max(vec2(r + a,r + b), vec2(0));
  590. return min(-r, max (a, b)) + length(u);
  591. }
  592. float fOpDifferenceRound (float a, float b, float r) {
  593. return fOpIntersectionRound(a, -b, r);
  594. }
  595. // The "Columns" flavour makes n-1 circular columns at a 45 degree angle:
  596. float fOpUnionColumns(float a, float b, float r, float n) {
  597. if ((a < r) && (b < r)) {
  598. vec2 p = vec2(a, b);
  599. float columnradius = r*sqrt(2)/((n-1)*2+sqrt(2));
  600. pR45(p);
  601. p.x -= sqrt(2)/2*r;
  602. p.x += columnradius*sqrt(2);
  603. if (mod(n,2) == 1) {
  604. p.y += columnradius;
  605. }
  606. // At this point, we have turned 45 degrees and moved at a point on the
  607. // diagonal that we want to place the columns on.
  608. // Now, repeat the domain along this direction and place a circle.
  609. pMod1(p.y, columnradius*2);
  610. float result = length(p) - columnradius;
  611. result = min(result, p.x);
  612. result = min(result, a);
  613. return min(result, b);
  614. } else {
  615. return min(a, b);
  616. }
  617. }
  618. float fOpDifferenceColumns(float a, float b, float r, float n) {
  619. a = -a;
  620. float m = min(a, b);
  621. //avoid the expensive computation where not needed (produces discontinuity though)
  622. if ((a < r) && (b < r)) {
  623. vec2 p = vec2(a, b);
  624. float columnradius = r*sqrt(2)/n/2.0;
  625. columnradius = r*sqrt(2)/((n-1)*2+sqrt(2));
  626. pR45(p);
  627. p.y += columnradius;
  628. p.x -= sqrt(2)/2*r;
  629. p.x += -columnradius*sqrt(2)/2;
  630. if (mod(n,2) == 1) {
  631. p.y += columnradius;
  632. }
  633. pMod1(p.y,columnradius*2);
  634. float result = -length(p) + columnradius;
  635. result = max(result, p.x);
  636. result = min(result, a);
  637. return -min(result, b);
  638. } else {
  639. return -m;
  640. }
  641. }
  642. float fOpIntersectionColumns(float a, float b, float r, float n) {
  643. return fOpDifferenceColumns(a,-b,r, n);
  644. }
  645. // The "Stairs" flavour produces n-1 steps of a staircase:
  646. // much less stupid version by paniq
  647. float fOpUnionStairs(float a, float b, float r, float n) {
  648. float s = r/n;
  649. float u = b-r;
  650. return min(min(a,b), 0.5 * (u + a + abs ((mod (u - a + s, 2 * s)) - s)));
  651. }
  652. // We can just call Union since stairs are symmetric.
  653. float fOpIntersectionStairs(float a, float b, float r, float n) {
  654. return -fOpUnionStairs(-a, -b, r, n);
  655. }
  656. float fOpDifferenceStairs(float a, float b, float r, float n) {
  657. return -fOpUnionStairs(-a, b, r, n);
  658. }
  659. // Similar to fOpUnionRound, but more lipschitz-y at acute angles
  660. // (and less so at 90 degrees). Useful when fudging around too much
  661. // by MediaMolecule, from Alex Evans' siggraph slides
  662. float fOpUnionSoft(float a, float b, float r) {
  663. float e = max(r - abs(a - b), 0);
  664. return min(a, b) - e*e*0.25/r;
  665. }
  666. // produces a cylindical pipe that runs along the intersection.
  667. // No objects remain, only the pipe. This is not a boolean operator.
  668. float fOpPipe(float a, float b, float r) {
  669. return length(vec2(a, b)) - r;
  670. }
  671. // first object gets a v-shaped engraving where it intersect the second
  672. float fOpEngrave(float a, float b, float r) {
  673. return max(a, (a + r - abs(b))*sqrt(0.5));
  674. }
  675. // first object gets a capenter-style groove cut out
  676. float fOpGroove(float a, float b, float ra, float rb) {
  677. return max(a, min(a + ra, rb - abs(b)));
  678. }
  679. // first object gets a capenter-style tongue attached
  680. float fOpTongue(float a, float b, float ra, float rb) {
  681. return min(a, max(a - ra, abs(b) - rb));
  682. }