├── .gitignore ├── .npmignore ├── LICENSE.md ├── README.md ├── demo ├── index.frag ├── index.js ├── index.vert └── lookat.glsl ├── exp.glsl ├── index.html ├── package.json ├── poly.glsl └── pow.glsl /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | .DS_Store 4 | bundle.js 5 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | .DS_Store 4 | bundle.js 5 | index.html 6 | test 7 | test.js 8 | demo 9 | example 10 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | Copyright (c) 2014 [stackgl](http://github.com/stackgl/) contributors 5 | 6 | *stackgl contributors listed at * 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 11 | 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # glsl-smooth-min 2 | 3 | [![stable](http://badges.github.io/stability-badges/dist/stable.svg)](http://github.com/badges/stability-badges) 4 | 5 | Smooth minimum functions for GLSL, sourced from 6 | [Iñigo Quílez's article](http://www.iquilezles.org/www/articles/smin/smin.htm). 7 | 8 | Particularly useful when doing Shadertoy-style raymarching with distance 9 | fields: you can smoothly blend between two volumes, instead of doing a 10 | hard union with `min(a, b)`. 11 | 12 | **[Check it out in action](http://stack.gl/glsl-smooth-min)** 13 | 14 | ![](http://imgur.com/H7vqNm4.png) 15 | 16 | ## Usage 17 | 18 | [![NPM](https://nodei.co/npm/glsl-smooth-min.png)](https://nodei.co/npm/glsl-smooth-min/) 19 | 20 | ### `smin(float a, float b, float k)` 21 | 22 | Blends smoothly between `a` and `b`, with a smoothing amount 23 | determined by the value of `k`. For example: 24 | 25 | ``` glsl 26 | #pragma glslify: smin = require(glsl-smooth-min) 27 | 28 | float doModel(vec3 position) { 29 | // Take two sphere volumes 30 | float a = length(position + 0.5) - 0.7; 31 | float b = length(position - 0.5) - 0.7; 32 | 33 | // And smooth them together 34 | return smin(a, b, 0.8); 35 | } 36 | ``` 37 | 38 | There are three variants of this function available, all with the 39 | same function signature: 40 | 41 | ``` glsl 42 | #pragma glslify: poly = require(glsl-smooth-min/poly) 43 | #pragma glslify: pow = require(glsl-smooth-min/pow) 44 | #pragma glslify: exp = require(glsl-smooth-min/exp) 45 | 46 | // Exports `poly` by default 47 | #pragma glslify: poly = require(glsl-smooth-min) 48 | ``` 49 | 50 | Each of these variants differ somewhat in their results, 51 | and some are more appropriate in specific situations: 52 | 53 | > These three functions produce smooth results, with different qualities. The three accept a paramter *k* that controls the radious/distance of the smoothness. From these three, probably the polynomial is the fastest, and also the easiest to control, for *k* maps directly to a blending band size/distance. Unlike the other two, it probably suffers from second order discontinuities (derivatives), but visually is pleasing enough for most applications. 54 | 55 | ## Contributing 56 | 57 | See [stackgl/contributing](https://github.com/stackgl/contributing) for details. 58 | 59 | ## License 60 | 61 | MIT. See [LICENSE.md](http://github.com/stackgl/glsl-smooth-min/blob/master/LICENSE.md) for details. 62 | -------------------------------------------------------------------------------- /demo/index.frag: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | // The majority of this shader is sourced from: 4 | // https://www.shadertoy.com/view/ldfSWs 5 | // 6 | // Courtesy again of Iñigo Quílez. 7 | 8 | #pragma glslify: lookAt = require(./lookat) 9 | #pragma glslify: frame = require(glsl-square-frame) 10 | #pragma glslify: smin = require(../) 11 | 12 | uniform float uTime; 13 | uniform vec2 uSize; 14 | 15 | float doModel(vec3 position) { 16 | // Determine the position of two spheres 17 | vec3 apos = position + vec3(sin(uTime * 0.8), cos(uTime * 0.71), 0.0); 18 | vec3 bpos = position - vec3(0.0, sin(uTime * 0.7), cos(uTime * 0.53)); 19 | 20 | // Determine their respective distances from 21 | // the camera along the ray 22 | float a = length(apos) - 0.7; 23 | float b = length(bpos) - 0.7; 24 | 25 | // Smooth the two spheres together 26 | return smin(a, b, 0.8); 27 | } 28 | 29 | vec3 doMaterial(in vec3 position, in vec3 normal) { 30 | return vec3(0.2, 0.07, 0.01); 31 | } 32 | 33 | vec3 doLighting( 34 | in vec3 pos, 35 | in vec3 normal, 36 | in vec3 ray, 37 | in float dist, 38 | in vec3 materialColor 39 | ) { 40 | vec3 color = vec3(0.0); 41 | 42 | vec3 keyAngle = normalize(vec3(1.0,0.7,0.9)); 43 | float keyAmplitude = max(dot(normal, keyAngle),0.0); 44 | 45 | color += keyAmplitude * vec3(4.00); 46 | color += 2.0; 47 | 48 | return materialColor * color; 49 | } 50 | 51 | vec3 calcNormal( in vec3 pos ) { 52 | const float eps = 0.002; 53 | 54 | const vec3 v1 = vec3( 1.0,-1.0,-1.0); 55 | const vec3 v2 = vec3(-1.0,-1.0, 1.0); 56 | const vec3 v3 = vec3(-1.0, 1.0,-1.0); 57 | const vec3 v4 = vec3( 1.0, 1.0, 1.0); 58 | 59 | return normalize( 60 | v1 * doModel(pos + v1 * eps) 61 | + v2 * doModel(pos + v2 * eps) 62 | + v3 * doModel(pos + v3 * eps) 63 | + v4 * doModel(pos + v4 * eps) 64 | ); 65 | } 66 | 67 | float calcIntersection(in vec3 camPosition, in vec3 ray) { 68 | const float maxDistance = 20.0; 69 | const float stepDistance = 0.001; 70 | 71 | float h = stepDistance; 72 | float t = 0.0; 73 | float dist = -1.0; 74 | 75 | for (int i = 0; i < 90; i++) { 76 | if (h < stepDistance || t > maxDistance) break; 77 | h = doModel(camPosition + ray * t); 78 | t += h; 79 | } 80 | 81 | if (t < maxDistance) dist = t; 82 | 83 | return dist; 84 | } 85 | 86 | void main() { 87 | vec2 pixPos = frame(uSize, gl_FragCoord.xy); 88 | float camRotate = 0.3 * uTime; 89 | vec3 camPos = vec3(3.5 * sin(camRotate), 1.0, 3.5 * cos(camRotate)); 90 | vec3 camTarget = vec3(0.0); 91 | mat3 camMatrix = lookAt(camPos, camTarget, 0.0); 92 | float lensLength = 1.5; 93 | 94 | vec3 ray = normalize(camMatrix * vec3(pixPos, lensLength)); 95 | vec3 color = vec3(0.0); 96 | 97 | float dist = calcIntersection(camPos, ray); 98 | 99 | // If the ray hits, update color for surface 100 | if (dist > -0.5) { 101 | vec3 pos = camPos + dist * ray; 102 | vec3 nor = calcNormal(pos); 103 | vec3 mal = doMaterial(pos, nor); 104 | 105 | color = doLighting(pos, nor, ray, dist, mal); 106 | } 107 | 108 | gl_FragColor = vec4(color, 1); 109 | } 110 | -------------------------------------------------------------------------------- /demo/index.js: -------------------------------------------------------------------------------- 1 | var glslify = require('glslify') 2 | var Toy = require('gl-toy') 3 | 4 | var size = new Float32Array(2) 5 | var start = Date.now() 6 | var shader = glslify({ 7 | vert: './index.vert', 8 | frag: './index.frag' 9 | }) 10 | 11 | Toy(shader, function(gl, shader) { 12 | size[0] = gl.drawingBufferWidth 13 | size[1] = gl.drawingBufferHeight 14 | 15 | shader.uniforms.uSize = size 16 | shader.uniforms.uTime = (Date.now() - start) / 1000 17 | }) 18 | -------------------------------------------------------------------------------- /demo/index.vert: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | attribute vec2 aPosition; 4 | 5 | void main() { 6 | gl_Position = vec4(aPosition, 1, 1); 7 | } 8 | -------------------------------------------------------------------------------- /demo/lookat.glsl: -------------------------------------------------------------------------------- 1 | mat3 calcLookAtMatrix(in vec3 camPosition, in vec3 camTarget, in float roll) { 2 | vec3 ww = normalize(camTarget - camPosition); 3 | vec3 uu = normalize(cross(ww, vec3(sin(roll), cos(roll), 0.0))); 4 | vec3 vv = normalize(cross(uu, ww)); 5 | 6 | return mat3(uu, vv, ww); 7 | } 8 | 9 | #pragma glslify: export(calcLookAtMatrix) 10 | -------------------------------------------------------------------------------- /exp.glsl: -------------------------------------------------------------------------------- 1 | float smin(float a, float b, float k) { 2 | float res = exp(-k * a) + exp(-k * b); 3 | return -log(res) / k; 4 | } 5 | 6 | #pragma glslify: export(smin) 7 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | glsl-smooth-min 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "glsl-smooth-min", 3 | "version": "1.0.0", 4 | "description": "Smooth minimum functions for GLSL", 5 | "main": "poly.glsl", 6 | "license": "MIT", 7 | "scripts": { 8 | "start": "wzrd demo/index.js:bundle.js", 9 | "bundle": "browserify demo/index.js -o bundle.js" 10 | }, 11 | "author": { 12 | "name": "Hugh Kennedy", 13 | "email": "hughskennedy@gmail.com", 14 | "url": "http://hughsk.io/" 15 | }, 16 | "dependencies": {}, 17 | "devDependencies": { 18 | "browserify": "^8.1.3", 19 | "gl-toy": "^1.0.0", 20 | "glsl-square-frame": "^1.0.1", 21 | "glslify": "^1.6.0", 22 | "wzrd": "^1.2.1" 23 | }, 24 | "repository": { 25 | "type": "git", 26 | "url": "git://github.com/stackgl/glsl-smooth-min.git" 27 | }, 28 | "keywords": [ 29 | "ecosystem:stackgl", 30 | "smin", 31 | "smoothmin", 32 | "smooth minimum", 33 | "min", 34 | "raymarch", 35 | "raytrace", 36 | "glsl", 37 | "shader" 38 | ], 39 | "homepage": "https://github.com/stackgl/glsl-smooth-min", 40 | "bugs": { 41 | "url": "https://github.com/stackgl/glsl-smooth-min/issues" 42 | }, 43 | "browserify": { 44 | "transform": [ 45 | "glslify" 46 | ] 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /poly.glsl: -------------------------------------------------------------------------------- 1 | float smin(float a, float b, float k) { 2 | float h = clamp(0.5 + 0.5 * (b - a) / k, 0.0, 1.0); 3 | return mix(b, a, h) - k * h * (1.0 - h); 4 | } 5 | 6 | #pragma glslify: export(smin) 7 | -------------------------------------------------------------------------------- /pow.glsl: -------------------------------------------------------------------------------- 1 | float smin(float a, float b, float k) { 2 | a = pow(a, k); 3 | b = pow(b, k); 4 | return pow((a * b) / (a + b), 1.0 / k); 5 | } 6 | 7 | #pragma glslify: export(smin) 8 | --------------------------------------------------------------------------------