Skip to content

Commit a616ffa

Browse files
mate-hJMS55
andauthored
Web support for atmosphere (#18582)
# Objective Add web support to atmosphere by gating dual source blending and using a macro to determine the target platform. The main objective of this PR is to ensure that users of Bevy's atmosphere feature can also run it in a web-based context where WebGPU support is enabled. ## Solution - Make use of the `#[cfg(not(target_arch = "wasm32"))]` macro to gate the dual source blending, as this is not (yet) supported in web browsers. - Rename the function `sample_sun_illuminance` to `sample_sun_radiance` and move calls out of conditionals to ensure the shader compiles and runs in both native and web-based contexts. - Moved the multiplication of the transmittance out when calculating the sun color, because calling the `sample_sun_illuminance` function was causing issues in web. Overall this results in cleaner code and more readable. ## Testing - Tested by building a wasm target and loading it in a web page with Vite dev server using `mate-h/bevy-webgpu` repo template. - Tested the native build with `cargo run --example atmosphere` to ensure it still works with dual source blending. --- ## Showcase Screenshots show the atmosphere example running in two different contexts: <img width="1281" alt="atmosphere-web-showcase" src="https://github.com/user-attachments/assets/40b1ee91-89ae-41a6-8189-89630d1ca1a6" /> --------- Co-authored-by: JMS55 <[email protected]>
1 parent a1fd3a4 commit a616ffa

File tree

4 files changed

+36
-18
lines changed

4 files changed

+36
-18
lines changed

crates/bevy_pbr/src/atmosphere/functions.wgsl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -277,21 +277,21 @@ fn sample_local_inscattering(local_atmosphere: AtmosphereSample, ray_dir: vec3<f
277277

278278
const SUN_ANGULAR_SIZE: f32 = 0.0174533; // angular diameter of sun in radians
279279

280-
fn sample_sun_illuminance(ray_dir_ws: vec3<f32>, transmittance: vec3<f32>) -> vec3<f32> {
280+
fn sample_sun_radiance(ray_dir_ws: vec3<f32>) -> vec3<f32> {
281281
let r = view_radius();
282282
let mu_view = ray_dir_ws.y;
283283
let shadow_factor = f32(!ray_intersects_ground(r, mu_view));
284-
var sun_illuminance = vec3(0.0);
284+
var sun_radiance = vec3(0.0);
285285
for (var light_i: u32 = 0u; light_i < lights.n_directional_lights; light_i++) {
286286
let light = &lights.directional_lights[light_i];
287287
let neg_LdotV = dot((*light).direction_to_light, ray_dir_ws);
288288
let angle_to_sun = fast_acos(neg_LdotV);
289289
let pixel_size = fwidth(angle_to_sun);
290290
let factor = smoothstep(0.0, -pixel_size * ROOT_2, angle_to_sun - SUN_ANGULAR_SIZE * 0.5);
291291
let sun_solid_angle = (SUN_ANGULAR_SIZE * SUN_ANGULAR_SIZE) * 4.0 * FRAC_PI;
292-
sun_illuminance += ((*light).color.rgb / sun_solid_angle) * factor * shadow_factor;
292+
sun_radiance += ((*light).color.rgb / sun_solid_angle) * factor * shadow_factor;
293293
}
294-
return sun_illuminance * transmittance * view.exposure;
294+
return sun_radiance;
295295
}
296296

297297
// TRANSFORM UTILITIES

crates/bevy_pbr/src/atmosphere/mod.rs

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@
2525
//! at once is untested, and might not be physically accurate. These may be
2626
//! integrated into a single module in the future.
2727
//!
28+
//! On web platforms, atmosphere rendering will look slightly different. Specifically, when calculating how light travels
29+
//! through the atmosphere, we use a simpler averaging technique instead of the more
30+
//! complex blending operations. This difference will be resolved for WebGPU in a future release.
31+
//!
2832
//! [Shadertoy]: https://www.shadertoy.com/view/slSXRW
2933
//!
3034
//! [Unreal Engine Implementation]: https://github.com/sebh/UnrealEngineSkyAtmosphere
@@ -46,8 +50,6 @@ use bevy_reflect::{std_traits::ReflectDefault, Reflect};
4650
use bevy_render::{
4751
extract_component::UniformComponentPlugin,
4852
render_resource::{DownlevelFlags, ShaderType, SpecializedRenderPipelines},
49-
renderer::RenderDevice,
50-
settings::WgpuFeatures,
5153
};
5254
use bevy_render::{
5355
extract_component::{ExtractComponent, ExtractComponentPlugin},
@@ -159,15 +161,6 @@ impl Plugin for AtmospherePlugin {
159161
};
160162

161163
let render_adapter = render_app.world().resource::<RenderAdapter>();
162-
let render_device = render_app.world().resource::<RenderDevice>();
163-
164-
if !render_device
165-
.features()
166-
.contains(WgpuFeatures::DUAL_SOURCE_BLENDING)
167-
{
168-
warn!("AtmospherePlugin not loaded. GPU lacks support for dual-source blending.");
169-
return;
170-
}
171164

172165
if !render_adapter
173166
.get_downlevel_capabilities()

crates/bevy_pbr/src/atmosphere/render_sky.wgsl

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
sample_transmittance_lut, sample_transmittance_lut_segment,
66
sample_sky_view_lut, direction_world_to_atmosphere,
77
uv_to_ray_direction, uv_to_ndc, sample_aerial_view_lut,
8-
view_radius, sample_sun_illuminance, ndc_to_camera_dist
8+
view_radius, sample_sun_radiance, ndc_to_camera_dist
99
},
1010
};
1111
#import bevy_render::view::View;
@@ -20,7 +20,9 @@
2020

2121
struct RenderSkyOutput {
2222
@location(0) inscattering: vec4<f32>,
23+
#ifdef DUAL_SOURCE_BLENDING
2324
@location(0) @second_blend_source transmittance: vec4<f32>,
25+
#endif
2426
}
2527

2628
@fragment
@@ -33,15 +35,24 @@ fn main(in: FullscreenVertexOutput) -> RenderSkyOutput {
3335

3436
var transmittance: vec3<f32>;
3537
var inscattering: vec3<f32>;
38+
39+
let sun_radiance = sample_sun_radiance(ray_dir_ws.xyz);
40+
3641
if depth == 0.0 {
3742
let ray_dir_as = direction_world_to_atmosphere(ray_dir_ws.xyz);
3843
transmittance = sample_transmittance_lut(r, mu);
3944
inscattering += sample_sky_view_lut(r, ray_dir_as);
40-
inscattering += sample_sun_illuminance(ray_dir_ws.xyz, transmittance);
45+
inscattering += sun_radiance * transmittance * view.exposure;
4146
} else {
4247
let t = ndc_to_camera_dist(vec3(uv_to_ndc(in.uv), depth));
4348
inscattering = sample_aerial_view_lut(in.uv, t);
4449
transmittance = sample_transmittance_lut_segment(r, mu, t);
4550
}
51+
#ifdef DUAL_SOURCE_BLENDING
4652
return RenderSkyOutput(vec4(inscattering, 0.0), vec4(transmittance, 1.0));
53+
#else
54+
let mean_transmittance = (transmittance.r + transmittance.g + transmittance.b) / 3.0;
55+
return RenderSkyOutput(vec4(inscattering, mean_transmittance));
56+
#endif
57+
4758
}

crates/bevy_pbr/src/atmosphere/resources.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,7 @@ pub(crate) struct RenderSkyPipelineId(pub CachedRenderPipelineId);
326326
pub(crate) struct RenderSkyPipelineKey {
327327
pub msaa_samples: u32,
328328
pub hdr: bool,
329+
pub dual_source_blending: bool,
329330
}
330331

331332
impl SpecializedRenderPipeline for RenderSkyBindGroupLayouts {
@@ -340,6 +341,15 @@ impl SpecializedRenderPipeline for RenderSkyBindGroupLayouts {
340341
if key.hdr {
341342
shader_defs.push("TONEMAP_IN_SHADER".into());
342343
}
344+
if key.dual_source_blending {
345+
shader_defs.push("DUAL_SOURCE_BLENDING".into());
346+
}
347+
348+
let dst_factor = if key.dual_source_blending {
349+
BlendFactor::Src1
350+
} else {
351+
BlendFactor::SrcAlpha
352+
};
343353

344354
RenderPipelineDescriptor {
345355
label: Some(format!("render_sky_pipeline_{}", key.msaa_samples).into()),
@@ -367,7 +377,7 @@ impl SpecializedRenderPipeline for RenderSkyBindGroupLayouts {
367377
blend: Some(BlendState {
368378
color: BlendComponent {
369379
src_factor: BlendFactor::One,
370-
dst_factor: BlendFactor::Src1,
380+
dst_factor,
371381
operation: BlendOperation::Add,
372382
},
373383
alpha: BlendComponent {
@@ -388,6 +398,7 @@ pub(super) fn queue_render_sky_pipelines(
388398
pipeline_cache: Res<PipelineCache>,
389399
layouts: Res<RenderSkyBindGroupLayouts>,
390400
mut specializer: ResMut<SpecializedRenderPipelines<RenderSkyBindGroupLayouts>>,
401+
render_device: Res<RenderDevice>,
391402
mut commands: Commands,
392403
) {
393404
for (entity, camera, msaa) in &views {
@@ -397,6 +408,9 @@ pub(super) fn queue_render_sky_pipelines(
397408
RenderSkyPipelineKey {
398409
msaa_samples: msaa.samples(),
399410
hdr: camera.hdr,
411+
dual_source_blending: render_device
412+
.features()
413+
.contains(WgpuFeatures::DUAL_SOURCE_BLENDING),
400414
},
401415
);
402416
commands.entity(entity).insert(RenderSkyPipelineId(id));

0 commit comments

Comments
 (0)