How to Create Complex Non-Destructive Effects in Krita

Share
In this tutorial, we'll learn how to create complex non-destructive effects in Krita (version 5.2), using filter layers, filter masks, transform masks, layer styles, clone layers, layer groups, and alpha blend modes. This tutorial won't explain how to create a specific filter, but instead introduce you to techniques you can use to create your own effects, and workarounds for Krita's peculiarities.

Foreword

Complex, non-destructive effects can quickly consume a lot of memory and processing power to function. It's possible to keep the activated while you edit the image, but in general it's easier to keep them hidden and only make them visible when you want to preview what the output is going to be.

One advantage of effects is that you can easily copy a set of effects from one project to another to apply them as if they were a filter.

Available Tools

Krita has several non-destructive tools that we can use:

  • Filter layers and filter masks apply filters non-destructively.
  • Transform masks apply transforms non-destructively.
  • Layer styles can apply certain effects like stroke and drown shadow non-destructively.
  • Clone layers can create copy-pasted versions of a layer non-destructively.
  • Layer groups can merge effects non-destructively.
  • Certain blending modes can apply clipping and erase parts of the image non-destructively.

By combining these together, we can create all sorts of effects, but there are some limitations:

  1. Krita doesn't have many filters to begin with. Notably, Krita lacks a filter like GIMP's displacement map filter, which could be used to create effects that transform masks can't create.
  2. The G'MIC effects available in Krita can't be use non-destructively as G'MIC is an external program. It would probably be too slow to make this non-destructive, but if you asked me, I'd rather have something slow that is possible, than something that is not possible at all.
  3. Krita was clearly not designed for someone to do what I'm doing with it. It's very easy to find bugs when doing things non-destructively. For example, stacking transform masks leads to display bugs. Filters that change a layer's boundary size, like gaussian blur, can end up appearing "clipped" if the blur is larger than the view tile size.
  4. It's not possible to select areas of an image automatically, so things that require masks will typically require manual editing.

Despite these shortcomings, I believe in the right hands there are a lot of cool things that can be done with.

Using Layer Groups to Merge Non-Destructively

The first thing we'll learn to do is how to use layer groups to merge effects non-destructively.

Layer groups have a property called "pass through" that can be toggled on and off in the layer's properties or by clicking on a brick wall at the right. When pass through is activated, the layer group exists purely for organizational purposes. When pass through is deactivated, the layer group acts as a temporary image, everything inside of it getting merged before being applied to the layers below the group.

Let's see an example to understand how this works.

1: create a new paint layer and draw a simple shape with it, like a green circle, or a red heart.

2: right click the paint layer to make its context menu appear, and then click "Layer Style...." A dialog to configure the layer style will appear.

3: this dialog has a list-detail layout. On the list pane, select the Stroke item. The square at its left is a checkbox. Check it to make the stroke effect appear.

4: change the blend mode of the stroke effect to "Normal," opacity to 100%, and change the color to something discernible, like orange or blue. Then click OK to apply the changes. You should see a colored stroke around the shape you drew.

You may notice that there is no way in Krita's layer style dialog to add a second stroke after the first one. However, this is still possible if we use a layer group.

5: right click your shape layer again to make its context menu appear, and then click on Group -> Quick Group (keyboard shortcut: Ctrl+G). This will create a group layer and place the paint layer inside.

6: right click the new group layer and go to its layer style. Add a stroke of different color to it (blonde mode "Normal," opacity 100%, etc.), then click OK. You should see that your shape now has 2 strokes.

This is possible because of how the stroke effect works. The stroke effects add an outline based on the opaque pixels of an image, which are determined by the alpha channel. When we draw something on a paint layer, we create some opaque pixels. If we add add a stroke style to the paint layer, the stroke added will be new opaque pixels.

The group's style operates on the sum of all the layers inside the group. It operates on an image generated from the group's content. It doesn't know what parts of this generated image are pixels that come directly from paint layers and which parts are pixels that come from effects. For the style program, it's all the same thing, which means it treats the stroke we added automatically just as if we had added it manually.

We can repeat this process as many times as we want (or as many times our computers can handle). Try creating three or four different strokes one on top of the other!

