Skip to content

AlphaMode::Blend produces non-opague shape when defined as opague when used with fog #25462

Description

@Saratii

Bevy version and features

0.19.0
0.19.1
and main

What you did

I have a scene with a cuboid with an opague blend material and I noticed something is wrong with the depth when the cuboid is between my fog god ray and the camera causing it to render transparent.

What went wrong

Its suppose to be opague its rendering transparent

Additional information

Image

minimized repro

use bevy::{
    asset::RenderAssetUsages,
    camera::Exposure,
    camera_controller::free_camera::{FreeCamera, FreeCameraPlugin},
    core_pipeline::{prepass::DepthPrepass, tonemapping::Tonemapping},
    light::{FogVolume, VolumetricFog, VolumetricLight},
    post_process::bloom::Bloom,
    prelude::*,
    render::render_resource::{Extent3d, TextureDimension, TextureFormat},
};

const SURFACE_Y: f32 = 0.0;
const BOTTOM_Y: f32 = SURFACE_Y - 45.0;
const GOD_RAY_LIGHT_HEIGHT: f32 = 20.0;
const GOD_RAY_RANGE: f32 = 200.0;
const COLLAR_INNER_RADIUS: f32 = 2.5;
const COLLAR_HEIGHT: f32 = 0.8;
const GOD_RAY_FOG_WIDTH: f32 = 12.0;
const GOD_RAY_BRIGHTNESS: f32 = 3_000_000.0;
const PLAYER_CUBOID_SIZE: Vec3 = Vec3::new(0.5, 1.5, 0.5);
const PLAYER_POSITION: Vec3 = Vec3::new(6.5, -5.0, 0.0);
const EXPOSURE_EV100: f32 = 5.5;

fn main() {
    App::new()
        .add_plugins((DefaultPlugins, FreeCameraPlugin))
        .insert_resource(ClearColor(Color::BLACK))
        .add_systems(Startup, (setup_camera, setup_god_ray, setup_player))
        .run();
}

fn setup_camera(mut commands: Commands) {
    commands.spawn((
        Camera3d::default(),
        Transform::from_xyz(11.5, -4.6, 0.0).looking_at(Vec3::new(0.0, -6.0, 0.0), Vec3::Y),
        FreeCamera::default(),
        DepthPrepass,
        Msaa::Off,
        Exposure {
            ev100: EXPOSURE_EV100,
        },
        Tonemapping::AcesFitted,
        Bloom::NATURAL,
        AmbientLight {
            brightness: 0.0,
            ..default()
        },
        VolumetricFog {
            ambient_intensity: 0.0,
            ..default()
        },
    ));
}

fn setup_god_ray(mut commands: Commands, mut images: ResMut<Assets<Image>>) {
    let outer_angle = (COLLAR_INNER_RADIUS / GOD_RAY_LIGHT_HEIGHT).atan();
    commands.spawn((
        SpotLight {
            color: Color::WHITE,
            intensity: GOD_RAY_BRIGHTNESS,
            range: GOD_RAY_RANGE,
            shadow_maps_enabled: true,
            inner_angle: outer_angle * 0.7,
            outer_angle,
            ..default()
        },
        VolumetricLight,
        Transform::from_xyz(0.0, SURFACE_Y + GOD_RAY_LIGHT_HEIGHT, 0.0)
            .looking_at(Vec3::new(0.0, BOTTOM_Y, 0.0), Vec3::Z),
    ));
    let fog_top = SURFACE_Y + COLLAR_HEIGHT;
    commands.spawn((
        FogVolume {
            density_factor: 0.04,
            absorption: 0.05,
            scattering: 0.5,
            light_intensity: 8.0,
            density_texture: Some(images.add(god_ray_density_image())),
            ..default()
        },
        Transform::from_xyz(0.0, (BOTTOM_Y + fog_top) / 2.0, 0.0).with_scale(Vec3::new(
            GOD_RAY_FOG_WIDTH,
            fog_top - BOTTOM_Y,
            GOD_RAY_FOG_WIDTH,
        )),
    ));
}

fn setup_player(
    mut commands: Commands,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<StandardMaterial>>,
) {
    let mesh = meshes.add(Cuboid::from_size(PLAYER_CUBOID_SIZE));
    commands.spawn((
        Mesh3d(mesh.clone()),
        MeshMaterial3d(materials.add(StandardMaterial {
            base_color: Color::srgb(0.8, 0.3, 0.3),
            ..default()
        })),
        Transform::from_translation(PLAYER_POSITION + Vec3::new(0.0, 0.0, -0.6)),
    ));
    commands.spawn((
        Mesh3d(mesh),
        MeshMaterial3d(materials.add(StandardMaterial {
            base_color: Color::srgba(0.8, 0.3, 0.3, 1.0),
            alpha_mode: AlphaMode::Blend,
            ..default()
        })),
        Transform::from_translation(PLAYER_POSITION + Vec3::new(0.0, 0.0, 0.6)),
    ));
    commands.spawn((
        PointLight {
            color: Color::srgb(1.0, 0.85, 0.6),
            intensity: 300_000.0,
            range: 40.0,
            shadow_maps_enabled: false,
            ..default()
        },
        Transform::from_translation(PLAYER_POSITION + Vec3::new(4.0, 2.0, 2.5)),
    ));
}

fn god_ray_density_image() -> Image {
    const N: usize = 32;
    let mut data = vec![0u8; N * N * N];
    for z in 0..N {
        for y in 0..N {
            for x in 0..N {
                let dx = x as f32 / (N - 1) as f32 * 2.0 - 1.0;
                let dz = z as f32 / (N - 1) as f32 * 2.0 - 1.0;
                let r = (dx * dx + dz * dz).sqrt();
                let t = ((1.0 - r) / 0.3).clamp(0.0, 1.0);
                let d = t * t * (3.0 - 2.0 * t);
                data[(z * N + y) * N + x] = (d * 255.0) as u8;
            }
        }
    }
    Image::new(
        Extent3d {
            width: N as u32,
            height: N as u32,
            depth_or_array_layers: N as u32,
        },
        TextureDimension::D3,
        data,
        TextureFormat::R8Unorm,
        RenderAssetUsages::RENDER_WORLD,
    )
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-RenderingDrawing game state to the screenC-BugAn unexpected or incorrect behaviorD-ComplexQuite challenging from either a design or technical perspective. Ask for help!S-Needs-DesignThis issue requires design work to think about how it would best be accomplished

    Type

    No type

    Projects

    Status
    Needs SME Triage

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions