Earlier today (20240620221253) I ran into the problem that ViewportTexture
does not have mipmaps.
I realized that to solve this problem without generating mipmaps, we would need to:
- determine the LOD to use
- linearly interpolate between the current mipmap and the next mipmap (using
mix
) - average the colors from each of the pixels that would be in the mipmap at that LOD
When I was streaming earlier today, I got a little tripped up on determining what LOD to use, but apparently there’s a function to do just that called textureQueryLod
. With that, we can skip figuring out what LOD to use and just let Godot do that for us:
shader_type spatial;
uniform sampler2D albedo_texture: filter_nearest;
vec4 sample(vec2 uv, int lod) {
ivec2 size = textureSize(albedo_texture, 0);
vec2 uv_step = 1.0 / vec2(size);
int block = lod * lod;
int count = 0;
vec4 color = vec4(0.0);
for (int i = -block/2; i <= block/2; i++) {
for (int j = -block/2; j <= block/2; j++) {
vec2 delta = vec2(uv_step.x * float(i), uv_step.y * float(j));
color += texture(albedo_texture, uv + delta);
count += 1;
}
}
return color / float(count);
}
vec4 samplef(vec2 uv, float lod) {
int low_lod = int(lod);
int high_lod = int(lod + 1.0);
return mix(sample(uv, low_lod), sample(uv, high_lod), mod(lod, 1.0));
}
void fragment() {
float lod = textureQueryLod(albedo_texture, UV).y;
ALBEDO = samplef(UV, lod).xyz;
}
This fixes the pixel shimmering and improves the image quality when zoomed out!