Weird Noise-Like Patterns Appear When I Try to Compose Layers

Hello,

I’m trying to figure out how to get blending modes to work by composing multiple layers, and sometimes it works, but sometimes the results are … weird.

As a test image, I draw a red rectangle and on top of it a smaller rectangle filled with a linear gradient from white to black using Multiply as blending mode. The code is arranged like this and the image looks as it should.:

compose {
  draw {
    // draw red rectangle
  }
  layer {
    blend(Multiply())
    draw {
      // draw rectangle with gradient
    }
  }
}

But when I add another layer, using Normal as blending mode, to draw a circle on top, the result looks like this:

What could be the reason for that? If I draw this on the screen in a loop, it looks like noise on a TV screen, since the pattern changes from frame to frame.

By the way, the image in my posting is the result of drawing off-screen and saving the color buffer to a file. But as I wrote, it looks similar when drawn on the screen, just changing with each frame. The same patterns appear when I wrap the red rectangle in a layer using Normal as blending mode.

This is how the test image looks (and should look) when I just draw the two rectangles using the code structure that I outlined in my posting:

Hi hi! :slight_smile: Any chance you could share a minimal program showing the issue? Then we could try run it on our computers to see what’s happening.

1 Like

Sure, here is a program that produces the effect I described. Thanks a lot!

import org.openrndr.application
import org.openrndr.color.ColorRGBa
import org.openrndr.extra.compositor.blend
import org.openrndr.extra.compositor.compose
import org.openrndr.extra.compositor.draw
import org.openrndr.extra.compositor.layer
import org.openrndr.extra.fx.blend.Multiply
import org.openrndr.extra.fx.blend.Normal
import org.openrndr.extra.shadestyles.linearGradient
import org.openrndr.math.Vector2

fun main(args: Array<String>) = application {
    program {
        val composite = compose {
            draw {
                drawer.clear(ColorRGBa.BLACK)
                drawer.stroke = null
                drawer.fill = ColorRGBa(1.0, 0.0, 0.0)
                drawer.rectangle(width / 3.0, 0.0, width / 3.0, height.toDouble())
            }
            layer {
                blend(Multiply())
                draw {
                    drawer.stroke = null
                    drawer.fill = ColorRGBa.WHITE
                    drawer.shadeStyle = linearGradient(
                        ColorRGBa.WHITE,
                        ColorRGBa.BLACK,
                        Vector2.ZERO,
                        90.0,
                        1.0
                    )
                    drawer.rectangle(
                        width / 3.0,
                        height / 3.0,
                        width / 3.0,
                        height / 3.0
                    )
                }
            }
            layer {
                blend(Normal())
                draw {
                    drawer.stroke = null
                    drawer.fill = ColorRGBa.BLUE
                    drawer.circle(width / 2.0, height / 2.0, height / 5.0)
                }
            }
        }
        extend {
            composite.draw(drawer)
        }
    }
}

I think blend(DestinationAtop()) may give you what you expect, but I can’t say why :slight_smile:

If you can read GLSL, here are the blend shaders. And the Visual glBlendFunc Tool can help sometimes, but I’m not sure if it does in this case.

2 Likes

It does. Thank you!

I played around with more layers and found out that I can avoid the patterns by first moving the drawer.clear out of the composition, not using a layer for the “background”, i.e. the actual first layer that would use Normal as blending mode, and then using DestinationAtop wherever I would have assumed Normal would be the right choice.

2 Likes

I can not (yet), unfortunately. But the tool looks promising, thanks!

Unfortunately, this only seemed to work for some simple tests, or I maybe didn’t pay enough attention. While I do not yet fully understand what DestinationAtop does, it is no replacement for Normal. Here is an example:

import org.openrndr.application
import org.openrndr.color.ColorRGBa
import org.openrndr.extra.compositor.blend
import org.openrndr.extra.compositor.compose
import org.openrndr.extra.compositor.draw
import org.openrndr.extra.compositor.layer
import org.openrndr.extra.fx.blend.DestinationAtop

fun main(args: Array<String>) = application {
    program {
        val composite = compose {
            draw {
                drawer.fill = ColorRGBa(1.0, 0.0, 0.0)
                drawer.rectangle(width / 3.0, 0.0, width / 3.0, height.toDouble())
            }

            layer {
                blend(DestinationAtop())
                draw {
                    drawer.fill = ColorRGBa.YELLOW
                    drawer.rectangle(0.0, height / 2.0 - 30, width.toDouble(), 60.0)
                }
            }
        }
        extend {
            drawer.clear(ColorRGBa.BLACK)
            composite.draw(drawer)
        }
    }
}

In the resulting image, the yellow rectangle is clipped at the borders of the red one, which it would not be when using Normal() which, even in this case, leads to the noise patterns.

Could this be an issue somewhere in my system stack, i.e. JVM, driver, OS etc.? I’m working on an M1 Mac, are there any known issue or is there some place where I could find something like that out?

Okay, I finally managed to fix this issue simply by not calling blend(...) at all for layers that should be drawn normally :man_shrugging:

2 Likes

Ha! Good to know! Sometimes less is more XD Thank you for sharing the solution :slight_smile:

2 Likes