@vmjcv said:
This is what i want,Thank you very much for your help
Awesome, happy to help!
If you have time, can you explain the code to me.
i think clamped_UV is vec2(1.0, 1.0) if UV.x > 1.0 or UV.y > 1.0 . so the line will be a same color in the second half, i think it will like this (image omitted to save space)
but the code is magic.it can get this result: (image omitted to save space)
i want to know why you do it ,and the result is right.
I can try to explain how it works, though my explanation may be poor due to my lack of shader experience. There are two problems that the shader fixes: The gradient needs to be mapped from the first point to the second point, and the dashes need to tile across the length of the line.
First, let's quickly cover how UV mapping works in relation to this problem, as its an important concept to understand that makes the shader make sense. Keep in mind, this is the super fast and dirty explanation, and if you are curious to learn more, I would highly suggest doing additional research.
UV mapping tells Godot where to place pixels from the texture when drawing. In 2D, most everything is actually a rectangle composed of four vertices. For Line2D, we can just assume that for each point in the line, there are two vertices (a safe assumption for the most part).
Each vertex has a UV coordinate, which is in the range of 0
to 1
. (0, 0)
is the top left corner, while (1, 1)
is the bottom right corner. If a coordinate goes over the UV range, one of two things happens: If the texture is set to repeat, it rolls the UV coordinate back into the 0-1 range and draws the texture as required (essentially, it's a bit more complicated but...), or the coordinate is clamped to the nearest valid position (so (2, -1)
becomes (1, 0)
, as that's the closest valid position).
I probably explained it poorly, so it it doesn't seem to make sense, please look into how UV mapping works from other developers who explain it better than I am here.
First, we need to make the dashed line texture repeat across the length of the entire Line2D node, but without relying on the texture being set to repeat. To make the texture repeat, we need to take the coordinates that are outside of the 0-1 range and place them into the 0-1 range. To do this, we look to see if a coordinate is higher than the given range, and if it is, we subtract 1 until it's in the correct range.
How the shader works:
We get the UV coordinate and assign it to a variable (working_uv
) and then check if it's coordinates are over the 0-1 range. If it is, then we subtract 1
from the UV coordinates until its in the 0-1 range using a while loop. Then, instead of passing the UV coordinates to the dashed line texture, we instead pass the UV we processed (working_uv
).
To put it another way: what we're doing is similar to how the space ship in Asteroids wraps around when it hits the edges of the screen. This makes the dashed line texture repeat as expected, and in 95% of cases it gives the correct result (there are a few edge cases where it may not, but they're unlikely to occur)
Now, to handle the issue of the gradient, we need to tackle the issue of the UV coordinates being out of range, but instead of repeating, we want to instead push the out-of-range vertex UV coordinates to the nearest edge that is in range. This will cause the texture to properly apply the gradient starting from the first point (UV coordinates (0, 0)
) to the second point (given coordinates: (3, 2.5)
-> code -> desired result: (1, 1)
).
To do this, we need to use the vertex
function. This function is called on each of the vertices in the Line2D, unlike the fragment
function which is called per pixel drawn. However, because we will need to pass the UV position to the fragment so we sample the right part in the texture, we need to define a varying
variable, which is simply a variable that we can populate in vertex
and access in fragment
.
The code in vertex
is fairly simple: We check to see if the UV coordinates of the vertex we are processing is out of range on either the X or Y axis. (line 3). If the UV coordinates are out of range, we set clamped_UV
to (1, 1)
; This will take coordinates like (5, 2)
and map them to (1, 1)
. If the UV coordinates are not outside of range, we set clamped_UV
to whatever the passed-in UV coordinates are (UV
).
Then, in the fragement
function, instead of passing UV
as the UV coordinates when sampling the gradient texture, we pass clamped_UV
instead. For the first point in the Line2D, this will not really do anything (the UV coordinates are (0, 0)
and do not need adjusting), but for the second point in the Line2D, this will map it's UV coordinates to the maximum position in the texture if the UV coordinates are out of bounds ((8, 3)
becomes (1, 1)
).
(Note: This only really works for a Line2D with just two points. For more than two points, I'd recommend using a different approach (like manually drawing the line using the _draw
function in a Node2D))
I wrote the whole thing in a single go, and as I mentioned, my shader experience is limited at best, so I'm sure the explanation could be improved. That said, hopefully now it makes a bit more sense on what is going on. It's really just a matter of clamping (for the gradient) and wrapping (for the dashed-line) UV coordinates so they are where you want them for each texture.