1

I'm trying to render music notations to godot using SMuFL fonts. To do this polygons are created from the lines and curves described in the OpenType fonts, read from SixLabors Font. The current problem is how to render the polygons so that overlapping regions are excluded, like an XOR operation.

The first approach I tried is adding polygons directly to the scene, each with a shader that XORs the screen color and texture color. The problem I found in this approach is that the overlapping regions do not become transparent.

The second approach is to use Clipper library, but this does not clip polygons lying fully inside another.

The current approach is to create a separate Viewport fill with black pixels, add polygons each with a BackBufferCopy behind and holding a shader that compares color equality to simulate XOR process. Then, Light2D node is added to the scene, which takes the texture from the separate Viewport and use Sub Mode so white colors are subtracted to get intended black colors. However, the use of Light2D decreases performance by a great amount (setting one Light2D to invisible increases the fps by around 20, wow), as described in this github issue, so I need another way to render music scores, likely by performing overlap XOR operation directly on screen.

The shader script is this:

shader_type canvas_item;

uniform vec4 active_col:hint_color;
uniform vec4 same:hint_color;
uniform vec4 different:hint_color;
vec4 exclusion(vec4 base, vec4 blend){
    return base + blend - 2.0 * base * blend;
}
vec4 color_dodge(vec4 base, vec4 blend){
    if (blend.r == 0f && blend.g == 0f && blend.b == 0f) return vec4(0f,0f,0f,0f);
    return base / (1.0 - blend);
}
void fragment() {
    vec4 scrC = textureLod(SCREEN_TEXTURE, SCREEN_UV, 0.0);
    vec4 texC = texture(TEXTURE, UV);
    // This doesn't work somehow
    //COLOR.rgb = vec3(float(int(scrC.r * 256f) ^ int(texC.r * 256f)) / 256f,
    //              float(int(scrC.g * 256f) ^ int(texC.g * 256f)) / 256f,
    //              float(int(scrC.b * 256f) ^ int(texC.b * 256f)) / 256f);
    //if (scrC == texC) {
    //  COLOR = new;
    //}
    //else COLOR = texC;
    //scrC = overlay(scrC, texC); 
    //COLOR.rgb = scrC.rgb;
    //COLOR = texC;
    if (texC.rgb == active_col.rgb && scrC.rgb != texC.rgb)
        COLOR.rgb = different.rgb;
    else 
        COLOR.rgb = same.rgb;
}

The reason not to use label directly is that I need to know the bounding boxes of each notation, so I can place stems and flags, etc. to the anchors of a notehead, where the anchor is relative to the bounding box's origin. Here is an image example (The red box is the bounding box, the blue box is the overall size of the text, green line is the origin):
red box indicates the bounding box

Qiufeng54321
  • 169
  • 11
  • BackBuffer + Viewport + Light 2D is overkill. You could do with only the Viewport: [GODOT How can I xray through tilemaps around me](https://stackoverflow.com/questions/69911347/godot-how-can-i-xray-through-tilemaps-around-me) – Theraot Apr 18 '22 at 02:38
  • I didn't know one vote from me would be enough to close the question, since I only have a bronze badge on Godot, not a golden one. Anyway, I have added a couple notes at the end of the linked question that might help you. – Theraot Apr 18 '22 at 13:10
  • 1
    @Theraot This problem is now solved as the solution in the link you provided worked so well, so I closed this one by myself. Thank you so much for helping me out! :D – Qiufeng54321 Apr 18 '22 at 14:05
  • Ah, good to know. – Theraot Apr 18 '22 at 14:12

0 Answers0