• 3D
  • How to apply anti-aliasing and glow to a viewport used as mesh texture?

Hi there,

I successfully managed to utilize the technique described by @TwistedTwigleg in https://godotdevelopers.org/forum/discussion/20065/how-to-position-hud-items-in-a-vr-application for creating a reticle-style GUI control for interacting with objects just by gazing at them. (I am really impressed by the amount of out-of-the-box utility functions provided by Godot like that RayCast thing!)

More precisely, I have a Node2D inside a Viewport, whose texture is used as albedo_texture for a MeshInstance's surface_material. On that particular Node2D, I do some custom drawing to indicate whether an interactable object is in focus, whether activation is in progress, and the like. As mentioned, all that works fine, but the drawing still looks a bit ugly -- in particular due to the lack of anti-aliasing.

Furthermore, I'd like to apply some glow effect to the reticle HUD. I experimented a bit with a custom environment node below the Viewport, but glow settings adjusted here apparently influence the whole scene.

So here's my questions:

  • Is there a way to enable anti-aliasing for the texture drawn by the Node2D?
  • Is there a way to selectively apply glow settings to a sub-set of objects in a scene?

Looking forward to any advice --

Torsten

@tcrass said: So here's my questions:

  • Is there a way to enable anti-aliasing for the texture drawn by the Node2D?

You can add anti-aliasing to the viewport through the MSAA property in the Viewport node. However, I am not sure if this effects the 2D side or not.

If you are using the _draw function, depending on how you are drawing to the scene you may be able to pass in a additional argument to apply anti-aliasing. For example, the draw_line function has an additional argument for whether or not the line drawn should have anti-aliasing applied.

Another way to make the GUI/UI look better is to change the scale on the UI and/or increase the resolution of the Viewport and sprites/images drawn within it.

  • Is there a way to selectively apply glow settings to a sub-set of objects in a scene?

Good question, and to be honest I am not sure.

If you check the Own world checkbox in the Viewport and then add a WorldEnvironment node as a child of the Viewport, it should only effect the nodes within the Viewport. I've had hit and miss luck with this though.

In theory, you may be able to add glowing to your the objects by making a emission texture.

You would need another Viewport where only the elements you want to glow are visible, and everything else is black. Then you could take the texture rendered from that Viewport and use it as the input for the emission texture in the SpatialMaterial. Not sure how easy/practical that would be though.

If you want to only make the object in the UI appear to glow, you could use the additive CanvasMaterial shader on your sprites within the Viewport. Granted that would only make it look like it is glowing on the texture rendered by the Viewport, which may or may not work for your project.

TwistedTwigleg,

If you are using the _draw function, depending on how you are drawing to the scene you may be able to pass in a additional argument to apply anti-aliasing. For example, the draw_line function has an additional argument for whether or not the line drawn should have anti-aliasing applied.

stupid me -- how could I have missed that?! Thanks for pointing this out to me, the drawing function I'm using does indeed support anti-aliasing.

Another way to make the GUI/UI look better is to change the scale on the UI and/or increase the resolution of the Viewport and sprites/images drawn within it.

Yeah, I came up with this already, but it feels a bit like cheating... ;)

  • Is there a way to selectively apply glow settings to a sub-set of objects in a scene?

Good question, and to be honest I am not sure.

If you check the Own world checkbox in the Viewport and then add a WorldEnvironment node as a child of the Viewport, it should only effect the nodes within the Viewport. I've had hit and miss luck with this though.

Tried that before, but any adjustments made in the sub-tree's Environment did in fact affect the whole display.

If you want to only make the object in the UI appear to glow, you could use the additive CanvasMaterial shader on your sprites within the Viewport. Granted that would only make it look like it is glowing on the texture rendered by the Viewport, which may or may not work for your project.

It seems I used the term "glow" incorrectly since this is exactly what I want -- I don't need the object to emit any light, I just want to it to appear to be glowing, e.g. by adding a blurred version of the drawing to itself. I had no success with the 'additive CanvasMaterial' you suggested (or I did it the wrong way), but I took this as a motivation to look into shader programming. So for a start, I applied a "no-op" ShaderMaterial with the following shader code to my Node2D object:

