The past few months I’ve been rewriting the text layout engine used by Krita’s text tool. This is not the same as the text tool itself, which is still a super small rich text editor, but it is a prerequisite to getting any kind of new features into the text shape. We haven’t done any real improvements to text since the work for the last fundraiser we had for it, and that is because this needed to be done, it is a lot of work, an we had vowed to take care of resource management first, which, uh, took us so long and was so intensive that it covered the whole development cycle from 4.0 to 5.0, or a span of 5~ years. I’m not the only developer who can finally tackle a sore point, there’s work being done on audio, lots of file format updates, work on assistants, technology upgrades and more… But this blog is about text.
So, with Krita 5.0 nearing completion. There’s been some discussion about what we’ll do next.
On of the proposed topics has been to replace our calligraphy tool with something that can produce nice variable width editable lines.
I’ve been slowly building up an OPDS feed, because I added support to KNewStuff, and I wanted to make sure I was able to make an OPDS feed myself.
Of course, the biggest problem is content. I have some fan comics lying around, and I have some Krita Resource Bundles that are all over the place… But there was a thing missing. Back when I was fifteen, I started work on a comic named Streets of Ganjet, an urban fantasy comic about the people living in the city of Ganjet. I have tried to rewrite it, but never got far enough for it to publish new chapters.
Sometimes, when you look at distribution systems with build-in user interaction, you will see that system-designers haven’t really thought about what the user interaction actually is.
Like most online manuals, the Krita manual has a contributor’s guide. It’s filled with things like “who is our assumed audience?”, “what is the dialect of English we should use?”, etc. It’s not a perfect guide, outdated in places, definitely, but I think it does its job.
So, sometimes I, who officially maintains the Krita manual, look at other project’s contributor’s guides. And usually what I find there is…
So, back in the day, I had posted 100 sketches I had made between 2014 and 2016. I kept continuing into a next folder called 200+, in the hopes that I would get to the next 200…
I took the month of November off, which had been very necessary, as I hadn’t had a proper vacation in a year. In this month, I slowly started being able to draw again, and I did a lot of drawing. I also had a conundrum: I wanted to learn a bit more about Godot Engine, but I also wanted to learn a bit more about internet technologies. Solution: Write an XMPP client in Godot.
So, we had a Krita sprint last week, a gathering of contributors of Krita. I’ve been at all sprints since 2015, which was roughly the year I became a Krita contributor. This is in part because I don’t have to go abroad, but also because I tend to do a lot of administrative side things.
This sprint was interesting in that it was an attempt to have more if not as much artists as developers there. The idea being that the previous sprint was very much focused on bugfixing and getting new contributors familiar with the code base(we fixed 40 bugs back then), this sprint would be more about investigating workflow issues, figuring out future goals, and general non-technical things like how to help people, how to engage people, how to make people feel part of the community.
Unfortunately, it seems I am not really built for sprints. I was already somewhat tired when I arrived, and was eventually only able to do half days most of the time because there were just too many people …
So, what did I do this sprint?
So, last week I spend time on figuring out different animations in terms of raster animation that is then baked into the final tile. This week, I investigated a different approach to game graphics: Shaders.
Shaders are formally tiny programs that are specially written for the graphics card. In game engines, shaders are most prominently used to calculate how a 3d object should be rendered, or shaded. But they’re also used in 2d graphics a lot. Krita for example uses shaders to make the canvas perform better, handling cursor and to display HDR images. Godot also allows applying shaders to 2d graphics, even specific tile graphics. So I spent some time playing with that.
In the case of water, there’s a ton of stuff out there on water shaders in 3d. But I am interested in water shaders in 2d, to spruce up the water effect I had going. So I set up a tileset in Godot, with my water tiles as an ‘animated texture’ as one of the spritesheets/tileatlasses used for the tileset. Godot allows individual textures that are part of a tileset to have a shader assigned to them, so the following visual shader was set up:
This may seem like a very overcomplicated thing, but visual programming always looks overcomplicated, as each block in visual programming is a single operation. I chose to use visual programming here because I imagine there’s people out there for whom regular programming is a bit intimidating, so here’s a confirmation that what I am about to do can be done in the visual programming interface. Let’s break down what is done here!
Starting out, these are the water tiles we have:
We overlay these over terrain on a seperate tile layer:
This looks a bit dull, so the first order is to blend them with the underlying tiles. We do this in two passes: once with multiply, and once with overlay, so that the contrast gets increased nicely, as is typical of wet things in the real world. So we go from the most basic ‘canvas item’ visual shader:
In the above, you can see two things not mentioned. First is that the preview is useless here. The second is that we also use vector interpolation to blend the multiply and the overlay 50% with the underlying texture. This is to avoid oversaturated colors.
Next up, we’ll add some clouds to give the effect of an overhead sky. I want to show the overlaid area only when there’s no cloud shadow, so you get the effect of sun reflecting on the water. For this we’ll use a simple black and white texture.
We’re getting to the point where the visual shader graph is a bit too big to really fit, so subsequent images are going to only show a small part, and you’ll have to imagine to integrate them into the whole.
So, the next step is getting the clouds to move. This is as simple as taking the ‘time’ input and using a vector op to add it to the uv input of the clouds. (The uv being a coordinate system that the texture is mapped onto) However, we’ll want to control in which direction the clouds move. We can just use sine and cosine to calculate x and y modification from a radian angle, but most people prefer degrees for input. So we’ll need to convert those degrees to radians. Furthermore, we will also need to make sure the input is limited to 360 degrees.
To do this, we start with a ScalarUniform we’ll name ‘Angle’. Then, whenever you use this shader, Godot will present you with a little input labeled ‘Angle’ that you can set to anything. Another one is ‘speed’, to indicate how fast the clouds go. The visual shader graph is the following:
In the above image, we take the angle, use the modulus/remainder operator to limit the value to 360, then multiply by 3.1427(visual shader editor doesn’t have constants) and divide by 180 to get the corresponding radians. The radian value then has sine and cosine functions applied to it to get the x and y coordinates. These are normalized values, so we can just multiply them with the time vector. The time vector is the time interpolated with 0 using the ‘speed’ uniform as the interpolation factor. That is, if the speed is high, the resulting value will be higher, if the speed is low the resulting value will be low. The resulting value is our time vector, which is then multiplied with the angle vector and that is then added to the screen uv, which is what causes the movement animation.
For water flow, we can use a flicker animation and overlay that. Then, similar to the clouds we can move them in a given direction. But first, the flicker animation is 1/8th in size compared to the animated texture. We’ll need to apply a transformation matrix to the uvs of the flicker texture here.
In the above graph there’s still the time stuff, but we’ve hidden it offscreen for now. Once the UVs are aligned properly, you can use the alpha of the flickers to interpolate/blend them onto the main texture.
So, one of the things that I really wanted to figure out was whether I could use flowmaps to control the water direction. There’s been several papers on this, with the most notable one being the valve paper, where flow maps weren’t just a simple but cool result, but also helped players find their way through a map. I tried to glean most of my info from this useful tutorial on the subject.
So, in the above we have a flowmap texture(that is aligned to the original water texture, more on that later), this is then decomposed into xyz values. The xyz values are decoded from their 0 to 1 range to the proper -1 to 1 range so they’re proper vectors. Finally, the result is multiplied with the time vector, and then added to the other UV modifications(offscreen). This is following the initial part of that tutorial, giving the simple ‘distortion’ effect (the tutorial also recommends adding a fraction function so that the time doesn’t repeat into infinity doing weird things in the shader).
Above we replace the vector op ‘multiply’ node with an attempt to rotate the time vector with the vector from the flow map, following the second part of the flowmap tutorial. I was unsure how to do this, as I kept thinking to myself ‘surely, they’re not expecting us to manually multiply the matrices?’, but after some search this youtube video demonstrated that indeed, in the visual shader graph the matrices of the two vectors do need to be multiplied manually. Note that the first vector compose, and two decomposes are unnecessary here, but are just there to help visualize.
In the end, I would’ve liked it if I could have created a second tilemap where I would create the flow using tiles, and then used that to inform the distortion, but that doesn’t seem to be possible. So while it is possible to create a flowmap effect in Godot’s visual shader, I am not sure how to effectively create and align a flowmap to objects in the game engine.
If you add in masks to emphasize the edges, and a mask to make the sparkles in the center less strong, you end up with something like this:
Meanwhile, on the OGA forums, BlueCarrot was able to get a much simpler and easier to use flow effect going by just animating center tiles, so next stop is to experiment with that instead. I just figured I’d document everything I’ve learned up till now. 🙂
Overall, it was a little surprising that the Godot visual shader graph, which is for people not experienced with coding didn’t really have easy ways to generate a transformation matrix from vectors or radians from degrees, or even have access to common maths constants like pi. This makes the shader graph surprisingly barebones. The above shader could probably also be optimized, but right now this was more of a ‘how would we go about it’ rather than worrying about speed.
I occasionally make tiles following the Liberated Pixel Cup style guide for relaxation. Given that I just handed in my bachelor thesis, I was in need of some relaxation.
One of the things that had been bothering me is that the default water for this style didn’t animated, nor really felt like it would get my characters wet. There were also very few people who’d made an attempt at animating the water. I myself tried, but I failed. So I suspect very few people know how to animate water properly. With that in mind, I decided to do a ton of experiments and document them so that others might learn from it as well.
I set up a thread on OpenGameArt.org, to see what others think, or to see what kind of things people have been trying. For this post, I am documenting all the ways to animated the edges of water tiles, with the aim of creating a nice transparent mountain brook.
Types of Edge Animation
So, the first thing you can try to animate is the border between the edge and the land. There’s several types here.
So, the most common edge animation is the expand and contract. Frame order is as follows:
- Outline at rest
- Outline contracted 1 pixel on all sides.
- Outline at rest (copy of 1)
- Outline expanded 1 pixel on all sides.
You can see variations of this in most games, as it is a really easy way to animate using few colors. The animation should be adjusted to the environment, so for example below there is a pebble texture that the animation is adjusted to. Then some outlines are added, and the whole thing is made semi-transparent. Of note here is that the outline is a little bit aligned to the texture as well, leading to a nice effect when made semi-transparent.
One of the posters on OGA, Evert, pointed out it’s a bit odd though: the surface area increases.
To tackle that, one of the solutions is to make the water undulate. That is, the water pixels just circle around, being 1 pixel up, 1 pixel left, 1 pixel down and finally 1 pixel right, without any rest.
Waves on the side.
The other solution was proposed by Evert: A little wave that goes by the side. This can be done by contracting 1/4th of the tile and expanding 1/4th of the tile, and then moving those areas 1/4th over a frame, leading to 4 frames where the 1st and 2nd frames as well as the 3rd and 4th of mirrored tiles are mirrors of each other. So if you have the topleft corner on frame 0, it is a mirror image of the topright corner on frame 1.
There’s several variations of these, one with a single pixel wave, one with two pixels on each side, one where the pixels on the sides are a little bigger than the transitional pixel, so the result feels a bit rounded, and one where the pixels don’t travel. Frame 0 and 2 are resting frames here, and frames 1 and 3 are mirrored as described above, this one rather looks like the water is a blob receiving a shock.
You can then adjust the outlines to the rock below, but sometimes this may end up so noisy that it might be better to avoid it, especially when combining with other animations
The ocean wave is a little tricky. It first most is much like an expand/contract one, with the main wave expanding, and then an underlying wave contracting or slowly becoming transparent to indicate evaporation. Furthermore, there’s a lot of timing tweaks necessary, you can for example make the contraction a lot slower than the expansion to get a more natural ocean-rhythm. Also of note is that because so many pixels get covered with water, these need to be 8 fps, or it will look choppy.
This one will need a lot more experimentation, but for now these little examples to demonstrate this type of edge animation.
Animating the inner outline
The second thing you can animate is the inner outline. These are often a lot more soothing.
Animating like the borders above but not animating the border.
This is basically, you animate the inner outline like above. Because you don’t animate the outer outline, the result is a lot smoother.
This one is basically little dots that move about, aligned to the underlying pebble texture. By itself it doesn’t look fantastic, but once you start blending it becomes a very easy to control refraction effect.
A very simple style where a single pixel border is animated inwards from the sides. The border is made more transparent as the wave gets closer to the center, fading it out.
Animating the brightness.
These were demonstrated on the OGA thread by both BenCreating and MedicineStorm, and they do show up on many commercial examples.
Basically, animate the outer outline from cyan to bright white. I also tried replacing the wave animation from the edge animation section with a brighter wave in this style:
Blending the whole thing.
I’d also been playing with blending modes, the examples here using a water layer separated from the rocks, duplicated twice, both layers set to transparency, and the lower layer set to multiply while the higher is set to overlay. This increases the contrast of the water tiles, which is something wet objects are known to do in real life.
And other ideas.
Fading the center
These faded the centers a little, making the borders more distinct and adherent to the LPC style guide. It has a little bit the downside that these don’t make for good RPGmaker mv tiles, but for LPC and Wang it works just fine.
Animating the flow
This was an attempt at seeing how to handle water flow. General concensus was that it looked weird because the water came out of nothing.
There’s still other parts to animating water, but I hope this gives some inspiration for how to tackle your own water. I myself will continue with experimenting till I have some satisfying tiles to share 🙂