CanvasItem light shaders: how to constrain lighting to texture resolution?
Asked Answered
O

13

0

My task should be very simple: constrain the lighting caused by a Sprites normal_map to the original textures pixel resolution. reduce all lighting to the resolution of my Sprites texture. I want to get rid of smooth lights/shadows destroying the pixel look. This is a sample of what I'd like to achieve, generated using a Viewport (sadly, I cannot use Viewports for my game):

sample of pixelated lights/shadows

In the docs it says

The light() processor runs per pixel too, and it runs once for every light that affects the object. [..]

So it should be easily possible to write the same LIGHT values to all screen pixels inside the respective regions of texture pixels e.g. by

void light() 
{
	vec2 pixel = round(UV / TEXTURE_PIXEL_SIZE);
	pixel = pixel * TEXTURE_PIXEL_SIZE;

    // only a test to see if we still have smooth lighting
    LIGHT = vec4(pixel.x, pixel.y, 1.0, 1.0);
}

However, the lighting is still smooth. Applying "pixelate" shaders like 2 in the fragment() part cannot eliminate the smooth lighting either, although fragment() somehow also is responsible during AT_LIGHT_PASS. In fact, no code in fragment() or light() seems to be able to generate pixelated light.

Do I miss something? Where exactly in the fragment() part are the smooth sub-pixels written? And what is the architecture / design principle behind the built-in CanvasItem light shaders? Is there any official example for how to use the 2D light() processor?

I have read all the docs (3, 4) and a lot of discussion (5, 6, 7) but didn't find any satisfying answe. So any hint is highly appreciated. An example project can be found under 8.

Oralle answered 12/2, 2022 at 17:19 Comment(0)
R
0

You can try disabling the filtering on the viewport texture. This should make it pixelated. You do this by grabbing the TextureImage from the root Viewport and then setting flags to 0. However, it appears your art and the resolution of the window are not at the same dimensions. So that will eliminate the smoothing, but it won't match up 1:1 pixel wise. But maybe that will look good enough.

Rotz answered 12/2, 2022 at 18:13 Comment(0)
O
0

@cybereality said: You can try disabling the filtering on the viewport texture.

Thanks for your answer. So you assume the shadings smoothness comes from filters in the ViewportTexture? How is it then that the unshaded pixels are not smooth? Nevertheless, changing those flags has no effect. The smoothing really seems to be happening in the shaders somehow.

Oralle answered 13/2, 2022 at 8:21 Comment(0)
R
0

I'm not sure because I mostly work in 3D. Can you send me a simple example project of the issue? I can probably fix it if I can edit the project myself.

Rotz answered 13/2, 2022 at 8:55 Comment(0)
O
0

@cybereality, it seems you had the right intuition: I didn't turn off the filter during import of the height map :# . With the filter turned off at least the shading is pixel-aligned. However, I still have kind of the same problem as the external lights texture is superimposed on the original texture and there seems no way to do a pixelation using the shaders. I have created 1 a minimal project with a commented shader attached to Textures ShaderMaterial.

Thanks a lot for your help.

Oralle answered 13/2, 2022 at 21:1 Comment(0)
R
0

Okay, I'll take a look in a moment.

Rotz answered 13/2, 2022 at 21:27 Comment(0)
O
0

I have updated the title and text and added a GIF of what I'd like to achieve. @cybereality have you been able to take a look at the project?

Oralle answered 16/2, 2022 at 20:39 Comment(0)
R
0

I just took a look, and I'm not sure. I noticed if I set the depth of the Light2D to a smaller value (I used 4) it does look better, but there is still some gradient. I tried the other settings, but none of them seemed to help. I might have to look if a shader can fix it, but this will take more time.

Rotz answered 16/2, 2022 at 21:16 Comment(0)
O
0

@cybereality said: I might have to look if a shader can fix it

I think it will require a shader. However, ultimate light calculation seems to be out of user reach. It would be elegant to have a final fragment() pass to just do with the texture (or light) whatever the user wants. I am curious of what a shader expert would have to say about this :).

Oralle answered 17/2, 2022 at 13:44 Comment(0)
R
0

You can also make your game window the same pixel size as your sprites. So the window pixel is exactly 1 pixel for the sprite. This should work (you can use scaling options like shrink to get the window size larger, but this will also affect text and UI).

Rotz answered 17/2, 2022 at 14:47 Comment(0)
O
0

@cybereality said: You can also make your game window the same pixel size as your sprites.

That's not going to work as different objects should transform smoothly on screen.

I found the light texture lookup here:


vec2 light_uv = light_uv_interp.xy;
vec4 light = texture(light_texture, light_uv);

light_uv_interp is calculated here:

light_uv_interp.xy = (light_matrix * outvec).xy;

It should be possible to snap light lookup to the original textures pixel coordinates by discretization of light_matrix or outvec. It would be even better to average over the pixels using sort of a buffer. Unfortunately I am very new to shaders. Any suggestions?

Oralle answered 18/2, 2022 at 8:20 Comment(0)
R
0

Yeah, if you stepify light_uv, that may work.

Rotz answered 18/2, 2022 at 8:36 Comment(0)
O
0

Not directly, I have to somehow stepify outvec before calculating light_uv_interp because it resides in the desired texture space. However, I don't understand what outvec is. It seems not to be between 0 and 1 so that it can be simply stepified using color_texpixel_size.

Oralle answered 18/2, 2022 at 12:49 Comment(0)
O
0

Sorry, I need some more help on this one. light_matrix seems to convert directly from view space to texture coordinates (is this clip space?). So what I need are pixelized view space coordinates in outvec. Can this be done by converting them to texture coordinates (is this the clip space via projection_matrix outvec?), discretize them to texture resolution and then convert back into view space (inverse(projection_matrix) uv)?

As an alternative, is it somehow possible to simply have a second fragment shader pass?

Edit: this is what I am trying:


light_uv_interp.xy = (light_matrix * outvec).xy;
light_uv_interp.zw = (light_local_matrix * outvec).xy;

highp vec2 uv_px = ((projection_matrix * outvec).xy * 0.5 + vec2(0.5, 0.5));
uv_px = round(uv_px / color_texpixel_size + vec2(0.5, 0.5)) * color_texpixel_size;

highp vec4 outvec_px = inverse(projection_matrix) * vec4(uv_px * 2 - vec2(1.0, 1.0), 0.0, 1.0);

light_uv_interp_px.xy = (light_matrix * outvec_px).xy;

vec4 light = texture(light_texture, light_uv_interp_px.xy);

light_uv_interp_px is passed via vertex out / fragment in.

Oralle answered 18/2, 2022 at 15:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.