shader_type canvas_item;
void fragment() {
    COLOR.rgb = texture(SCREEN_TEXTURE, SCREEN_UV).rgb;
}

The scene tree basically looks like this:

Spatial
+ MeshInstance # <- uses the Viewport's texture as a SpatialMaterial's albedo_texture
+ Viewport # <- with transparent_bg = true
    + Node2D # <- features the mentioned ShaderMaterial

However, when assigning the ShaderMaterial to the Node2D object, the drawing is still visible, but everything that used to be colored now appears just black!

So... am I missing something? Is it perhaps generally not possible to sensibly apply a shader to a custom-drawn Canvasitem?

(In case anybody feels inclined to reproduce the described issue: I uploaded the project to GitLab at https://gitlab.com/tcrass/angstrom. Use arrow keys to move the Reticle towards the green cube to see it in action (the orange sphere deliberately doesn't react to the reticle). The ShaderMaterial (already termed 'GlowEffect') is applied to the "ReticleDrawing" node in res://scenes/reticle/Reticle.tscn.)

Cheers --

Torsten

@tcrass said: It seems I used the term "glow" incorrectly since this is exactly what I want -- I don't need the object to emit any light, I just want to it to appear to be glowing, e.g. by adding a blurred version of the drawing to itself. I had no success with the 'additive CanvasMaterial' you suggested (or I did it the wrong way), but I took this as a motivation to look into shader programming. So for a start, I applied a "no-op" ShaderMaterial with the following shader code to my Node2D object:

shader_type canvas_item;
void fragment() {
    COLOR.rgb = texture(SCREEN_TEXTURE, SCREEN_UV).rgb;
}

The scene tree basically looks like this:

Spatial
+ MeshInstance # <- uses the Viewport's texture as a SpatialMaterial's albedo_texture
+ Viewport # <- with transparent_bg = true
    + Node2D # <- features the mentioned ShaderMaterial

However, when assigning the ShaderMaterial to the Node2D object, the drawing is still visible, but everything that used to be colored now appears just black!

So... am I missing something? Is it perhaps generally not possible to sensibly apply a shader to a custom-drawn Canvasitem?

(In case anybody feels inclined to reproduce the described issue: I uploaded the project to GitLab at https://gitlab.com/tcrass/angstrom. Use arrow keys to move the Reticle towards the green cube to see it in action (the orange sphere deliberately doesn't react to the reticle). The ShaderMaterial (already termed 'GlowEffect') is applied to the "ReticleDrawing" node in res://scenes/reticle/Reticle.tscn.)

Cheers --

Torsten

Hey Torsten. Sorry the delay in replying, I was busy with other stuff :smile: .

Anyway, I think I know why you are having a problem with the shader. Right now your shader is getting the texture from the screen and displaying it. If you use this shader on a normal sprite outside of a Viewport node, you will not be able to see it because it will look just like the texture that is being rendered to the screen. It would seem to be completely transparent, while in reality it's not but rather it is showing the scene at that exact position.

The reason it is black instead of transparent in your scene is because there is nothing drawn to the viewport yet. Because by default viewports have a black background (though you can set it to transparent in the Transparent BG flag), that is why you are getting a black screen.


Have you looked at the 2D shaders demo on the GitHub Godot repository? I think there is a glow shader that may work. The only potential snag there is, if I recall correctly, the shaders in the demo require there to be a texture for sampling purposes.

This QA post has a interesting workaround way to get something similar, not sure how feasible it would be though. Unfortunately I could not find anything that has 2D glow on stuff made with the _draw function.

(Side note: You did used the term glow correctly. I was confused and was thinking you were wanting the texture to emit light through the emissive property in the Spatial Material so the glow would happen on the 3D side of the scene, rather than the 2D side :smile: )

TwistedTwigleg,

@TwistedTwigleg said: Sorry the delay in replying, I was busy with other stuff :smile: .

no need to apologize -- I am so grateful that there are any people at all like you who apparently have time for and patience with Godot beginners like me... :)

Anyway, I think I know why you are having a problem with the shader. Right now your shader is getting the texture from the screen and displaying it. If you use this shader on a normal sprite outside of a Viewport node, you will not be able to see it because it will look just like the texture that is being rendered to the screen. It would seem to be completely transparent, while in reality it's not but rather it is showing the scene at that exact position.

Right, I guess understand that now. I just thought that pixels modified by draw_* functions would appear on the screen before any custom shader materials kick in.

The reason it is black instead of transparent in your scene is because there is nothing drawn to the viewport yet. Because by default viewports have a black background (though you can set it to transparent in the Transparent BG flag), that is why you are getting a black screen.

No, i don't get a black screen (or a black rectangle where the Mesh is) -- it's just the pixels touched by the draw_* functions which appear black, all other pixels look transparent.

Anyway...

Have you looked at the 2D shaders demo on the GitHub Godot repository? I think there is a glow shader that may work. The only potential snag there is, if I recall correctly, the shaders in the demo require there to be a texture for sampling purposes.

...this seems to work only with canvas_item shaders on pixels already on-screen; I tried like a million variations of the textureLod approach to get some blurring, but I just couldn't get any effect -- not in the setup described above, nor in my new scene which looks like follows:

Spatial
+ Sprite3D # <- uses the Viewport as ViewportTexture and the ShaderMaterial described below
+ Viewport # <- with transparent_bg = true
    + Node2D # <- this is where the pixels are drawn to

Shader code:

shader_type spatial;

render_mode unshaded;

uniform vec2 dx = vec2(1, 0);
uniform vec2 dy = vec2(0, 1);
uniform float offset = 0.05;
uniform float strength = 0.1;

uniform sampler2D texturemap : hint_albedo;

void fragment() {
    ALBEDO = (
        0.0625 * strength * texture(texturemap, UV + offset * (-dx +dy))
        + 0.125 * strength * texture(texturemap, UV + offset * (+dy))
        + 0.0625 * strength * texture(texturemap, UV + offset * (+dx +dy))
        + 0.125 * strength * texture(texturemap, UV + offset * (-dx))
        + 1.25 * texture(texturemap, UV)
        + 0.125 * strength * texture(texturemap, UV + offset * (+dx))
        + 0.0625 * strength * texture(texturemap, UV + offset * (-dx -dy))
        + 0.125 * strength * texture(texturemap, UV + offset * (-dy))
        + 0.0625 * strength * texture(texturemap, UV + offset * (+dx -dy))
    ).rgb;

    ALPHA = min(length(ALBEDO.rgb) * 5.0, 1.0);

}

The shader's texturemap uniform gets bound to the same ViewportTexture as the Sprite itself; the latter seems actually only necessary due to the issue described in https://github.com/godotengine/godot/issues/7998

This setup kind of works, but since the ViewportTexture's background turns out to be black (in spite of the Viewports's transparent_bg property set to true) the glow also fades towards black rather than transparency. I need to do some additional fiddling here...

