Posted: Posted September 20thEdited September 20th by Xhin
This is a story of my journey from basic enemy behavior to the most fascinating image distortion effects I've ever seen.
I've always been fascinated by image distortion effects like this, but I could never figure out how on earth they worked. Since then, I've not only rediscovered them, but I've come up with some that are quite a bit more interesting as well.
So, the story starts with the game Shatterloop that I'm developing. It's a 2D top-down game with tactical combat. I was working on basic enemy behavior and I wanted to develop a way for enemies to move towards or away from the player. If there was a fixed set of coordinates they could occupy I could just use some if-then statements so the enemy would know where they were in relation to the player:
This kind of solution worked for something else I was doing (clicking part of the screen to make the player move in that direction), but with enemies it didn't work at all because the enemies could be any distance away from the player, not just within that 21x21 screen. I needed a more generalized solution.
Thinking back to old math classes, I realized that you could just represent positions as angles around a circle and then use specific ranges of those angles to determine a "direction" from the player:
So that's great in theory, but how on earth do you actually get those angles when all you know is the X and Y coordinates? Well, I again thought back to old math classes and remembered that you could take the arctan of a triangle where you knew both sides and wanted to find the angle:
Doing some more math and a bit of translation, I was able to get the angle based on a "centerpoint" and an arbitrary X and Y coordinate, which I then tied into enemy behavior to give them the behavior I wanted.
One huge issue I came across is that mathematical graphs and computer graphics have inverted y-axes -- on a graph, the y axis runs from the bottom to the top but when you're rendering graphics it runs from the top to the bottom. When you're programming, the trigonometric functions act like the Y axis works from bottom-to-top so you have to do a bit of extra work to turn your code back into graphics. In any case, if you see any future graphs or graphics that have the y axis in the wrong place, this is why.
Getting the distance
This "calculate the angle" system proved to be extremely useful for a number of things in the game's development beyond enemy behavior. Mapping the mouse location to a direction for weapon effects, rendering triangles, and so on.
Eventually I started working on Provinces -- this is a system where towns will generate infinitely in all directions in fairly random ways. I needed a way to figure out the distance between towns, because that factors into the cost to travel between them.
Fortunately, the pythagorean theorem is quite useful here -- since you know the X and Y coordinates of both you can just use it to get the distance between the two points. Rather than creating a separate function for this, I stuck it in the same function I was using for getting angles -- now when you give the function a centerpoint and an (x,y) coordinate, it'll return both the angle and the distance.
Later in development, I was working on a way of generating various shapes. I made rectangles at first, but was successful in adapting the code I created to get the angle and distance to make triangles as well, and then eventually, circles as well:
Basically it takes a rectangle, converts it to this "angle/distance" thing and filters out any tiles that are beyond a certain distance.
If you also filter out specific angles, you can get "star" like patterns, which are quite neat.
Spirals and Angle remapping
I love spirals, so I then decided that I should make them as well. Somehow. I'd been wanting to make them for a while but couldn't figure out a good way to do this, but with this new "coordinate system" I realized hey you could just map the distance to the angle -- the smaller the angle, the bigger the distance, and vice-versa.
This "angle remapping" idea was a huge breakthrough. I started working on plugins for my graphics program again and I realized with this idea and with this new coordinate system I maybe had an idea of how the "Polar Transformation" plugin worked.
My thinking was that it must translate an image into these "polar coordinates" and then remap them back into the X and Y axis in some way (I don't remember what the exact translation was, this is just a guess.
By transferring my "polar coordinate" code directly from shatterloop and with a bit of experimentation, I was able to duplicate the effect.
My first distortion plugin
While working on this effect, I would occasionally go out of bounds or go below zero for either the x or y coordinate. Elsewhere I had been working on a "Reflecting" plugin that reflects an image across the x and y axis seamlessly and had a lot of great safeguards in place, so I ported that code in for the time being.
Instead of being a short-term fix though, this had the neat effect of reflecting things outwards and this bit of code has since been in all of my distortion plugins.
My code obviously has some differences from the original plugin, but it's a pretty close match, and as an added bonus it reflects itself outwards which is quite cool.
I started playing around with the variables in my effect and I realized if you did a modulo of the angle by something that was 360 could evenly divide by, it would repeat the pattern around the circle in a "kaleidoscope"-like effect. By migrating my reflection code in there you could get a much smoother kaleidoscope effect.
This and some other variables formed the basis for my first plugin, Polar Kaleidoscope -- it essentially converts an image to polar coordinates, remaps them, and then does the modulo/mirroring code to reflect that around the circle and outwards.
Since it's reflecting the image itself, you get some really neat effects from it that you wouldn't get with other kaleidoscopes (because they only take a piece of the image).
Making regular Kaleidoscopes
With this under my belt, I decided to try my hand at making actual kaleidoscopes. This would work very similarly -- you would turn cartesian coordinates into polar coordinates, do the modulo and mirroring functions to get a triangular "piece", and then reflect that piece around the circle and outwards.
So yeah, that worked pretty well. And since it reflects outwards, it's definitely one of the better kaleidoscope plugins out there. But while playing with the variables I realized you could adjust the size of the "outwards" reflection to get make the kaleidoscope look different internally:
This "change the size of the reflection" technique is extremely useful elsewhere.
For my next project, I wanted to turn an image "inside-out" -- the basic idea is that you convert an image to polar coordinates, then subtract the distance from the maximum distance. This should mean that everything on the outside gets remapped to the inside, everything on the inside gets remapped to the outside, and everything else gets shifted around accordingly.
There was a plugin that did this already called "Inside-out", however as seen here it turns rectangular images into a circle.
Thinking about it, I realized that the maximum distance would be very different depending on your angle -- if you were straight up and down it would be half the height, if you were straight left-and-right, it would be half the width, and if you were along a line to the corner it would be the greatest distance of all. Everything else would be somewhere between those values depending on the angle.
So the creator of the inside-out plugin probably just used a fixed distance to preserve their sanity. I felt like I could do better though -- I wanted to actually turn an image inside-out while preserving its rectangular nature.
Well, it turns out you already have everything you need to figure this out.
To get this new angle, all you need to do is take the arctan of (dx/dy). Dx and Dy are just the difference between the coordinate in question and the centerpoint. Once you have this angle, you can then use it with the Y position of the centerpoint in order to find out the length of the hypotenuse of this triangle. This hypotenuse is the same as the Dmax for that specific angle along the original polar coordinates, so everything works beautifully.
While this formula works for any time that your angle has you facing the X axis, you have to modify it somewhat if you're facing the Y axis -- just replace X's with Y's basically. If you're facing the corner directly then Dmax is just going to be the length of the hypotenuse between the centerpoint's X and Y.
In any case though, now that you have the correct Dmax for any point inside the rectangle, you can smoothly invert a rectangle into another rectangle. A very cool effect, but it gets better.
If you multiply the Distance by something, you effectively "zoom out". And since it retains the outwards reflection code I put in either, it will reflect out along this new warping pattern. Now we seem to be getting somewhere! It looks a lot like the original polar inversion plugin, except with a "rectangular nature". This formed the basis of my Rectangular Inversion plugin.
With some more work I was able to fully replicate the original polar inversion plugin, along with some cooler variations (haven't released this yet). However the cooler effects came from exploring these "rectangular coordinates" more.
As cool as this effect was, it still inverted the image. I decided I wanted a version of it that would preserve the image but just make it "more rectangular". Such a plugin could turn circles into rectangles, and if reversed, vice-versa.
I was successful here, but while experimenting I decided to optionally flip the way the algorithm determines whether to use the x coordinate or y coordinate for determining the distance.
Remember here, this example only works if the angle is "facing" the x axis. If it's "facing" the y axis you have to swap the x and y variables in the formulas. Determining this has to do with checking both the dx of the point (x distance from the centerpoint) against the dy (y distance from the centerpoint), which has been multiplied by the ratio between the width and height. Whether the dx is closer than that or not determines whether the dmax formula uses x or y. If you flip this, so a closer dx will make the formula instead use dy, you end up a new set of effects.
All told, you get a total of four effects. The math is getting pretty ridiculous at this point and building on itself so I'm just going to focus on what's happening rather than how it works in detail (that's basically covered by the rest of this post).
Part of the appeal with the rectangular inversion effect is that you can move the centerpoint around. If you do this you end up with some pretty cool transformations:
With the "rectangular transformation" plugin I was working on, if you do that with either of the pyramid effects or the Tube Concave effect it will just move the source image around like you'd expect.
The Tube Convex effect, meanwhile, is quite crazy.
To show you what I mean I'm going to switch to a bigger source image.
Here's a basic Tube Convex transform. When I first did this I thought "okay cool it's just stretching the edges out".
But when you move the centerpoint around, the warping changes a lot. You end up feeling like you're looking around inside of some bullet-shaped container.
These ideas (and some various other variables) made their way into my Cubic Transformation plugin.
So a while after this I decided to expand on the polar kaleidoscope code in order to make things reflect in a polar way without being transformed. This worked out quite well, as you can see.
I decided to apply the rectangular transformation code optionally to this plugin, allowing you to use this effect and make it rectangular at the same time.
This apparently creates "hallways", which with larger canvases will very definitely have that 3D illusion feel to them.
I also imported my spiral techniques into this plugin -- you can make twisting rectangular hallways, which has a real OOT Forest temple feel.
If you pan around these you very definitely feel like you're looking around a hallway, even while the texture is changing simultaneously.
There are a number of other crazy effects as well. All these black and white ones are coming from the same exact source image. I guess bottom line there's a lot of potential here. I bundled all of this into my Polar Reflection plugin.
Some other crazy stuff
I've been messing around with various variables and formulas. All of these effects basically convert cartesian coordinates to polar coordinates and then convert back, but in the interim I'm adjusting either the distance or the angle (or both).
You get this kind of singularity-like effect by dividing the distance by the angle. The stuff in the middle is happening because after that I divide the angle by the dx (x distance from centerpoint).
If you square the distance and then divide down until you can see things again, you end up getting some kind of crazy circular fractal pattern.
Is there a name for these? They're really cool, whatever they are.
So yeah, there's obviously still a lot to explore and develop. Overall though, a lot of really neat effects which all started from trying to get enemy behavior to work right.
By the way, if you're interested in these plugins or using them, I have them all on my github here, along with a lot of documentation:
There are 11 Replies
Reply to: Polar Coordinates