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
This commit is contained in:
JMS55 2023-02-20 00:02:40 +00:00
parent 912fb58869
commit 03575aef22
8 changed files with 27 additions and 8 deletions

View File

@ -18,8 +18,9 @@ fn environment_map_light(
) -> EnvironmentMapLight { ) -> EnvironmentMapLight {
// Split-sum approximation for image based lighting: https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf // 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; // Technically we could use textureNumLevels(environment_map_specular) - 1 here, but we use a uniform
let radiance_level = perceptual_roughness * f32(smallest_specular_mip_level); // 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 irradiance = textureSample(environment_map_diffuse, environment_map_sampler, N).rgb;
let radiance = textureSampleLevel(environment_map_specular, environment_map_sampler, R, radiance_level).rgb; let radiance = textureSampleLevel(environment_map_specular, environment_map_sampler, R, radiance_level).rgb;

View File

@ -1,9 +1,9 @@
use crate::{ use crate::{
directional_light_order, point_light_order, AmbientLight, Cascade, CascadeShadowConfig, directional_light_order, point_light_order, AmbientLight, Cascade, CascadeShadowConfig,
Cascades, CascadesVisibleEntities, Clusters, CubemapVisibleEntities, DirectionalLight, Cascades, CascadesVisibleEntities, Clusters, CubemapVisibleEntities, DirectionalLight,
DirectionalLightShadowMap, DrawMesh, GlobalVisiblePointLights, MeshPipeline, NotShadowCaster, DirectionalLightShadowMap, DrawMesh, EnvironmentMapLight, GlobalVisiblePointLights,
PointLight, PointLightShadowMap, SetMeshBindGroup, SpotLight, VisiblePointLights, MeshPipeline, NotShadowCaster, PointLight, PointLightShadowMap, SetMeshBindGroup, SpotLight,
SHADOW_SHADER_HANDLE, VisiblePointLights, SHADOW_SHADER_HANDLE,
}; };
use bevy_asset::Handle; use bevy_asset::Handle;
use bevy_core_pipeline::core_3d::Transparent3d; use bevy_core_pipeline::core_3d::Transparent3d;
@ -218,6 +218,7 @@ pub struct GpuLights {
n_directional_lights: u32, n_directional_lights: u32,
// offset from spot light's light index to spot light's shadow map index // offset from spot light's light index to spot light's shadow map index
spot_light_shadowmap_offset: i32, 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 // 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( pub fn prepare_lights(
mut commands: Commands, mut commands: Commands,
mut texture_cache: ResMut<TextureCache>, mut texture_cache: ResMut<TextureCache>,
images: Res<RenderAssets<Image>>,
render_device: Res<RenderDevice>, render_device: Res<RenderDevice>,
render_queue: Res<RenderQueue>, render_queue: Res<RenderQueue>,
mut global_light_meta: ResMut<GlobalLightMeta>, mut global_light_meta: ResMut<GlobalLightMeta>,
mut light_meta: ResMut<LightMeta>, mut light_meta: ResMut<LightMeta>,
views: Query< views: Query<
(Entity, &ExtractedView, &ExtractedClusterConfig), (
Entity,
&ExtractedView,
&ExtractedClusterConfig,
Option<&EnvironmentMapLight>,
),
With<RenderPhase<Transparent3d>>, With<RenderPhase<Transparent3d>>,
>, >,
ambient_light: Res<AmbientLight>, ambient_light: Res<AmbientLight>,
@ -1029,7 +1036,7 @@ pub fn prepare_lights(
.write_buffer(&render_device, &render_queue); .write_buffer(&render_device, &render_queue);
// set up light data for each view // 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( let point_light_depth_texture = texture_cache.get(
&render_device, &render_device,
TextureDescriptor { 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. // 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 spot_light_shadowmap_offset: num_directional_cascades_enabled as i32
- point_light_count 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 // TODO: this should select lights based on relevance to the view instead of the first ones that show up in a query

View File

@ -546,6 +546,7 @@ impl FromWorld for MeshPipeline {
image.texture_descriptor.size.width as f32, image.texture_descriptor.size.width as f32,
image.texture_descriptor.size.height as f32, image.texture_descriptor.size.height as f32,
), ),
mip_level_count: image.texture_descriptor.mip_level_count,
} }
}; };

View File

@ -59,6 +59,7 @@ struct Lights {
cluster_factors: vec4<f32>, cluster_factors: vec4<f32>,
n_directional_lights: u32, n_directional_lights: u32,
spot_light_shadowmap_offset: i32, spot_light_shadowmap_offset: i32,
environment_map_smallest_specular_mip_level: u32,
}; };
struct Fog { struct Fog {

View File

@ -78,6 +78,7 @@ fn fallback_image_new(
image.texture_descriptor.size.width as f32, image.texture_descriptor.size.width as f32,
image.texture_descriptor.size.height as f32, image.texture_descriptor.size.height as f32,
), ),
mip_level_count: image.texture_descriptor.mip_level_count,
} }
} }

View File

@ -499,6 +499,7 @@ pub struct GpuImage {
pub texture_format: TextureFormat, pub texture_format: TextureFormat,
pub sampler: Sampler, pub sampler: Sampler,
pub size: Vec2, pub size: Vec2,
pub mip_level_count: u32,
} }
impl RenderAsset for Image { impl RenderAsset for Image {
@ -548,6 +549,7 @@ impl RenderAsset for Image {
texture_format: image.texture_descriptor.format, texture_format: image.texture_descriptor.format,
sampler, sampler,
size, size,
mip_level_count: image.texture_descriptor.mip_level_count,
}) })
} }
} }

View File

@ -253,6 +253,7 @@ impl FromWorld for Mesh2dPipeline {
image.texture_descriptor.size.width as f32, image.texture_descriptor.size.width as f32,
image.texture_descriptor.size.height as f32, image.texture_descriptor.size.height as f32,
), ),
mip_level_count: image.texture_descriptor.mip_level_count,
} }
}; };
Mesh2dPipeline { Mesh2dPipeline {

View File

@ -134,6 +134,7 @@ impl FromWorld for SpritePipeline {
image.texture_descriptor.size.width as f32, image.texture_descriptor.size.width as f32,
image.texture_descriptor.size.height as f32, image.texture_descriptor.size.height as f32,
), ),
mip_level_count: image.texture_descriptor.mip_level_count,
} }
}; };