One more problem is that I use a fixed 3x3 kernel for blurring, which has rather limited blurring capacity. Instead, I'd prefer to use the established horizontal/vertical gaussian blurring technique, but this seems to require some means to temporarily store intermediary textures. I couldn't find anything, though, about the possibility to create and use additional texture buffers in fragment shaders -- or did I miss something?

So things are still exciting and under heavy development... Latest version pushed to https://gitlab.com/tcrass/angstrom

Cheers --

Torsten

@tcrass said: TwistedTwigleg,

@TwistedTwigleg said: Sorry the delay in replying, I was busy with other stuff :smile: .

no need to apologize -- I am so grateful that there are any people at all like you who apparently have time for and patience with Godot beginners like me... :)

Well, when I first started I had a lot of help, so I just view it as repaying the help given to me. Besides, helping others helps me learn new and interesting things too. (And I like helping others when/if I can :smile: )

Anyway, I think I know why you are having a problem with the shader. Right now your shader is getting the texture from the screen and displaying it. If you use this shader on a normal sprite outside of a Viewport node, you will not be able to see it because it will look just like the texture that is being rendered to the screen. It would seem to be completely transparent, while in reality it's not but rather it is showing the scene at that exact position.

Right, I guess understand that now. I just thought that pixels modified by draw_* functions would appear on the screen before any custom shader materials kick in.

