In an earlier post i described an old program I wrote in C++ using Openframeworks. I tried remaking that program in Unity but the process of updating textures in unity simply took too much time and bottlenecked the entire program. I ultimately shelved it for this reason.
I decided to try once more to port this program over to the Godot game engine. Initial tests were promising. Texture updating was quicker compared to unity, but still not quite as quick as it was in C++.
Taking it to the next level
There were several improvements I had planned:
- Allow the chaining of filters, so it would be possible to process several filters per iteration. Furthermore, i wanted to be able to visually connect and disconnect the filters. Something interactive like this would make it easy to combine filters for even cooler results, while also making the program intuitive and user-friendly.
- Add a nice user interface that allows the user to toggle automatic image processing and save images on the fly.
- Allow for any size of image, as opposed to hardcoding the dimensions.
- Make it easy for the user to code new filters and register them.
- Have a parameter system where data can be assigned to individual pixels by the filter, and give filters parameters that the user could modify through the UI. Exposing the parameters via the user interface turned out to be a bit more work than i had hoped, so i didn’t finish that functionality.
One idea I had was to make this a program that any person could download and use. There are two problems with this approach: This program is more fun to use if you know how to code your own filters, and I’m selfish and prefer to keep my algorithms secret.
Why is this so slow?
There was one critical oversight I made when I started porting the C++ program to Unity.
I presumed that images being manipulated in the old C++ program were 1080p. if i had looked closer at my old source code, i would have seen that the images were only 720p. with more pixels to iterate over, any new program I wrote would certainly take longer to process an image. This isn’t necessarily bad (higher resolution images are a good thing) but it contributed to the confusion as to why the old C++ program was so much faster than any new code I wrote.
I didn’t notice this key distinction until I started writing the program over in Godot. It made it a bit easier to live with the realization that my new program wouldn’t be as fast as it was in C++. I traded the speed of C++ for a better user interface, a more modular object oriented system and a nice interactive filter pipeline.
After handling the boilerplate code for the user interface and interactive filter system, all that was left to do was actually write some filters to test it.
Unfortunately, I didn’t keep track of all of my old C++ code, so I lost a lot of interesting filters that gave me fantastic results in the past. Still, using some of the code I still had access to, I could come up with some interesting new stuff.
It turns out that allowing the chaining of multiple filters was a game changer. If a filter gives underwhelming results on its own, then I’d simply combine it with a different filter. It’s damn near impossible to predict how filters are going to interact when chained together.
With the old C++ program there were a lot of “dud” algorithms that I would test and discard if they simply didn’t give interesting results. There was a lot of wasted code and dead ends. With the new filter pipeline, I can take duds and combine their logic. There’s a greater chance I might be able to salvage a filter by combining its effects with another.
Some of my most interesting recent results are the product of chaining filters together. What makes this even more interesting is that I can run the program on autopilot and let it iterate several times and if I don’t see favorable results, I can just pause execution and re-wire the filter chain. This wasn’t possible with the old program.
Enough chatter, show me the goods
The images you see below started out as photos taken on Lake of the Woods.