Let's understand how to manipulate images with complex functions.
The idea is that we can see an image a a function that maps every possible position to a color. This can be expressed as $$ img:ℝ^2→ℝ^3 $$ Nothing prevents us to look at $ℝ^2$ as if it were $ℂ$ with $\mat{u\\v}=u+iv$ (where $uv$ is our screen coordinate) $$ img:ℂ→ℝ^3 $$ Now what happens if instead of $img(uv)$ we draw $img(f(uv))$ for some $f:ℂ→ℂ$.
We'll see that even basic functions look quite nice.
The following piece of code sets up a canvas to be drawn by a fragment shader from which we have access to
- uv coordinates
- a texture
- time
I'll use
Let's draw our texture, but we'll control the position of it with the mouse.
Hey, that code looks wrong ! Why
There is something a bit counter-intuitive but very important : we do not transform our image directly, we rather ask each pixel on the screen, "if we applied this transformation, where would the pixel come from on the original image ?"
So if we were to apply $deform1$, then $deform2$, then $deform3$, we would need to go backward.
$$\al{
\text{original texture} &&
\al{
\xrightarrow[]{\text{deform1}} && \xrightarrow[]{\text{deform2}} && \xrightarrow[]{\text{deform3}} && \\
\xleftarrow[\text{deform1}^{-1}]{} && \xleftarrow[\text{deform2}^{-1}]{} && \xleftarrow[\text{deform3}^{-1}]{} &&
}
\text{deformed image}
}$$
The affine transform can squish the building's windows from square to parallelograms.
Here is something to keep in mind : $$\al{ z &= r⋅\exp(iθ) \text{ with } \ca{r=|z| \\ angle(z)} \\ \log(z) &= \log(r) + i⋅θ }$$ thus if we want to read this as a texture coordinate, we need to convert $θ∈[0,2π]$ to $θ/2π=[0,1]$
In our case, I mapped the texture in such a way that we read coordinates in $[-1,1]$ so I only divise by $π$
$n$ is the amount of winding. It should be an integer otherwise you'll get a jump in angle.
$s$ kind of changes the basis of the $log$, which squish the pattern around the hole. It decides how fast we fall into it.
Here is another interpretation of what $\log$ does : it cuts a 2d band in $ℝ^2$ that is of this shape : $ℝ×[-π,π]$. It's an infinite band in the middle.
It assumes that everything above and below this band is a repetition of this band (this will become an issue later).
This thing then exponentially squished so that the negative part
$$ \log(z) = \log(|z|) + i⋅ang(z) $$ $$ \exp(z) = \exp(|z|) ⋅ \exp(i⋅ang(z)) $$
step 1 : exponential squish on the real axis.
step 2 : kill everything outside of $i⋅[-π.π]$
step 3 : twist according to the vertical position
options
An interactive visualization of complex log intuition can be found in the "complex log" section, under "interactive toy"
On step3 of that interactive toy, we see how we seal the top part of the image with the bottom part on a circular deformation. When using a seamless texture, we then expect continuity (as we have here with our building texture).
But this is not the only way one can seal the bottom part to the top part : we could join the bottom part of the left neightboor with the top part of oneself. Then we obtain that spiral like structure (this is what $m$ does)
The affine transform can squish the building's windows from square to parallelograms, as you could already see in the "affine" section.
If we stay with complex derivable functions, we'll get the awesome properties such as "all angles are preserved". This means that right angles will stay right angles, though they can be deformed in non-rigid ways. This looks quite peasing, so we'll work with that mainly.
A complex derivable function is called holomorphic, and if it's derivable execpt on isolated points, it's called meromorphic (like $1/z$). We'll go for meromorphic funtions to allow crazy explosions.
Note that complex analysis has quite surprising properties, but we won't use them here. We will only note that any finite composition of meromorphic functions is meromorphic. This means that with a small set of functions, we can craft sophisticated functions.
And here are the basic holomorphic/meromorphic functions we might want to use
$\exp$
holomorphic
$\log$
almost holomorphic,
there is a seam issue we'll discuss
polynoms $∑_{0}^{K} λ_k x^k$
holomorphic
finite laurent series $∑_{-K}^{K}λ_k x^k$
meromorphic
And from that we can already construct $$\al{ \cos(z) &= \frac{1}{2}(\exp(iz)+\exp(-iz)) \\ \sin(z) &= \frac{i}{2}(\exp(iz)-\exp(-iz)) \\ \tan(z) &= \frac{\sin(z)}{\cos(z)} }$$
If $m$ and $n$ are integer, it works fine, but inbetween we get a seam.
Is there a way to get some continuous analog to decide how much twist we want ? To figure this out we need to understant why we have that seam.
Applying $\log$ to coordinates is equivalent to deform the image with an $exp$. But here is a property of $\exp$ : $$ ∀k, \; r\exp(iθ) \equiv r\exp(iθ+i2πk)$$ The $i$ axis (vertical axis) is interpreted as an angle, and every $2π$ upward (or downward) will be sent to the same place.
This means that when we go back from there with $\log$, we need to choose one of the possibility for θ, which could be anything up to a $2πk$ offset. If all point are evaluated to be the same value, then there is no issue : whatever we choose will work. But if we don't have that regularity, each choice could jump anywhere, and thus make discontinuities.
My implementation of complex $log$ is such that I choose $θ∈[-π,π]$, so the potential seam happens if we have something different at $π$ and $-π$.
Conclusion : we need to find a transformation that keeps vertical periodicity.