The reason it is black instead of transparent in your scene is because there is nothing drawn to the viewport yet. Because by default viewports have a black background (though you can set it to transparent in the Transparent BG flag), that is why you are getting a black screen.

No, i don't get a black screen (or a black rectangle where the Mesh is) -- it's just the pixels touched by the draw_* functions which appear black, all other pixels look transparent.

Anyway...

Whoops! Right, that makes sense. I was testing the shader using a ColorRect and so I was getting a black rectangle, which I wrongly assumed was happening everywhere.

Have you looked at the 2D shaders demo on the GitHub Godot repository? I think there is a glow shader that may work. The only potential snag there is, if I recall correctly, the shaders in the demo require there to be a texture for sampling purposes.

...this seems to work only with canvas_item shaders on pixels already on-screen; I tried like a million variations of the textureLod approach to get some blurring, but I just couldn't get any effect -- not in the setup described above, nor in my new scene which looks like follows:

Spatial
+ Sprite3D # <- uses the Viewport as ViewportTexture and the ShaderMaterial described below
+ Viewport # <- with transparent_bg = true
    + Node2D # <- this is where the pixels are drawn to

Shader code:

shader_type spatial;

render_mode unshaded;

uniform vec2 dx = vec2(1, 0);
uniform vec2 dy = vec2(0, 1);
uniform float offset = 0.05;
uniform float strength = 0.1;

uniform sampler2D texturemap : hint_albedo;

void fragment() {
    ALBEDO = (
        0.0625 * strength * texture(texturemap, UV + offset * (-dx +dy))
        + 0.125 * strength * texture(texturemap, UV + offset * (+dy))
        + 0.0625 * strength * texture(texturemap, UV + offset * (+dx +dy))
        + 0.125 * strength * texture(texturemap, UV + offset * (-dx))
        + 1.25 * texture(texturemap, UV)
        + 0.125 * strength * texture(texturemap, UV + offset * (+dx))
        + 0.0625 * strength * texture(texturemap, UV + offset * (-dx -dy))
        + 0.125 * strength * texture(texturemap, UV + offset * (-dy))
        + 0.0625 * strength * texture(texturemap, UV + offset * (+dx -dy))
    ).rgb;

    ALPHA = min(length(ALBEDO.rgb) * 5.0, 1.0);

}

The shader's texturemap uniform gets bound to the same ViewportTexture as the Sprite itself; the latter seems actually only necessary due to the issue described in https://github.com/godotengine/godot/issues/7998

This setup kind of works, but since the ViewportTexture's background turns out to be black (in spite of the Viewports's transparent_bg property set to true) the glow also fades towards black rather than transparency. I need to do some additional fiddling here...

I think that is because the _draw function does not give the shader a texture, and the shaders in the demo require there to be a texture to work. Unfortunately I'm not well versed in shaders, and you have gotten way farther than I would have.

One more problem is that I use a fixed 3x3 kernel for blurring, which has rather limited blurring capacity. Instead, I'd prefer to use the established horizontal/vertical gaussian blurring technique, but this seems to require some means to temporarily store intermediary textures. I couldn't find anything, though, about the possibility to create and use additional texture buffers in fragment shaders -- or did I miss something?

You can add more textures to a shader using uniform sampler2D texture_name_here, but then the textures have to be passed in from the editor (or through a script). The documentation shows a few hints you can give the texture so it responds correctly in the editor and/or when there are no values inputted.

I am not sure if that is helpful though.

So things are still exciting and under heavy development... Latest version pushed to https://gitlab.com/tcrass/angstrom

