Created
November 2, 2020 18:46
-
-
Save lsh/b0eeaaa498d20765770a6e86fcae483e to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
### A Pluto.jl notebook ### | |
# v0.12.6 | |
using Markdown | |
using InteractiveUtils | |
# This Pluto notebook uses @bind for interactivity. When running this notebook outside of Pluto, the following 'mock version' of @bind gives bound variables a default value (instead of an error). | |
macro bind(def, element) | |
quote | |
local el = $(esc(element)) | |
global $(esc(def)) = Core.applicable(Base.get, el) ? Base.get(el) : missing | |
el | |
end | |
end | |
# ╔═╡ 03e6d8a4-1c0d-11eb-23c7-57525308d771 | |
begin | |
using Colors | |
using ImageShow | |
# On MacOS I also neededed QuartzImageIO installed | |
#using Pkg;Pkg.add("QuartzImageIO") | |
end | |
# ╔═╡ cc2522f4-1d33-11eb-386f-f9530f5a9fbb | |
using Pipe | |
# ╔═╡ df7c6b84-1d33-11eb-0a40-112e53d1896d | |
md""" | |
# Distance Functions in Julia | |
I'm a big fan of [The Book of Shaders](https://thebookofshaders.com/). It was a driving force in helping me understand using algorithms for parallelization. I recently realized Julia has a ton of utilities that allow for working in a similar fashion to how I code fragment shaders. Below you'll see me play around with some distance functions like those found in the book. | |
—Lukas ([gh](https://github.com/lsh)|[tw](https://twitter.com/lukashermann_)) | |
""" | |
# ╔═╡ 9f27ea9c-1d34-11eb-00ce-c133f2dac376 | |
md""" | |
The [Colors.jl](https://github.com/JuliaGraphics/Colors.jl) package is gonna do most of the heavy lifting here. It has a few utilities that make it incredibly easy to convert a matrix into an image. | |
""" | |
# ╔═╡ 395d9aae-1c7b-11eb-1c1f-e7288114c806 | |
begin | |
const w = 256 | |
const h = 256 | |
end | |
# ╔═╡ d57981aa-1d34-11eb-14fa-510a71100a21 | |
md"""For example, we can make a series of random pixels by [broadcasting](https://docs.julialang.org/en/v1/manual/arrays/#Broadcasting) the Gray constructor across a matrix""" | |
# ╔═╡ 09aa9cfc-1d35-11eb-1752-65d28d61158f | |
Gray.(rand(w,h)) | |
# ╔═╡ 8a6e0e34-1d35-11eb-3c7d-b92084530089 | |
md""" | |
To use this functionality like we do in shaders, we need to generate UV coordinates. We divide by the width and height so that our coordinates range from $$[0,1]$$. | |
_Note: if this wasn't a square image you'd have to aspect adjust_ | |
""" | |
# ╔═╡ 56806ab6-1c81-11eb-019d-75b4a853f71c | |
st = [(i/h,j/w) for i in 0:h, j in 0:w] | |
# ╔═╡ 17c13afc-1d36-11eb-1591-cdb9bc262011 | |
md""" | |
Now we can access the coordinates with `st[1]` for the x position and `st[2]` for the y position. | |
We can use the same method of visualization as above to see the coordinates change. | |
""" | |
# ╔═╡ 64145664-1d36-11eb-043e-dd63013bac25 | |
(c -> Gray(c[1])).(st) | |
# ╔═╡ a01df1d8-1d36-11eb-39e5-433d81f6968b | |
(c -> Gray(c[2])).(st) | |
# ╔═╡ b2f8c620-1d36-11eb-3cc0-13d285d57b45 | |
md""" | |
Above you'll notice that we're using [anonymous functions](https://docs.julialang.org/en/v1/manual/functions/#man-anonymous-functions) to exctract the x and y positions. We then broadcast this function across the matrix. | |
To view these together, we can use the RGB function. We'll just set the blue value to `1`. | |
""" | |
# ╔═╡ a2b22428-1c7d-11eb-2d45-31d95434244d | |
(c -> RGB(c[1], c[2], 1)).(st) | |
# ╔═╡ 3927ba58-1d37-11eb-0eb3-85f77dc09be9 | |
md""" | |
Now to draw shapes with distance functions we need a way to actually measure distance. If we want to draw a circle for instance, we measure the distance of points from a radius. | |
$$c = \sqrt{x^2+y^2}$$ | |
Here we'll define it so it can take both an `xy` pair or `x,y` | |
""" | |
# ╔═╡ 460c8e1c-1c7c-11eb-1873-2da2ead2fb32 | |
begin | |
length(x::Float64, y::Float64)::Float64 = sqrt(x^2 + y^2) | |
length(xy::Tuple{Float64,Float64})::Float64 = sqrt(xy[1]^2 + xy[2]^2) | |
end | |
# ╔═╡ 6454452e-1d38-11eb-2ab6-571f07accff5 | |
md""" | |
Now let's try it out! | |
""" | |
# ╔═╡ 6ceb28da-1c78-11eb-0e10-2dd217346811 | |
Gray.(length.((c -> c.-0.5).(st))) | |
# ╔═╡ b341c97c-1d38-11eb-0dd8-8dcc6a46287d | |
md"""We subtract 0.5 from `st` so that it's range is $$[-0.5, 0.5]$$. This means the length is compared to the center. You could change it to just`Gray.(length.(st))`, but it would be measuring from the top left corner instead. | |
Another useful function for drawing with shapes is `step`, which takes a value and a threshold, then sets the value to 0 or 1 depending on if it's greater than that threshold. | |
""" | |
# ╔═╡ dfbc6ece-1c7c-11eb-0558-6525bd0cf44a | |
step(val::Float64, thresh::Float64)::Float64 = val < thresh ? zero(val) : one(val) | |
# ╔═╡ 7c9b8c7c-1d39-11eb-32db-c9cfc82b792a | |
md"""We can use step to cut off the circle at a radius. Here we'll use a Pluto bound slider to play with the radius.""" | |
# ╔═╡ cd5d4fb0-1d39-11eb-3c1d-d33c06938880 | |
rad_slider = @bind rad html"<input type='range' min='0.0' max='1' step='0.01' value='0.15'>" | |
# ╔═╡ d98a438c-1c7c-11eb-0e3f-e513e5a5e275 | |
Gray.((c -> step(c, rad)).((c -> length(c.-0.5)).(st))) | |
# ╔═╡ 9b760860-1d3a-11eb-158e-9d7169d4ee63 | |
md"""One way we can visualize a distance function is by taking the sin of it mulitplied by a value.""" | |
# ╔═╡ 4362dfb2-1c7d-11eb-3ae6-a5b52503852d | |
Gray.((c -> sin(c*50)).((c -> length(c.-0.5)).(st))) | |
# ╔═╡ cc583b06-1d3a-11eb-0eed-7b77c9a89eff | |
md"""Another useful trick with distance functions is that we can union them through taking the minimum of two distance functions. For more information on distance function operations, check out [Inigo Quilez's page on it](https://iquilezles.org/www/articles/distfunctions/distfunctions.htm).""" | |
# ╔═╡ 800e76e8-1c81-11eb-0e7a-ff16cf9d08e2 | |
begin | |
c1 = (f -> length(f[1]-.45, f[2]-.45)).(st) | |
c2 = (f -> length(f[1]-.80, f[2]-.70)).(st) | |
c3 = (f -> length(f[1]-.90, f[2]-.20)).(st) | |
c4 = (f -> length(f[1]-.25, f[2]-.25)).(st) | |
fc = min.(min.(min.(c1, c2), c3), c4) | |
Gray.(fc) | |
end | |
# ╔═╡ 0f1e950c-1d3b-11eb-36ee-c7b02872fd3a | |
md"""Just like length and sin, we can broadcast min and abs across the matrices as well.""" | |
# ╔═╡ 59438c84-1c80-11eb-1f8c-2763d3f0f451 | |
begin | |
df = abs.(sin.(fc.*100)) | |
Gray.(df) | |
end | |
# ╔═╡ 6c280132-1c93-11eb-334f-ad915af4a853 | |
Gray.((f -> step(f, 0.25)).(df)) | |
# ╔═╡ 4954ca84-1d3b-11eb-0e25-7b5c6de2f69c | |
md"""As a final note, some of these functions are getting a little crazy, and simialarly to how I broke it up above, you can use the [Pipe](https://github.com/oxinabox/Pipe.jl) package to make a clean chain of operations""" | |
# ╔═╡ e1f80b2e-1c92-11eb-1c6e-8739cb21708d | |
@pipe st |> | |
(f -> 1.0-length(f.-0.5)).(_) |> | |
(f -> sin.(f*25)).(_) |> | |
(f -> 1.0 - step(f, .5)).(_) |> | |
(f -> ([0.7, 0.85, 0.25]).*f).(_) |> | |
(f -> RGB(f[1], f[2], f[3])).(_) | |
# ╔═╡ Cell order: | |
# ╟─df7c6b84-1d33-11eb-0a40-112e53d1896d | |
# ╠═03e6d8a4-1c0d-11eb-23c7-57525308d771 | |
# ╟─9f27ea9c-1d34-11eb-00ce-c133f2dac376 | |
# ╠═395d9aae-1c7b-11eb-1c1f-e7288114c806 | |
# ╠═d57981aa-1d34-11eb-14fa-510a71100a21 | |
# ╟─09aa9cfc-1d35-11eb-1752-65d28d61158f | |
# ╟─8a6e0e34-1d35-11eb-3c7d-b92084530089 | |
# ╠═56806ab6-1c81-11eb-019d-75b4a853f71c | |
# ╟─17c13afc-1d36-11eb-1591-cdb9bc262011 | |
# ╠═64145664-1d36-11eb-043e-dd63013bac25 | |
# ╠═a01df1d8-1d36-11eb-39e5-433d81f6968b | |
# ╟─b2f8c620-1d36-11eb-3cc0-13d285d57b45 | |
# ╠═a2b22428-1c7d-11eb-2d45-31d95434244d | |
# ╟─3927ba58-1d37-11eb-0eb3-85f77dc09be9 | |
# ╠═460c8e1c-1c7c-11eb-1873-2da2ead2fb32 | |
# ╟─6454452e-1d38-11eb-2ab6-571f07accff5 | |
# ╠═6ceb28da-1c78-11eb-0e10-2dd217346811 | |
# ╟─b341c97c-1d38-11eb-0dd8-8dcc6a46287d | |
# ╠═dfbc6ece-1c7c-11eb-0558-6525bd0cf44a | |
# ╟─7c9b8c7c-1d39-11eb-32db-c9cfc82b792a | |
# ╟─cd5d4fb0-1d39-11eb-3c1d-d33c06938880 | |
# ╠═d98a438c-1c7c-11eb-0e3f-e513e5a5e275 | |
# ╟─9b760860-1d3a-11eb-158e-9d7169d4ee63 | |
# ╠═4362dfb2-1c7d-11eb-3ae6-a5b52503852d | |
# ╟─cc583b06-1d3a-11eb-0eed-7b77c9a89eff | |
# ╠═800e76e8-1c81-11eb-0e7a-ff16cf9d08e2 | |
# ╟─0f1e950c-1d3b-11eb-36ee-c7b02872fd3a | |
# ╠═59438c84-1c80-11eb-1f8c-2763d3f0f451 | |
# ╠═6c280132-1c93-11eb-334f-ad915af4a853 | |
# ╟─4954ca84-1d3b-11eb-0e25-7b5c6de2f69c | |
# ╠═cc2522f4-1d33-11eb-386f-f9530f5a9fbb | |
# ╠═e1f80b2e-1c92-11eb-1c6e-8739cb21708d |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment