Godot: Simplest way to achieve blend mode in custom draw

Created on 15 Jan 2020  路  4Comments  路  Source: godotengine/godot

That problem confuse me for a long time.

If I want to draw a rectangle with a hole manually like this
Grey grid is the transparent area
Here's the node code

extends Node2D
func _draw():
    draw_rect(Rect2(50, 200, 150, 150), Color(1, 0, 0, 0))
    draw_circle(Vector2(200, 200), 100, Color(1, 0, 0, 0.9))

and the shader code:

shader_type canvas_item;

void fragment() {
    COLOR.a = 1.0 - COLOR.a;
}

Without shader

With Shader

I think enable the shader, the result should be like this

The problem is draw() function is not draw all the things on a cache, then apply the shader, then draw it on the screen, it apply the shader after every time you call function like drawcircle(), draw_rect(), etc. So I cant draw some mask effects like the first image shows. And I try light2D, because light2D only support texture, so I cant use custom draw either.

So I dont know how to get the color from current drawing or just cache them into a texture/surface. I think it's a little bit complex for a blendmode function

discussion rendering

Most helpful comment

Unfortunately, due to how rendering pipelines work nowadays, it is a bit tricky to cut out a hole from an already-rendered object, as the information about the pixels behind it has already been lost.

To combat this problem, there exist a few solutions:

  • Render an object of the background color/texture instead of a hole.

    • Evidently, this works only if you do not have anything behind the hole which needs to be visible through it.

  • Render the object without rendering the hole

    • For example, you can use a polygon which has a cut-out hole. Note that draw_circle already draw a polygon under-the-curtains.

    • Alternatively, the shader can use UV coordinates to find the distance to the center, and then trace out a circle based on that.

  • Render the object and the hole to a separate texture.

    • There is little support for overwriting the alpha channel of the viewport from a shader, so the only way to do this in Godot for now is to use a texture for the object, a texture for the hole, and a shader which combines both in the main scene.

  • Copy the screen before rendering the object, use it as a texture of the hole.

    • This is surprisingly easy to do in Godot, due to the fact that the BackBufferCopy node exists. What you can do is setup a BackBufferCopy which renders right before the object, then render the object, and then render the hole with a shader which uses COLOR = texture(SCREEN_TEXTURE, SCREEN_UV). To make things more optimal, you might make the BackBufferCopy use COPY_MODE_RECT and a rectangle which covers the hole (in local coordinates).

All 4 comments

This sounds like a duplicate of #31124 to me.

Unfortunately, due to how rendering pipelines work nowadays, it is a bit tricky to cut out a hole from an already-rendered object, as the information about the pixels behind it has already been lost.

To combat this problem, there exist a few solutions:

  • Render an object of the background color/texture instead of a hole.

    • Evidently, this works only if you do not have anything behind the hole which needs to be visible through it.

  • Render the object without rendering the hole

    • For example, you can use a polygon which has a cut-out hole. Note that draw_circle already draw a polygon under-the-curtains.

    • Alternatively, the shader can use UV coordinates to find the distance to the center, and then trace out a circle based on that.

  • Render the object and the hole to a separate texture.

    • There is little support for overwriting the alpha channel of the viewport from a shader, so the only way to do this in Godot for now is to use a texture for the object, a texture for the hole, and a shader which combines both in the main scene.

  • Copy the screen before rendering the object, use it as a texture of the hole.

    • This is surprisingly easy to do in Godot, due to the fact that the BackBufferCopy node exists. What you can do is setup a BackBufferCopy which renders right before the object, then render the object, and then render the hole with a shader which uses COLOR = texture(SCREEN_TEXTURE, SCREEN_UV). To make things more optimal, you might make the BackBufferCopy use COPY_MODE_RECT and a rectangle which covers the hole (in local coordinates).

I think the backbuffercopy is what I need, just like in gamemaker, you render a stuff to the surface then use shader to the surface, but I think it's better if there's a way to get the buffer from current draw not the screen, on the other words, render current draw to a separate place. In godot I think we must put different layer into individual scene, but I dont't know how to do that concretely

@firecloud888 That would be the third option, but as I mentioned, achieving transparency in that layer is a bit not-simple.

Was this page helpful?
0 / 5 - 0 ratings