Cool! I wish you the best of luck with your project, and if you have any questions, let me know and I'll do my best to help. I do not know much (especially about shaders) but I do not mind trying to help if I can :smile:

Edit: I just thought of something. In the shader on your MeshInstance, od you have the Transparency property checked? Perhaps the reason everything is black is because the SpatialMaterial is not rendering transparency.

TwistedTwigleg,

Whoops! Right, that makes sense. I was testing the shader using a ColorRect and so I was getting a black rectangle, which I wrongly assumed was happening everywhere. [...] You can add more textures to a shader using uniform sampler2D texture_name_here, but then the textures have to be passed in from the editor (or through a script). The documentation shows a few hints you can give the texture so it responds correctly in the editor and/or when there are no values inputted. I am not sure if that is helpful though.

thank you so much for all your input, but after one more night of shader experiment I think I'll drop this kind of business for now. Having a glow effect for the reticle would have been cool, but I feel I should now focus on the application's actual intended functionality.

I wish you the best of luck with your project, and if you have any questions, let me know and I'll do my best to help. I do not know much (especially about shaders) but I do not mind trying to help if I can :smile:

Well, there's a high probability that this project will end up in my vast pile of other unfinished projects... ;) But we'll see!

Edit: I just thought of something. In the shader on your MeshInstance, od you have the Transparency property checked? Perhaps the reason everything is black is because the SpatialMaterial is not rendering transparency.

I think I tried every possible combination of transparency settings of all nodes involved, but as soon as a spatial shader came into play the background turned black. It seems shaders have a very special relationship to "indirect" textures, as one might describe ViewportTextures. Maybe some day, when Godot's shaders get a more exhaustive documentation, things will become clear...

So thanks again, and see you in the next thread to come... ;)

Torsten

@tcrass said: TwistedTwigleg,

Whoops! Right, that makes sense. I was testing the shader using a ColorRect and so I was getting a black rectangle, which I wrongly assumed was happening everywhere. [...] You can add more textures to a shader using uniform sampler2D texture_name_here, but then the textures have to be passed in from the editor (or through a script). The documentation shows a few hints you can give the texture so it responds correctly in the editor and/or when there are no values inputted. I am not sure if that is helpful though.

thank you so much for all your input, but after one more night of shader experiment I think I'll drop this kind of business for now. Having a glow effect for the reticle would have been cool, but I feel I should now focus on the application's actual intended functionality.

That is probably best. You can always add it later if you need to, and sometimes taking a step back can help find solutions. Many times after I set a problem down, I find a solution shortly after (general at a inconvenient moment too :lol: )

I wish you the best of luck with your project, and if you have any questions, let me know and I'll do my best to help. I do not know much (especially about shaders) but I do not mind trying to help if I can :smile:

Well, there's a high probability that this project will end up in my vast pile of other unfinished projects... ;) But we'll see!

I have my own mountain of unfinished projects, so I definitely know how that goes. Regardless of where it ends up, I wish you the best of luck :smile:

Edit: I just thought of something. In the shader on your MeshInstance, od you have the Transparency property checked? Perhaps the reason everything is black is because the SpatialMaterial is not rendering transparency.

I think I tried every possible combination of transparency settings of all nodes involved, but as soon as a spatial shader came into play the background turned black. It seems shaders have a very special relationship to "indirect" textures, as one might describe ViewportTextures. Maybe some day, when Godot's shaders get a more exhaustive documentation, things will become clear...

Yeah, I’m not sure why ViewportTextures act so strangely. From my experience, ViewportTextures are especially flakey with SpatialMaterials, though I have had bad luck with them and Sprites as well.

As you said, hopefully Godot shaders will get more exhaustive documentation, as currently it is fairly lacking. Though to be fair, I have little shader experience, so that may contribute to my view of things. One day I’ll learn how to write GLSL code and maybe then it will make a bit more sense...

So thanks again, and see you in the next thread to come... ;)

No problem, though I am not sure I provided much (if any) help. As for the next thread, I am looking forward to it! :smile: