bevy-magic-light-2d icon indicating copy to clipboard operation
bevy-magic-light-2d copied to clipboard

Camera zooming issues

Open morr opened this issue 10 months ago • 7 comments

Hi!

While zooming scene out the screen is being "split" into a "grid" and around each "grid cell" I observe black areas. The more I zoom out the larger black areas become.

Any ideas how to fix this?

Details provided below:

Here it is illustrated in krypta demo app (I added zoom to fork https://github.com/zaycev/bevy-magic-light-2d/compare/main...morr:bevy-magic-light-2d:zoom-out-issue) image

And this is how the issue looks in my bevy app where I try very large zoom. image image image image

morr avatar Apr 13 '24 16:04 morr

Updated the description of the issue, removed unnecessary details.

morr avatar Apr 13 '24 19:04 morr

Known issue. The problem is likely due to a bug in SDF. Thanks for putting all details.

zaycev avatar Apr 14 '24 02:04 zaycev

@morr I would try something like this for your game.

  1. Change SDF to min operator instead of round merge. That would make walls completely black as no light can go into negative space:
@compute @workgroup_size(8, 8, 1)
fn main(@builtin(global_invocation_id) invocation_id: vec3<u32>) {
     let texel_pos  = vec2<i32>(invocation_id.xy);
     let dims = textureDimensions(sdf_out);
     let uv = (vec2<f32>(texel_pos) + 0.5) / vec2<f32>(dims);

     let world_pose = sdf_uv_to_world(uv,
        camera_params.inverse_view_proj,
        camera_params.sdf_scale);
    let r = 1.2;

     var sdf_merged = sdf_aabb_occluder(world_pose.xy, 0);
     for (var i: i32 = 1; i < i32(light_occluder_buffer.count); i++) {
        sdf_merged = min(sdf_merged, sdf_aabb_occluder(world_pose.xy, i));
     }

    textureStore(sdf_out, texel_pos, vec4<f32>(sdf_merged, 0.0, 0.0, 0.0));
}

  1. To fix zero irradiance for walls, try to sample it from neighbor pixels (in gi_post_processing.wgsl):
@fragment
fn fragment(in: VertexOutput) -> @location(0) vec4<f32> {
    let position = in.position;
    let uv = coords_to_viewport_uv(position.xy, view.viewport);

    // Read diffuse textures.
    let in_floor_diffuse   = textureSample(in_floor_texture,   in_floor_sampler, uv);
    let in_walls_diffuse   = textureSample(in_walls_texture,   in_walls_sampler, uv);
    let in_objects_diffuse = textureSample(in_objects_texture, in_objects_sampler, uv);

    let in_irradiance = textureSample(in_irradiance_texture, in_irradiance_texture_sampler, uv).xyz;

    // Calculate object irradiance.
    // TODO: parametrize this filter.
    // TODO: we don't really need to do this per pixel.
    var object_irradiance = in_irradiance;
    var walls_irradiance = in_irradiance;

    var k_size = 3;
    var k_width = 28;

    for (var i = -k_size; i <= k_size; i++) {
        for (var j = -k_size; j < 0; j++) {

            let offset = vec2<f32>(f32(i * k_width), f32(j * k_width));
            let irradiance_uv = coords_to_viewport_uv(position.xy - offset, view.viewport);

            let sample_irradiance = textureSample(
                in_irradiance_texture,
                in_irradiance_texture_sampler,
                irradiance_uv
            ).xyz;

            // TODO: Might also need a visibility check here.
            if any(irradiance_uv < vec2<f32>(0.0)) || any(irradiance_uv > vec2<f32>(1.0)) {
                continue;
            }

            object_irradiance = max(object_irradiance, sample_irradiance);
        }
    }

    k_size = 4;
    k_width = 16;
    for (var i = -k_size; i <= k_size; i++) {
        for (var j = -k_size; j < k_size; j++) {

            let offset = vec2<f32>(f32(i * k_width), f32(j * k_width));
            let irradiance_uv = coords_to_viewport_uv(position.xy - offset, view.viewport);

            let sample_irradiance = textureSample(
                in_irradiance_texture,
                in_irradiance_texture_sampler,
                irradiance_uv
            ).xyz;

            // TODO: Might also need a visibility check here.
            if any(irradiance_uv < vec2<f32>(0.0)) || any(irradiance_uv > vec2<f32>(1.0)) {
                continue;
            }

            walls_irradiance += sample_irradiance;
        }
    }

    walls_irradiance /= 16.0;

    let floor_irradiance_srgb   = lin_to_srgb(in_irradiance);
    let objects_irradiance_srgb = lin_to_srgb(object_irradiance);

    let final_floor   = in_floor_diffuse.xyz   * floor_irradiance_srgb;
    let final_walls   = in_walls_diffuse.xyz   * walls_irradiance;
    let final_objects = in_objects_diffuse.xyz * objects_irradiance_srgb;

    var out = vec4<f32>(final_floor, 1.0);
        out = vec4<f32>(mix(out.xyz, final_walls.xyz, in_walls_diffuse.w), 1.0);
        out = vec4<f32>(mix(out.xyz, final_objects.xyz, in_objects_diffuse.w), 1.0);

    return out;
}

It's very hacky, but might work for you. This is what I've got:

Screenshot 2024-04-13 at 10 05 37 PM Screenshot 2024-04-13 at 10 04 48 PM

zaycev avatar Apr 14 '24 03:04 zaycev

@zaycev thanks for quick response! The demo looks better with such fixes.

In my app I started getting black screen after changed shaders code, but this looks like my own fault, probably I did set up something incorrectly.

morr avatar Apr 14 '24 10:04 morr

In my app I started getting black screen after changed shaders code, but this looks like my own fault, probably I did set up something incorrectly.

Naga should tell you in the log if you have syntax error in shaders.

zaycev avatar Apr 14 '24 12:04 zaycev

@morr were you able to fix this? I am experiencing the same issue.

csandven avatar Aug 17 '24 09:08 csandven

@csandven the krypta example now supports zooming and I can't reproduce this issue - can you check if it's still available with 0.8.0 or 0.8.1?

tigerplush avatar Sep 03 '24 16:09 tigerplush