7: after you're done, click on the paint layer inside the group layer where our shape is. Grab a brush and draw something. You'll noticed that several layers of strokes are getting added to it automatically! This is the power of non-destructive effects.

8: right click on one of the group layers with a stroke style, and select "Properties..." in its context menu (you can also just select it and press F3). This will make the layer properties dialog appear.

9: on the properties dialog, there's a checkbox to configure the "Pass Through" setting of the dialog. Check it and then press OK.

You'll notice that the stroke style we had added to that layer group disappeared. This happens because when "Pass Through" is enabled, an image for the contents of the layer group isn't generated: instead, the layers are applied on the layer stack as if the group didn't exist at all.

In other words, it isn't possible to apply a stroke to the opaque pixels inside the group because we aren't keep tracking of what pixels are inside the group and what pixels are outside the group.

Using Layer Groups to Organize Filters

Next, we'll learn how to use layer groups to organize large amounts filter layers.

1: create a new document in Krita and draw a simple shape in the middle of the canvas.

2: create a new filter layer. Select the Blur -> Gaussian Blur filter and configure it so that it's noticeably blurry. Right now, your shape should be blurry.

3: create a new layer group and place the filter layer inside of it. You'll notice that all of sudden the filter layer stopped working.

This happens because "Pass Through" is off by default.

Filter layers work by applying the filter to the image that's generated so far under them on the layer stack. When we create a layer group with "Pass Through" turned off, it's as if a new transparent image is created just for that layer group, a new layer stack. This means that if the layer filter is the first thing in the group, it's like we're applying a filter to an empty image with nothing in it, so there is no effect.

4: open the layer group properties, and check the "Pass Through" setting. Your shape should be blurry now.

We can use this technique to organize complex effects into layer groups.

Applying Multiple Layer Filters to Only One Part of the Image

In Krita, layer filters and layer masks have internal transparency masks that can be edited when they are selected. In simple cases, these internal transparency masks are enough for masking the effect of the dynamic filters, but in complex scenarios they unexpectedly stop working. Let's see an example to understand what I mean.

1: create a new document in Krita, and draw one red circle and one green circle separated by a space.

2: create a new gaussian blur filter layer. This should make both circles blurry.

3: select the filter layer.

4: select the black color.

5: paint the area where the green circle is with black. This should change the transparency mask from white (opaque) to black (transparent), which means the gaussian blur effect will disappear from the green circle.

6: with the filter layer selected, add a new filter mask, so that you have a filter mask attached to the filter layer. Select the Other -> Random Noise filter, which will generate noise on the image, and then click OK to add it.

What I'd expect to happen is that noise is applied only to the area on the image where the gaussian is applied, i.e. both filters should share the same mask.

What actually happens is that there will be noise everywhere on the image, even on the green circle where our blur doesn't reach.

There is one way to solve this.

1: select the layer with our shapes and create a clone layer out of it.

2: create a layer group and place the clone layer inside of it.

3: place the layer filter with its layer mask inside the group, above the clone layer.

4: right click the layer filter to open its context menu, and then select Split Alpha -> Alpha into Mask. This will create a transparency mask based from the internal mask of the filter, and the reset the internal mask to fully opaque.

5: drag the transparency mask upwards until your mouse cursor is over the layer group (not over its contents, over the "Group" layer), then drop it. The transparency mask should get attached to the group as a whole. Doing this, all effects inside the group will be affected by this mask.

Essentially, what we did was copy-paste our initial image, apply two effects to it, and then remove part of it with a mask. All of this done automatically by Krita.

Proxies

The next technique we'll learn about are proxies, something that Krita was definitely not designed to handle.

1: create a new document.

2: create a new paint layer and name it "red," then draw a red circle in it on the upper side of the canvas.

You can use the keyboard shortcut F2 to quickly rename layers.

3: create a new paint layer and name it "green," then draw a green circle in it on the lower side of the canvas.

4: create a clone layer of the red layer, and name it "proxy."

5: hide all 3 layers by clicking on the eye icons on the left side of the layers panel.

6: create a clone layer of the proxy layer, and name it "left." You should see that "left" is visible even though "proxy" is invisible, and "red" is invisible.

7: create a transform mask on the "left" layer and using the transform tool, move it toward the left side of the canvas. Don't move it up or down, just to the left.

8: select the "left" layer and click the "Duplicate Layer or Mask" button. This will create a copy of "left" called "Copy of left." Rename it to "right," and using the transform tool move it to the right side of the canvas.

9: make red and green visible by clicking on their eye icons again.

You should see a red circle on the upper side of canvas with a red circle at its left and a red circle at its right, and a single green circle at the lower side of the canvas.

10: right click the proxy layer to make it context menu appear, then click "Set Copy From..." A small dialog box will apear for you to select the source of the clone layer.

11: change the source of the clone layer from "red" to "green," then click OK.

What this should do is that, because "left" and "right" are a clone of "proxy," if we change "proxy" from "red" to "green," the "left" and "right" layers stop being red circles, and start being green circles. They also stop being on the upper side of the canvas, and start being on the lower side of the canvas.

Krita will struggle a bit with this. When I tried it, it made 3 red circles and 3 green circles appear on the screen at same time. Thankfully, it's nothing that the good old "turn it on and off" doesn't solve. If you click the eye icon on the proxy to make it appear and then hide it again, Krita starts working properly, and now you have 3 green circles, and only 1 red circle.

If you want to create complex effects that you can easily copy from one project to another, I suggest you use this technique if you have many clone layers. This way you base all your clone layers on named proxy layers, and then when you change projects you only need to update what the proxies point to. If you're learning programming, this is what a pointer to a pointer is for, by the way.

Displacement Effect

Next we're going to learn about is how to move parts of a layer non-destructively. This isn't as useful as it could be with a displacement map, but it's pretty interesting nonetheless.

1: create a new document in Krita.

2: select the text tool and drag a rectangle on the canvas. This will make a dialog box appear where you can type the text. Type "Displacement" in the text, change the color of the text to black, and make the size bigger. If you can't figure out how to use the text tool, create a new paint layer and write the word using your mouse. Name this layer "start."

3: create a clone of the "start" layer and call it "start 2."

4: create a layer group and place "start 2" inside of it.

5: select "start 2" and add a transform mask to it. Using the transform tool, move it a bit toward the diagonal, such that the transformed clone still overlaps with the original "start."

You should see that we now have 2 copies of the same text at different positions. In order to move parts of the text, we will need to erase the moved part from the original position, and erased everything except the moved parts from the destination.

6: select the original "start" layer and create a new paint layer above it. Name the new paint layer "eraser."

7: with an airbrush or some other soft brush, draw over a few letters of the text. It doesn't matter which color you choose.

8: change the layer blend mode of "eraser" to "Mix -> Erase." Doing this will make the letters you painted over get erased. This blend mode subtracts the alpha of the current layer to the alpha of everything under the current layer.

If you have a white background layer, which you should have by default, you may notice the the white background has been erased as well.

9: create a new layer group, and place the "eraser" and "start" layers inside of it. Doing so will limit the effects of the eraser to the "start" layer, so it won't erase the white background.

10: create a clone layer of the "eraser," and name it "anti-eraser."

11: drag the "anti-eraser" into the layer group where "start 2" is, and place it above "start 2."

12: change the blend mode of the "anti-eraser" to "Mix -> Destination In." Doing this will turn the "anti-eraser" into a clipping mask, replacing the alpha of the layers beneath it with its own.

In other words, the "eraser" deletes its alpha from layers below, while the "anti-eraser" replaces the alpha from below with its alpha. Combined, these two effects can create a displacement effect. But you may notice that it's still oddly blurry. We'll fix that in a bit. Before that, we need to fix another thing: the transform mask is being applied only to "start 2," which means our copy of "start" is moving before we change its alpha. The order of operations is wrong, so the effect will look wrong. To fix this:

13: drag the transform mask from "start 2" and drop it on the layer group that contains the "anti-eraser" and "start 2." Doing this will make the whole group move instead of only the "start 2" layer, and "anti-eraser" and "start 2" will be "in sync."

Next we need to fix the blurriness.

In Krita, alpha is only used for transparency, which is a bit unfortunate. For example, consider a transform mask that moves an image to the left by 10 pixels. It's not possible to make this effect interpolate from 0 to 10 based on the alpha. The image always moves 10 pixels, and the alpha decides how to blend the moved image with the image under it.

Consequently, when we use a soft brush to draw the "eraser," we're just making the opacity fade. In GIMP, the displacement map filter would be make to move pixels at different offsets according to the color in the displacement map. Such functionality doesn't exist in Krita.

On top of that, most brushes will have some opacity or anti-alias effect in them, which means in most cases there will be semi-transparent pixels, and can result in weird "gaps" of pixels appearing in parts that should fit perfectly. Consequently, it would be very difficult to use most brushes in our "eraser" layer. The only brushes we would be able to use without creating gaps are the pixel art brushes. Fortunately, we can fix this by adding more effects.

14: select the "eraser" layer and attach a new filter mask to it. The filter that we want is the "Adjust -> Levels" filter.

15: on the top-left corner of the levels filter settings, click on the button to switch its mode from lightness-only to "Modify all channels independently." Then switch the channel from RGBA to Alpha.

16: set the input levels to 127 minimum, 1.000 gamma, and 127 maximum. This will make all alpha values under 127 become 0, and all alpha values above 127 to become 255. In other words, it will remove all semi-transparent pixels and make them either fully opaque or fully transparent. Then click OK to add the filter mask.

Now you can use any brush on the "eraser" layer, including textured brushes, without creating blurry displacements.

Fixing Banding

When you create complex effects, you may end up doing several math operations on a limited range of integer numbers, which can lead to compounding rounding errors creating artifacts like banding. To understand what I mean, let's see an example.

1: create a new document in Krita. Make sure that "Model" is set to RGBA and "Depth" is set to 8 bit per channel.

2: create a new layer group.

3: create paint layer inside the group and draw a large circle in it.

4: create a new "Blur -> Gaussian Blur" filter layer inside the group. Make the gaussian effect very large, like 50 pixels by 50 pixels.

5: create another filter layer inside the group. This time we want the "Adjust -> Color Adjustment" filter.

6; on the top-left corner of the color adjustment filter settings, change the channel from RGBA to Alpha.

7: on the middle of the settings, you'll find a curves editor with one control point at the bottom-left and one control point at the top-right. Leave those control points where they are. Click on the straight line that goes from one point to the other to add a middle control point. Drag it to the top-left corner, but a bit to the right. Create another control point and make it a bit more to the right, but toward the bottom of the area. Repeat this until you have 3 "peaks" to create a "wave" shape. Click OK to apply the effect.

The gaussian blur filter we applied will create an alpha gradient on the edges of our paint layer, while this new filter will turn that gradient from a normal slope into a zig-zag alpha effect that will look like 3 outlines because we made 3 peaks.

8: click on the paint layer and try drawing a bit more. You'll see a very neat effect where the borders of the paint "merge" together. Unfortunately, there's a couple of problems. First, you may notice parts of the effect appear "clipped" within rectangles. This is due to Krita's rendering program not working how it's supposed to. I'm not sure if there's any way to fix it. Second, you may notice that the edges of the effect have a strange color banding effect. This can be fixed.

The reason why this happens is that we're making, for example, a value from 0 to 64 become a value from 0 to 255 with our curves filter with multiple peaks. Because they're integers, we end up with gaps: what was 0, 1, 2, 3 before becomes 0, 4, 8, 12, the values 1, 2, 3 are skipped completely. These gaps can become noticeable, and that's what the banding is.

It's possible to solve the banding by using a higher color depth than 8 bits per pixel.

9: on the menubar, click on Image -> Convert Image Color Space... and change the depth from 8 bit to 16 or 32 bit, then click OK. Doing this should get rid of the banding.

There are of course drawbacks. 16 bits is more than 8 bits, which means more memory will be consume and more computing power will be needed to perform the same tasks, and this applies to all aspects of the whole image-editing or drawing process, not just to the cool effects that require it. On the other hand, the difference can be noticeable, because 8 bits is just too little for complex graphical work.

I'm not quite sure, but I think 16-bit integer is enough for most use cases, and it should be faster than 16-bit floating point. The difference in detail between 16-bit and 32-bit is immense for the computer, but not necessarily noticeable for human beings.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *