godot icon indicating copy to clipboard operation
godot copied to clipboard

Texture bias does not work in web builds

Open 3booodpro opened this issue 1 year ago • 1 comments

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: 41 https://github.com/user-attachments/assets/c3e9dd3e-06d9-4b6d-a751-5724ec75eb40

web version: image

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

image image image

Minimal reproduction project (MRP)

Glowing.zip

3booodpro avatar Oct 07 '24 17:10 3booodpro

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.

clayjohn avatar Oct 07 '24 17:10 clayjohn

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.

Image

Chrome Desktop Version: 138.0.7204.101 Chrome Mobile Version: 138.0.7204.63

AdamEttenberger avatar Jul 11 '25 09:07 AdamEttenberger