Open mk56-spn opened 1 year ago
I'm fairly new to godot and I'm not sure if TEXTURE_PIXEL_SIZE is meant to be size of the control / target or the size of the texture passed to it via the texture / sprite field of the node.
The documentation state "Normalized pixel size of default 2D texture. For a Sprite2D with a texture of size 64x32px, TEXTURE_PIXEL_SIZE = vec2(1/64, 1/32)" which seem to indicate the second one, but it's not really clear. For instance what does "default" here mean ? If TEXTURE_PIXEL_SIZE is the size of the output, then document could probably better reflect that.
I understand it would not make sense for Node2d as the size of the node is the size of the texture (scaled) but, for control, it is rarely consistent.
I'm fairly new to godot and I'm not sure if TEXTURE_PIXEL_SIZE is meant to be size of the control / target or the size of the texture passed to it via the texture / sprite field of the node.
The documentation state "Normalized pixel size of default 2D texture. For a Sprite2D with a texture of size 64x32px, TEXTURE_PIXEL_SIZE = vec2(1/64, 1/32)" which seem to indicate the second one, but it's not really clear. For instance what does "default" here mean ? If TEXTURE_PIXEL_SIZE is the size of the output, then document could probably better reflect that.
I understand it would not make sense for Node2d as the size of the node is the size of the texture (scaled) but, for control, it is rarely consistent.
Texture pixel size takes the pixel size ratio of the texture its node holds with no consideration for any applied transforms in my experience . which imo is fine. since people might want to normalise based on texture size whilst still being able to deform via scaling.
but yeah for control its not very useful, minus ofc for texture rect and such, but even in that case having the actual size would be of use for some custom texture scaling solutions
+1 to this, it would be very convenient to know it the node you're drawing on has been scaled so you can fix any aspect ratio issues.
My understanding of a poor workaround: Use a script and pass in the size through the material.set_shader_param()
Are there any shader only ways to find out the canvas size?
There is sort of a workaround, only for aspect-ratio though, not for knowing scale relative to a base scale.
You can calculate the aspect-ratio of the canvas rect in-shader, by running your UV through fwidthFine() and then dividing one axis of that by the other, and multiplying the axis of the UV by the same axis that was the divisor of that division.
Example:
vec2 uv = UV;
vec2 uv_deriv = fwidthFine(uv);
float aspect_ratio = uv_deriv.y / uv_deriv.x;
uv.x *= aspect_ratio;
This will result in a UV that is always square. If you scale the rect horizontally, the Y scale will remain the same, and the texture will repeat over the X axis (like "Fit Width Proportional":
If you swap the aspect ratio division and then multiply the uv.y
instead, then it will instead repeat on the Y axis and scale to fit on the X axis.
For those using the Visual Graph, for fwidthFine()
DerivativeFunc node is what you want, set to Vector2/Sum/Fine.
Actually, scratch that last message. Even better, just decompose the transform matrix and multiply vertex value.
// Get the X and Y scale transform of our Canvas element
vec2 scale = vec2(MODEL_MATRIX[0][0], MODEL_MATRIX[1][1]);
// Multiply our Vertex position by it to turn it into essentially a "square UV" that maintains its relative size no matter how much you scale the asset.
vec2 squared_uv = VERTEX * scale;
// This is essentially custom UVs mapped to "canvas pixels" now.
// THIS SHOULD BE CALCULATED IN THE VERTEX() program and passed to FRAGMENT()
You can use that value instead of your actual UV value then. It will basically be a "canvas-space-mapped UV" with aspect-correction, not caring about scale or dimensions, but the space it takes up on the canvas.
Or if you'd like, just use the scale
values to adjust your UV however you'd want, or to compute aspect-ratio by dividing one axis by the other.
This will be more consistent than using the screen-space derivatives approach in my previous comment, and more performant.
If you'd like to do a UV that maintains aspect and position/rotation, but keeps itself scaled to screen-size, you can do something like:
vec2 cscale = vec2(CANVAS_MATRIX[0][0], CANVAS_MATRIX[1][1]);
vec2 scale = vec2(MODEL_MATRIX[0][0], MODEL_MATRIX[1][1]);
vec2 squared_canvas_screen_uv = VERTEX * scale * cscale;
If you'd like to know the exact canvas-pixel dimensions of your element, there is a way to calculate that in-shader as well.
In your VERTEX() program, pass VERTEX
as a separate vec2 vert_cs
varying to your FRAGMENT() program.
(If you want it to also take scale into account, then pass VERTEX * vec2(MODEL_MATRIX[0][0], MODEL_MATRIX[1][1])
)
Then, in your FRAGMENT(), you can calculate the pixel dimensions like so:
vec2 uv_ratio = 1.0 / UV.xy;
//Multiply the current canvas pixel coordinate by our ratio, to raise current value up to the maximum
vec2 resolution = vert_cs * uv_ratio;
resolution
will now be a constant value across the whole element that holds its X and Y pixel size
that you set in the Inspector.
Actually, scratch that last message. Even better, just decompose the transform matrix and multiply vertex value.
// Get the X and Y scale transform of our Canvas element vec2 scale = vec2(MODEL_MATRIX[0][0], MODEL_MATRIX[1][1]); // Multiply our Vertex position by it to turn it into essentially a "square UV" that maintains its relative size no matter how much you scale the asset. vec2 squared_uv = VERTEX * scale; // This is essentially custom UVs mapped to "canvas pixels" now. // THIS SHOULD BE CALCULATED IN THE VERTEX() program and passed to FRAGMENT()
You can use that value instead of your actual UV value then. It will basically be a "canvas-space-mapped UV" with aspect-correction, not caring about scale or dimensions, but the space it takes up on the canvas.
Or if you'd like, just use the
scale
values to adjust your UV however you'd want, or to compute aspect-ratio by dividing one axis by the other.This will be more consistent than using the screen-space derivatives approach in my previous comment, and more performant.
If you'd like to do a UV that maintains aspect and position/rotation, but keeps itself scaled to screen-size, you can do something like:
vec2 cscale = vec2(CANVAS_MATRIX[0][0], CANVAS_MATRIX[1][1]); vec2 scale = vec2(MODEL_MATRIX[0][0], MODEL_MATRIX[1][1]); vec2 squared_canvas_screen_uv = VERTEX * scale * cscale;
If you'd like to know the exact canvas-pixel dimensions of your element, there is a way to calculate that in-shader as well.
In your VERTEX() program, pass
VERTEX
as a separatevec2 vert_cs
varying to your FRAGMENT() program. (If you want it to also take scale into account, then passVERTEX * vec2(MODEL_MATRIX[0][0], MODEL_MATRIX[1][1])
)Then, in your FRAGMENT(), you can calculate the pixel dimensions like so:
vec2 uv_ratio = 1.0 / UV.xy; //Multiply the current canvas pixel coordinate by our ratio, to raise current value up to the maximum vec2 resolution = vert_cs * uv_ratio;
resolution
will now be a constant value across the whole element that holds its X and Y pixelsize
that you set in the Inspector.
thanks for all this great knowledge!
not sure who to tag but given @Calinou seems to be the maintainer involved with this thread ill ping him
any chance this could be added to documentation in any form? its quite hard to figure this stuff out for most people unless they happen across this specific thread while googling an answer. i get that its a workaround but given it could be years before someone gets around to doing a proper implementation it might be worthwhile
I'd personally like to have these examples in the official docs, but I think they might be slightly too niche and technical to add? We currently don't really keep many applied examples in the shader docs, except for the minimum examples required to demonstrate how the shading language and the built-in variables work.
I think if these were to be added, it could be as part of documentation for how different coordinate spaces in 2D works. That would probably be in the CanvasItem shaders page. I'll look into it.
This is exactly the kind of content that the user-contributed notes are good for, though! @Invertex, you'd be welcome to repost your examples as a comment on https://docs.godotengine.org/en/stable/tutorials/shaders/shader_reference/canvas_item_shader.html. Note that while giscus only exposes a minimal UI, you can edit your comment on github afterwards if you need more complex formatting.
Describe the project you are working on
A game using a large amount of shaders which need to be applied to dynamically sized UI elements.
Describe the problem or limitation you are having in your project
Often times i find myself wanting to use a colour rect of an arbitrary width and height to project a pattern onto which i do not wish to be distorted by its aspect ratio or which i want to render items onto that need to respect a given size in pixels.
Unfortunately since texture pixel size does seemingly nothing on a colour rect ( i understand it doesn't have a texture but this feels horrible for usability) this is currently quite the pain to do
the responses i've seen on the internet tend to be something along the lines of " pass in those values manually via a custom uniform" (https://godotforums.org/d/21403-trying-to-fix-scale-of-a-circle/4) which is cumbersome and ruins the workflow forcing you to make a script just to pass in something as basic as the size of a canvas item
Describe the feature / enhancement and how it helps to overcome the problem or limitation
pass in the size of a colour rect via "texture pixel size" or a comparable parameter, it can then be used to provide aspect ration corrections easily on dynamically sized color rect nodes
Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams
I don't think this category is meaningfully distinct from the previous one . But i guess something like CANVAS_PIXEL_SIZE for control node sizes would be ideal.
ex:
fragment void(){ vec2 normalisedUV = UV / CANVAS_PIXEL_SIZE; }
to scale down everything to an aspect ratio correct pixel scale .
If this enhancement will not be used often, can it be worked around with a few lines of script?
For one shader yes. for 5 its a pain, when you depend on it for a lot of shaders then it becomes a major nuisance
Is there a reason why this should be core and not an add-on in the asset library?
because its simple functionality which tonnes of shaders depend on. take your pick of practically any shader on https://www.shadertoy.com/ for example and it'll use normalised coordinates. Even something as simple as a non deformed circle within a dynamically sized canvas requires it