Open choboy46 opened 1 month ago
Are you referring to a gradient changing the letters' colors vertically (i.e. all letters have the same gradient applied) or horizontally (i.e. each letter has a slightly different color compared to the previous one)? If this is about drawing a gradient on each letter, there's already a proposal tracking this: https://github.com/godotengine/godot-proposals/issues/2564
If you want to color each letter individually, this can already be achieved using RichTextLabel's [color]
tag (it could be scripted so you don't have to enter them manually) or by writing a custom RichTextEffect. Either way, I don't think this should be implemented in _draw()
as it's generally only used in low-level functionality (such as performance-critical scenarios). Higher-level GUI functionality is generally implemented in nodes instead.
Vertically right now, but horizontally is another thing I look forward to. The problem that I am facing here is how font modulation works with the shaders. Normally, I would be able to change the text color mid string using a single node. However, shaders do not account for modulation, which is a problem because in order to achieve what I wanted to achieve I would need several separate duplicates of the node drawing the text in order to display different colors with the same gradient effect applied.
RichTextEffect
only allow setting a single color for each glyph, so it won't work with gradient.
You can do text with gradient in various ways, but it will require at least one node per string/effect:
Label
node with clip children enabled and TextureRect
with gradient texture as a child).Label
+ ShaderMaterial
, I might be missing something, but seems like canvas shaders do not have transform or rect of the canvas item exposed, so you'll probably need to connect item_rect_changed
and pass it as a custom uniform, unless you want gradient to be defined in screen/viewport space).Both work, but can't be directly used with complex controls like RTL.
draw_string_gradient
seems a bit too specific, but draw_string_with_texture
or/and an option to apply texture in the RichTextEffect
might be useful.
Small update here, I figured out that you can access the modulation color from the vertex function by saving it in a varying vec4 variable. However, I still face issues with maintaining the same gradient across all characters due to the inconsistent UVs.
However, I still face issues with maintaining the same gradient across all characters due to the inconsistent UVs.
In case of text, UV
is for the glyph texture atlas, you can use SCREEN_UV
or VERTEX
position instead:
shader_type canvas_item;
uniform sampler2D gradient_texture;
uniform vec2 text_size;
varying vec2 vertex_coord;
void vertex() {
vertex_coord = VERTEX / text_size;
}
void fragment() {
COLOR = textureLod(gradient_texture, vertex_coord, 0.0) * COLOR;
}
Was actually making use of the mix() function but thanks for the advice! Anyway, I think its worth mentioning here that this code also creates a sort of a text shadow as well.
shader_type canvas_item;
uniform bool enable = false;
uniform float fnt_ht;
uniform float vspace = 32;
uniform float mult;
const vec4 white = vec4(1.0, 1.0, 1.0, 1.0);
const vec4 dkgray = vec4(64.0 / 255.0, 64.0 / 255.0, 64.0 / 255.0 , 1.0);
const vec4 navy = vec4(0.0, 0.0, 128.0 / 255.0, 1.0);
const vec4 black = vec4(0.0, 0.0, 0.0, 1.0);
varying float vertex_mix;
varying vec4 vertex_color;
varying vec2 vertex_pass;
void vertex() {
if (enable) {
float vm = VERTEX.y / (fnt_ht * mult);
float m = vspace / fnt_ht;
vertex_mix = mod(vm, m);
vertex_color = COLOR;
vertex_pass = VERTEX;
}
}
void fragment() {
if (enable) {
vec4 c = texture(TEXTURE, UV);
if (c.a != 0.0) {
vec4 gradient_color = mix(white, vertex_color, vertex_mix);
COLOR = gradient_color;
}
else {
vec2 tex_size = vec2(textureSize(TEXTURE, 0));
if (UV.x > 0.0 && UV.y > 0.0 && texture(TEXTURE, UV - vec2(1.0 / tex_size.x, 1.0 / tex_size.y)).a != 0.0) {
if (vertex_color == white) {
COLOR = mix(dkgray, navy, vertex_mix);
}
else {
COLOR = mix(vertex_color, black, 0.7);
}
}
else {
COLOR = vec4(0.0, 0.0, 0.0, 0.0);
}
}
}
}
Describe the project you are working on
A 2D sprite based game in Godot 4.3, with a dialogue system that's supposed to be able to display colored gradient text (makes use of the _draw() function).
Describe the problem or limitation you are having in your project
One of the previous engines I've used allowed for freely drawing with built in functions, so it delighted me to learn that Godot had its own _draw() function. However, I soon came to realize that it lacked an inherent ability to draw text with a gradient effect. Sure, I was able to figure out how to create such an effect using shaders, but that only solved part of the problem. I wanted to be able to draw multiple texts of varying colors which all had the gradient effect applied, but the way I was doing it could only allow for one gradient color per node. Moreover, only one shader effect can be applied per node at a time, and using multiple nodes to achieve my desired effect seems to be impractical.
Describe the feature / enhancement and how it helps to overcome the problem or limitation
A possible solution without needing to change how shaders work is to add a draw function that enables users to draw a string/char with a gradient color. Users should be able to choose the direction which the gradient is drawn in. This way, you can draw multiple different colored gradient texts from a single _draw() function. Alternatively, provide a way to change shader properties in the middle of the _draw() function so that different shader properties are able to be applied to different draws.
Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams
new draw function example:
draw_string_gradient(font, pos, "Gradient Text", gradient1, font_size)
draw_string_gradient(font, pos, "Gradient Text2", gradient2, font_size)
With a function such as this, users have a simpler way to draw text with different gradient effects applied without other draws in the same function being affected, removing the necessity to create excessive nodes.If this enhancement will not be used often, can it be worked around with a few lines of script?
As I've mentioned before, you could hypothetically achieve this by creating multiple nodes and give them each a shader with different uniform variables, but this would not be very practical in the context of a dialogue system.
Is there a reason why this should be core and not an add-on in the asset library?
This could improve accessibility for people who are new to Godot, especially for those who are used to working with other engines.