From 03575aef2259e8704550c2ad4c10551b726d67c0 Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Mon, 20 Feb 2023 00:02:40 +0000 Subject: [PATCH] EnvironmentMapLight support for WebGL2 (#7737) # Objective - Fix the environment map shader not working under webgl due to textureNumLevels() not being supported - Fixes https://github.com/bevyengine/bevy/issues/7722 ## Solution - Instead of using textureNumLevels(), put an extra field in the GpuLights uniform to store the mip count --- .../src/environment_map/environment_map.wgsl | 5 +++-- crates/bevy_pbr/src/render/light.rs | 21 ++++++++++++++----- crates/bevy_pbr/src/render/mesh.rs | 1 + .../bevy_pbr/src/render/mesh_view_types.wgsl | 3 ++- .../bevy_render/src/texture/fallback_image.rs | 1 + crates/bevy_render/src/texture/image.rs | 2 ++ crates/bevy_sprite/src/mesh2d/mesh.rs | 1 + crates/bevy_sprite/src/render/mod.rs | 1 + 8 files changed, 27 insertions(+), 8 deletions(-) diff --git a/crates/bevy_pbr/src/environment_map/environment_map.wgsl b/crates/bevy_pbr/src/environment_map/environment_map.wgsl index 7e2bc8b3882cf..1c225645a812c 100644 --- a/crates/bevy_pbr/src/environment_map/environment_map.wgsl +++ b/crates/bevy_pbr/src/environment_map/environment_map.wgsl @@ -18,8 +18,9 @@ fn environment_map_light( ) -> EnvironmentMapLight { // Split-sum approximation for image based lighting: https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf - let smallest_specular_mip_level = textureNumLevels(environment_map_specular) - 1i; - let radiance_level = perceptual_roughness * f32(smallest_specular_mip_level); + // Technically we could use textureNumLevels(environment_map_specular) - 1 here, but we use a uniform + // because textureNumLevels() does not work on WebGL2 + let radiance_level = perceptual_roughness * f32(lights.environment_map_smallest_specular_mip_level); let irradiance = textureSample(environment_map_diffuse, environment_map_sampler, N).rgb; let radiance = textureSampleLevel(environment_map_specular, environment_map_sampler, R, radiance_level).rgb; diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index ca77f917c849d..5131065614423 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -1,9 +1,9 @@ use crate::{ directional_light_order, point_light_order, AmbientLight, Cascade, CascadeShadowConfig, Cascades, CascadesVisibleEntities, Clusters, CubemapVisibleEntities, DirectionalLight, - DirectionalLightShadowMap, DrawMesh, GlobalVisiblePointLights, MeshPipeline, NotShadowCaster, - PointLight, PointLightShadowMap, SetMeshBindGroup, SpotLight, VisiblePointLights, - SHADOW_SHADER_HANDLE, + DirectionalLightShadowMap, DrawMesh, EnvironmentMapLight, GlobalVisiblePointLights, + MeshPipeline, NotShadowCaster, PointLight, PointLightShadowMap, SetMeshBindGroup, SpotLight, + VisiblePointLights, SHADOW_SHADER_HANDLE, }; use bevy_asset::Handle; use bevy_core_pipeline::core_3d::Transparent3d; @@ -218,6 +218,7 @@ pub struct GpuLights { n_directional_lights: u32, // offset from spot light's light index to spot light's shadow map index spot_light_shadowmap_offset: i32, + environment_map_smallest_specular_mip_level: u32, } // NOTE: this must be kept in sync with the same constants in pbr.frag @@ -787,12 +788,18 @@ pub(crate) fn spot_light_projection_matrix(angle: f32) -> Mat4 { pub fn prepare_lights( mut commands: Commands, mut texture_cache: ResMut, + images: Res>, render_device: Res, render_queue: Res, mut global_light_meta: ResMut, mut light_meta: ResMut, views: Query< - (Entity, &ExtractedView, &ExtractedClusterConfig), + ( + Entity, + &ExtractedView, + &ExtractedClusterConfig, + Option<&EnvironmentMapLight>, + ), With>, >, ambient_light: Res, @@ -1029,7 +1036,7 @@ pub fn prepare_lights( .write_buffer(&render_device, &render_queue); // set up light data for each view - for (entity, extracted_view, clusters) in &views { + for (entity, extracted_view, clusters, environment_map) in &views { let point_light_depth_texture = texture_cache.get( &render_device, TextureDescriptor { @@ -1096,6 +1103,10 @@ pub fn prepare_lights( // index to shadow map index, we need to subtract point light count and add directional shadowmap count. spot_light_shadowmap_offset: num_directional_cascades_enabled as i32 - point_light_count as i32, + environment_map_smallest_specular_mip_level: environment_map + .and_then(|env_map| images.get(&env_map.specular_map)) + .map(|specular_map| specular_map.mip_level_count - 1) + .unwrap_or(0), }; // TODO: this should select lights based on relevance to the view instead of the first ones that show up in a query diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index 1adb5db10ca58..bf8ba690b83dc 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -546,6 +546,7 @@ impl FromWorld for MeshPipeline { image.texture_descriptor.size.width as f32, image.texture_descriptor.size.height as f32, ), + mip_level_count: image.texture_descriptor.mip_level_count, } }; diff --git a/crates/bevy_pbr/src/render/mesh_view_types.wgsl b/crates/bevy_pbr/src/render/mesh_view_types.wgsl index 57ac0cfa27d92..b944aa2792e2f 100644 --- a/crates/bevy_pbr/src/render/mesh_view_types.wgsl +++ b/crates/bevy_pbr/src/render/mesh_view_types.wgsl @@ -24,7 +24,7 @@ struct DirectionalCascade { texel_size: f32, far_bound: f32, } - + struct DirectionalLight { cascades: array, color: vec4, @@ -59,6 +59,7 @@ struct Lights { cluster_factors: vec4, n_directional_lights: u32, spot_light_shadowmap_offset: i32, + environment_map_smallest_specular_mip_level: u32, }; struct Fog { diff --git a/crates/bevy_render/src/texture/fallback_image.rs b/crates/bevy_render/src/texture/fallback_image.rs index d152c07ea4f16..9160dadbac2e6 100644 --- a/crates/bevy_render/src/texture/fallback_image.rs +++ b/crates/bevy_render/src/texture/fallback_image.rs @@ -78,6 +78,7 @@ fn fallback_image_new( image.texture_descriptor.size.width as f32, image.texture_descriptor.size.height as f32, ), + mip_level_count: image.texture_descriptor.mip_level_count, } } diff --git a/crates/bevy_render/src/texture/image.rs b/crates/bevy_render/src/texture/image.rs index 07a2ad8e493b8..1b6b16427d8bb 100644 --- a/crates/bevy_render/src/texture/image.rs +++ b/crates/bevy_render/src/texture/image.rs @@ -499,6 +499,7 @@ pub struct GpuImage { pub texture_format: TextureFormat, pub sampler: Sampler, pub size: Vec2, + pub mip_level_count: u32, } impl RenderAsset for Image { @@ -548,6 +549,7 @@ impl RenderAsset for Image { texture_format: image.texture_descriptor.format, sampler, size, + mip_level_count: image.texture_descriptor.mip_level_count, }) } } diff --git a/crates/bevy_sprite/src/mesh2d/mesh.rs b/crates/bevy_sprite/src/mesh2d/mesh.rs index 5405195db0965..6b931a6e7513d 100644 --- a/crates/bevy_sprite/src/mesh2d/mesh.rs +++ b/crates/bevy_sprite/src/mesh2d/mesh.rs @@ -253,6 +253,7 @@ impl FromWorld for Mesh2dPipeline { image.texture_descriptor.size.width as f32, image.texture_descriptor.size.height as f32, ), + mip_level_count: image.texture_descriptor.mip_level_count, } }; Mesh2dPipeline { diff --git a/crates/bevy_sprite/src/render/mod.rs b/crates/bevy_sprite/src/render/mod.rs index d6ee43be48658..3d5ac0377449b 100644 --- a/crates/bevy_sprite/src/render/mod.rs +++ b/crates/bevy_sprite/src/render/mod.rs @@ -134,6 +134,7 @@ impl FromWorld for SpritePipeline { image.texture_descriptor.size.width as f32, image.texture_descriptor.size.height as f32, ), + mip_level_count: image.texture_descriptor.mip_level_count, } };