0 votes

I am trying to mask a sprite with a dynamic mask updated from code.
This should be possible, or am i wrong ?

I know i can do the masking part via shader, but how do i manipulate (draw/remove circles, rectangles and polygons) the mask from code ?
Is there a way to create a texture from the current content of a CanvasItem ?

Alternatively if someone knows of a better way to reach my goal please share.
Thanks a bunch.

asked Oct 23, 2016 in Engine by sleepprogger (118 points)
edited Oct 26, 2016 by sleepprogger

Do you want to make a Sprite or a Texture Button with a dynamic mask? because if I'm not mistaken a Sprite got no mask, only a picture which it displays

I want to mask a sprite. This works via Shader/Light2D. But my problem is to generate dynamic masks at runtime.

2 Answers

–1 vote
Best answer

While volzhs answer is valid for static brushes i needed a way to draw like one is able to in the canvasItem draw method (drawcircle, drawrect, draw_polygon...)
So here is what i ended up with:

Disclaimer: While this is working i am sure there are more efficient solutions

Create the mask renderer

1. Create a Viewport and set Render Target to enabled
2. Add a Node2D (or any CanvasItem) as child and use the _draw method to draw your mask. Paint everything which should be visible with white.

Use the mask
In GDScript use my_viewport.get_render_target_texture() to get your texture.
If you want to only show masked areas just plug that texture into a LightMask2D (See link posted by volzhs). In this case you are done and can stop reading.

If you want the reverse case (hide everything masked) you need to write a shader (at least that was the easiest way i could think of). Don't fret, its easy.
Create a material and set the shader to new CanvasItemShaderGraph.
In the fragment shader wire it up like seen below:
enter image description here
In the shader we defined two parameter (Uniforms): One is the Texture we want to mask and the other our mask texture.
In GDScript you can provide them to the shader like:

sprite.get_material.set_shader_param("Tex", sprite.get_texture())
sprite.get_material.set_shader_param("Mask", mask_viewport.get_render_target_texture())

Thats it.
This approach is using a 4 channel (r,g,b,a) image for our mask and only uses 1, tho.
If it is somehow possible to use only one chanel images that should improve efficiency.

answered Oct 26, 2016 by sleepprogger (118 points)

Hi, can you post a demo with source, i really don't understand how to use your code :(, thanks!!

I didn't check out godot since some time so no clue if it is still working.
Also the code isn't really cleaned up but should be enough to get you going.

Delite it please.

+2 votes

You can make a mask with Light2D
See demo for it.

There is similar question and answer about making dynamic mask.

answered Oct 23, 2016 by volzhs (9,487 points)

Thanks, i found that post (after submitting this question tho).
Manipulating the mask with brush_transfer should work for some things but is really suboptimal.

All that is needed is some way to render a CanvasItem to a texture or some other way to use the draw_* methods to create images/textures.
One could manipulate the image data pixel by pixel ofc. but i guess that would be slow as hell.
If there is no other way would this be something for a feature request ?

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read How to use this Q&A? before posting your first questions.