+1 vote

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.

in Engine by (121 points)
edited by

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

0 votes
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.

by (121 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.

+1 vote

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

There is similar question and answer about making dynamic mask.

by (9,752 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 Frequently asked questions and How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to webmaster@godotengine.org with your username.