Signed distance functions in 46 lines of Python
vgel.me
vgel.me
Painting a Character with Maths: https://www.youtube.com/watch?v=8--5LwHRhjk
And plenty more on his website in article form: https://iquilezles.org/articles/
Finally, Shadertoy, a graphics shader playground where you can see (and try!) a lot of this kind of stuff: https://www.shadertoy.com/
EDIT: Translating the article's code over to Javascript with some minor changes yields: https://textshader.com/#default.032468fa7db1ddd9602b9d2516aa...
It just uses marching cubes for triangulation but the SDFs are all numpy'd and the SDF is evaluated in batches on multiple threads so it's relatively fast.
I wrote it because I was a little frustrated with OpenSCAD. SDFs make it trivial to do things like rounded corners, for example.
The shape library looks pretty comprehensive, and the calling conventions are nice!
Not so short as OP's version(46 lines vs 171 lines), but it renders a nice looking image: https://github.com/taichi-dev/taichi-release-tests/blob/mast...
* The distance to the boundary of a circle is abs(sqrt(x*2 + y*2) - radius) ... so as you would expect on the outside, but it's also positive on the inside of the circle
* The distance to the circle is max(0, sqrt(x*2 + y*2) - radius) ... so as you would expect on the outside, but it's 0 on the inside
* The signed distance function of the circle is sqrt(x*2 + y*2) - radius ... so as you would expect on the outside, but it's negative on the inside
Technically the regular distance function would do for the flat looking rendering in the article but you have to calculate the signed distance function on the way there first anyway.
But the signed distance function does have a lot of interesting features you can't recover from the regular distance function [1]. One is that you can recover the surface normal from it by taking the gradient - exactly what was used for getting the normal vector in the article! Another property, not relevant here but very nice, is you can use (point on surface, signed distance) as a coordinate system in the space near to the surface. This gives you a nice formula for the integral of a function in a vicinity of the surface.
[1] https://en.wikipedia.org/wiki/Signed_distance_function#Prope...
That is the distance to a disc.
In fairness, "circle" is fairly often used to refer to the 2d shape, even sometimes in real maths contexts e.g. "squaring the circle" in classical geometry is never referred to as "squaring the disc". In fact I suspect use of "circle" to refer exclusively to the 1d boundary probably comes a very long time after the word first existed in the English language. And even when I was a research mathematician, I never would've corrected someone if it was really clear from the context that they were talking about the 2d shape (which, to be honest, is true of my comment above).
I have seen some of Inigo Quilez tutorials on this subject [1] but I feel like this is a much better introduction for those of us without too much interest in learning GLSL :-). On the other hand, Quilez's tutorials are amazing and show what can be accomplished with this technique!
> In sample(x, y), we only care about the ray of light that passes through (x, y, camera_z). So why bother with all the other rays? We'll shoot the ray backwards! We'll start it at (x, y, camera_z), and at each step, query the SDF to get the distance from the ray's current point to the scene (in any direction). If the distance is less than some threshold, we hit the scene! Otherwise, we can safely "march" the ray forward by whatever distance is returned, since we know the scene is at least that distance away in the forward direction.
Doesn't this assume all the rays are only in the z direction? We are assuming the light source is directly in front of the camera? The code actually only increments z so it's basically only capable of determining occlusion, right?
Exercise for the reader: - Update the sample function, pass the ray direction as argument. - Compute the direction for each ray.
Rays should have a slightly different direction, based on the camera field of view. When you move the camera back or increase the field of view, the donut should get smaller.
Next exercise: move the camera around the donut (but still looking at it).
https://en.wikipedia.org/wiki/Orthographic_projection#/media...
The ray and the light source are two different things. The ray is from the camera to a point in the space. The light source is at a different location; it’s for computing light reflection on the object.
It's kind of weird and confusing to increment z only. It should increment the ray. E.g.
camera_pos = vec3(0.0, 0.0, -5.0) // eye is -5.0 in front of the screen
screen_pos = vec3(x, y, 0.0) // screen plane lays at 0.0 on z-axis.
ray_direction = normalize(screen_pos - camera_pos)
For ray marching along a ray, each step extends the ray by a certain amount. marched_distance = 0.0
for 0 to N
ray_end = camera_pos + marched_distance * ray_direction
ray_end_to_object_distance = sdf_sphere(ray_end)
if (ray_end_to_object_distance < 0.0001)
return ray_end // ray_end hit the object surface
marched_distance += ray_end_to_object_distance
return null // ray never met the object after N steps.
The object's reflection at the returned ray_end point can be computed using the surface normal at the point, the light source position, and the camera_pos.[0] https://raw.githubusercontent.com/pedrovhb/brailliant/main/e... (warning, 8mb unoptimized animated svg)
When making a fixed width font and adding the braille characters, the first priority should be to make all 256 (also the empty one) of them the same width! And a VTE should of course prioritize rendering fixed width as fixed width. How did that get screwed up?
Now that I think of it, I could have used one of the others but made the font fg color the same as the bg color. But that wouldn't look good if then copypasting it into a plain text editor.