Texture bias does not work in web builds
Tested versions
Godot 4.3 stable
System information
Windows 10 - Compatibility - Godot 4.3 Stable
Issue description
Using any texture method in shaders for html resulting with broken and not working shader.
shader_type canvas_item;
uniform sampler2D screen_texture : hint_screen_texture, filter_linear_mipmap_anisotropic, repeat_disable;
uniform float hpass : hint_range(0.0, 1.0, 0.1) = 1.0;
uniform float vpass : hint_range(0.0, 1.0, 0.1) = 1.0;
uniform int radius : hint_range(0, 65, 1) = 65;
render_mode blend_add;
vec4 textureThresholded(sampler2D _texture, vec2 _uv, float _bias) {
vec4 pixel = textureLod(_texture, _uv, _bias);
if ( pixel.r <= 1. && pixel.g <= 1. && pixel.b <= 1. ) {
pixel.rgb = vec3(0.);
}
return pixel;
}
void fragment() {
vec4 pixel = textureThresholded(screen_texture, SCREEN_UV, 0.);
if (radius != 0) {
vec4 blurred = vec4(0., 0., 0., 1.);
float[65] w = {0.0064, 0.0063, 0.0062, 0.0061, 0.006,
0.0059, 0.0058, 0.0057, 0.0056, 0.0055, 0.0054, 0.0053, 0.0052, 0.0051, 0.005,
0.0049, 0.0048, 0.0047, 0.0046, 0.0054, 0.0044, 0.0043, 0.0042, 0.0041, 0.004,
0.0039, 0.0038, 0.0037, 0.0036, 0.0043, 0.0034, 0.0033, 0.0032, 0.0031, 0.003,
0.0029, 0.0028, 0.0027, 0.0026, 0.0052, 0.0024, 0.0023, 0.0022, 0.0021, 0.002,
0.0019, 0.0018, 0.0017, 0.0016, 0.0051, 0.0014, 0.0013, 0.0012, 0.0011, 0.001,
0.0009, 0.0008, 0.0007, 0.0006, 0.0005, 0.0004, 0.0003, 0.0002, 0.0001, 0.
};
float px = 1. / float(textureSize(screen_texture, 0).x);
float py = 1. / float(textureSize(screen_texture, 0).y);
for(int i = 0; i < radius; i++) {
float k = float(i + 1);
blurred += textureThresholded(screen_texture, SCREEN_UV + vec2(float(i) * cos(float(i)), 0) * vec2(px, py) * vec2(hpass, vpass), k) * w[i];
blurred += textureThresholded(screen_texture, SCREEN_UV + vec2(float(-i) * cos(float(i)), 0) * vec2(px, py) * vec2(hpass, vpass), k) * w[i];
blurred += textureThresholded(screen_texture, SCREEN_UV + vec2(0, float(i) * sin(float(i))) * vec2(px, py) * vec2(hpass, vpass), k) * w[i];
blurred += textureThresholded(screen_texture, SCREEN_UV + vec2(0, float(-i) * sin(float(i))) * vec2(px, py) * vec2(hpass, vpass), k) * w[i];
blurred += textureThresholded(screen_texture, SCREEN_UV + vec2(float(i) * cos(float(i)), float(i) * sin(float(i))) * vec2(px, py) * vec2(hpass, vpass), k) * w[i];
blurred += textureThresholded(screen_texture, SCREEN_UV + vec2(float(-i) * cos(float(i)), float(-i) * sin(float(i))) * vec2(px, py) * vec2(hpass, vpass), k) * w[i];
blurred += textureThresholded(screen_texture, SCREEN_UV + vec2(float(i) * cos(float(i)), float(-i) * sin(float(i))) * vec2(px, py) * vec2(hpass, vpass), k) * w[i];
blurred += textureThresholded(screen_texture, SCREEN_UV + vec2(float(-i) * cos(float(i)), float(i) * sin(float(i))) * vec2(px, py) * vec2(hpass, vpass), k) * w[i];
blurred += textureThresholded(screen_texture, SCREEN_UV + vec2(float(-i) * cos(float(i)), float(i) * sin(float(i))) * vec2(px, py) * vec2(hpass, vpass), k) * w[i];
blurred += textureThresholded(screen_texture, SCREEN_UV + vec2(float(i) * cos(float(i)), float(-i) * sin(float(i))) * vec2(px, py) * vec2(hpass, vpass), k) * w[i];
}
blurred /= float(radius) / 6.;
pixel += blurred;
} else {
pixel = vec4(0., 0., 0., 1.);
}
COLOR = pixel;
}
I was coding a glow post process using shaders to avoid using the world environment node. It was successful, however when exporting this shader to the web it did not work. I was able to figure that the bias in the texture method was the problem. So I tried replace it with textureLod but it had the same issue.
desktop version:
https://github.com/user-attachments/assets/c3e9dd3e-06d9-4b6d-a751-5724ec75eb40
web version:
Steps to reproduce
create a canvas layer node, attach a control and a color rect inside it. make them full rect.
attach this shader code to the color rect.
shader_type canvas_item;
uniform sampler2D screen_texture : hint_screen_texture, filter_linear_mipmap_anisotropic, repeat_disable;
uniform float hpass : hint_range(0.0, 1.0, 0.1) = 1.0;
uniform float vpass : hint_range(0.0, 1.0, 0.1) = 1.0;
uniform int radius : hint_range(0, 65, 1) = 65;
render_mode blend_add;
vec4 textureThresholded(sampler2D _texture, vec2 _uv, float _bias) {
vec4 pixel = textureLod(_texture, _uv, _bias);
if ( pixel.r <= 1. && pixel.g <= 1. && pixel.b <= 1. ) {
pixel.rgb = vec3(0.);
}
return pixel;
}
void fragment() {
vec4 pixel = textureThresholded(screen_texture, SCREEN_UV, 0.);
if (radius != 0) {
vec4 blurred = vec4(0., 0., 0., 1.);
float[65] w = {0.0064, 0.0063, 0.0062, 0.0061, 0.006,
0.0059, 0.0058, 0.0057, 0.0056, 0.0055, 0.0054, 0.0053, 0.0052, 0.0051, 0.005,
0.0049, 0.0048, 0.0047, 0.0046, 0.0054, 0.0044, 0.0043, 0.0042, 0.0041, 0.004,
0.0039, 0.0038, 0.0037, 0.0036, 0.0043, 0.0034, 0.0033, 0.0032, 0.0031, 0.003,
0.0029, 0.0028, 0.0027, 0.0026, 0.0052, 0.0024, 0.0023, 0.0022, 0.0021, 0.002,
0.0019, 0.0018, 0.0017, 0.0016, 0.0051, 0.0014, 0.0013, 0.0012, 0.0011, 0.001,
0.0009, 0.0008, 0.0007, 0.0006, 0.0005, 0.0004, 0.0003, 0.0002, 0.0001, 0.
};
float px = 1. / float(textureSize(screen_texture, 0).x);
float py = 1. / float(textureSize(screen_texture, 0).y);
for(int i = 0; i < radius; i++) {
float k = float(i + 1);
blurred += textureThresholded(screen_texture, SCREEN_UV + vec2(float(i) * cos(float(i)), 0) * vec2(px, py) * vec2(hpass, vpass), k) * w[i];
blurred += textureThresholded(screen_texture, SCREEN_UV + vec2(float(-i) * cos(float(i)), 0) * vec2(px, py) * vec2(hpass, vpass), k) * w[i];
blurred += textureThresholded(screen_texture, SCREEN_UV + vec2(0, float(i) * sin(float(i))) * vec2(px, py) * vec2(hpass, vpass), k) * w[i];
blurred += textureThresholded(screen_texture, SCREEN_UV + vec2(0, float(-i) * sin(float(i))) * vec2(px, py) * vec2(hpass, vpass), k) * w[i];
blurred += textureThresholded(screen_texture, SCREEN_UV + vec2(float(i) * cos(float(i)), float(i) * sin(float(i))) * vec2(px, py) * vec2(hpass, vpass), k) * w[i];
blurred += textureThresholded(screen_texture, SCREEN_UV + vec2(float(-i) * cos(float(i)), float(-i) * sin(float(i))) * vec2(px, py) * vec2(hpass, vpass), k) * w[i];
blurred += textureThresholded(screen_texture, SCREEN_UV + vec2(float(i) * cos(float(i)), float(-i) * sin(float(i))) * vec2(px, py) * vec2(hpass, vpass), k) * w[i];
blurred += textureThresholded(screen_texture, SCREEN_UV + vec2(float(-i) * cos(float(i)), float(i) * sin(float(i))) * vec2(px, py) * vec2(hpass, vpass), k) * w[i];
blurred += textureThresholded(screen_texture, SCREEN_UV + vec2(float(-i) * cos(float(i)), float(i) * sin(float(i))) * vec2(px, py) * vec2(hpass, vpass), k) * w[i];
blurred += textureThresholded(screen_texture, SCREEN_UV + vec2(float(i) * cos(float(i)), float(-i) * sin(float(i))) * vec2(px, py) * vec2(hpass, vpass), k) * w[i];
}
blurred /= float(radius) / 6.;
pixel += blurred;
} else {
pixel = vec4(0., 0., 0., 1.);
}
COLOR = pixel;
}
make sure HDR 2D is enabled in the project settings
Minimal reproduction project (MRP)
Here is the error that gets printed in the console:
[.WebGL-0x37040c685400] GL_INVALID_FRAMEBUFFER_OPERATION: Framebuffer is incomplete: Attachment level is not in the [base level, max level] range.
I suspect this is only an issue when generating mipmaps from a Viewport. We need to ensure that the proper base level and max level are specified before sampling from the backbuffer. The desktop APIs let us get away with having these set up wrong, but the web is much more strict.
Ran into a similar issue with the "Screen Space Shaders Demo", shaders passing non-zero LOD worked on desktop (desktop and web exports), but failed on mobile browsers.
For example, on mobile selecting blur.shader renders a black screen and vignette.shader renders the image unchanged.
Noticed the demo throws the following warning on mobile browsers:
"GL ERROR :GL_INVALID_OPERATION : glDrawArrays: Source and destination textures of the draw are the same."
Non-LOD sampling seems to work fine despite the warning, so that's probably unrelated.
Tried to avoid the warning in my project by including a BackBufferCopy Node in my popup widget, but that didn't work.
Chrome Desktop Version: 138.0.7204.101 Chrome Mobile Version: 138.0.7204.63