Skip to content

Procedural Atmosphere PBR integration #19037

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 39 commits into
base: main
Choose a base branch
from

Conversation

mate-h
Copy link
Contributor

@mate-h mate-h commented May 2, 2025

Objective

Currently Bevy's atmosphere solution does not extend to include the PBR pipeline, it is merely a composited effect on top of the scene using blend operations. The goal of this pull request is to provide realistic, physically-based lighting to any geometry in an outdoor scene, covering a vast number of use cases for game developers.

Solution

We achieve this using a combination of the following features:

  • Environment map light reflections include the atmosphere at all roughness levels
  • Volumetric shadowing through the atmosphere for opaque geometry
  • Space views using a custom origin point and raymarched rendering
  • Small bonus: added parameters for Martian atmosphere preset using scientific data

More specific implementation details:

  • Copy atmospheric functions to PBR pipeline and compute new directional light color.
  • Added new specialization key for when the atmosphere is present, and a new shader def in the main PBR pipeline
  • Added blue noise texture, added parser 16bit texture parser function into a missing implementation
  • Created a spherical coordinate system where the observer's position is relative to the planet surface instead of relying on just the altitude parameter "r". this needs to be followed up by supporting the transform component on the atmosphere.
  • Added a rendering_method parameter to the AtmosphereSettings which includes more precise volumetric shadows and enables space views
  • Added an origin parameter to the Atmosphere which controls the observers position, enabling space views for using the raymarched rendering method
  • Added a new SunLight component that controls the size of the sun disk rendered in the atmosphere and defaulted it to the mean value taken from scientific data

Shared ray-marching function

Shared ray-marching function in the atmosphere shader, except for the aerial-view LUT. For the Aerial-view LUT we have two nested for loops, and we march the ray further and further for each depth slice so it's slightly different.

The shared ray-marching function has the following signature:

struct RaymarchResult {
    inscattering: vec3<f32>,
    transmittance: vec3<f32>,
}

fn raymarch_atmosphere(
    pos: vec3<f32>,
    ray_dir: vec3<f32>,
    t_max: f32,
    sample_count: f32,
    uv: vec2<f32>,
    jitter: bool,
    ground: bool
) -> RaymarchResult

Since WGSL has no function overloading (see open issue), the caller has to supply all arguments. I also wanted to specifically avoid using a struct as an input to make the resulting called code more concise, at the sacrifice of readability. Because the function signature can easily be looked up, I didn't think this was an issue.
Caller code example:

#import bevy_pbr::atmosphere::raymarch_atmosphere;

let result = raymarch_atmosphere(world_pos, ray_dir, t_max, sample_count, uv, jitter, ground, shadow);

Proposal: additional parameter to control whether to apply the shadowing term for computing volumetric shadows.

PBR Directional light

PBR directional lights are occluded by the transmittance through the atmosphere, getting tinted orange, then red when the sun is low over the horizon.

  • First, we specify a pipeline key for the atmosphere
  • Bind the atmosphere buffer and the transmittance texture to the PBR pipeline.
  • Implemented the pbr_lighting.wgsl file's directional_light function to extend it to the atmospheric transmittance.

Generated Environment Map Light

In order to generate an environment map, we place a light probe into the world and attach the AtmosphereEnvironmentMapLight component to it. Given it's transform in the world, we create an environment map at that location, by running the atmosphere shader ray-marching for each cube face. Note how we need to create a separate view (2d storage texture array) and re-interpret it at runtime, using the create view function.

ECS flow:

  • The entity starts with LightProbe and AtmosphereEnvironmentMapLight
  • AtmosphereEnvironmentMapLight is extracted with ExtractComponentPlugin
  • prepare_atmosphere_probe_components adds FilteredEnvironmentMapLight and AtmosphereEnvironmentMap
  • create_environment_map_from_prefilter adds EnvironmentMapLight based on the FilteredEnvironmentMapLight

Single Pass downsampling (SPD) pipeline:

  • takes the 512x512 Cubemap as an input, and creates 9 MIP levels.
  • each level is progressively down-sampled.
  • It is actually broken into two passes due to architectural limitations.
  • largely based on Jasmine's code from github

Pre-filtering pipeline: composed of multiple Radiance Map (specular mips) generation passes, followed by the irradiance map pass (diffuse).
The pre-filtering pipeline is largely based on these articles:

Render Graph:
render-graph

Testing

  • Test scene has been updated to include a range of light probes with different roughness levels and metallic values
  • Added Minimal camera, sun position and exposure controller
  • Switch between Earth and Mars atmosphere using 1 and 2 keys
  • Added help text similar to other examples

Showcase

showcase-image

Usage:

// Spawn a new light probe to generate an environment map for the atmosphere
commands.spawn((
    LightProbe,
    AtmosphereEnvironmentMapLight::default(),
    // The translation controls where the light probe is placed,
    // and the scale controls the extents of where it will affect objects in the scene.
    Transform::from_xyz(0.0, 0.0, 0.0).with_scale(Vec3::splat(100.0)),
));

Notes for reviewers

In it's current state, this PR includes a lot of code that may not need to be merged into the main branch. Therefore I am looking for feedback which changes to eliminate before proceeding any more work on it.

Given that all this work is done, but how large this pull request is may make it difficult to review all at once. I'm also looking for feedback whether it is better/more efficient to split it into smaller ones, for example:

  1. PBR directional light
  2. SPD + Environment lighting
  3. Volumetric shadowing and mars

Next Steps

  • create a table comparing the ground, jitter, and shadow parameters per-render pass and screenshots that show the results
  • fix deferred rendering with the atmosphere
  • dispatch separate pass for rendering a sky-view lut for the environment and profile the improvement in gpu time
  • create a graphic that shows the new ECS components and their relationships
  • support the transform component in the atmosphere to set the scene origin
  • use shared raymarch function in aerial view lut as well.
  • support larger environment maps above 512x512 resolution per cubeface (more mips)

@mate-h
Copy link
Contributor Author

mate-h commented May 2, 2025

More robust test scene includes camera and sun controller(with right click) and switching between earth, mars, raymarched vs default rendering:
atmosphere.rs.txt

@jnhyatt
Copy link
Contributor

jnhyatt commented May 3, 2025

This seems to be several changes under one umbrella. Do you think it's at all feasible to split this into multiple PRs? It'd be much easier to review piecewise but I understand it might well be a lot of work. No worries if that's the case

@mate-h
Copy link
Contributor Author

mate-h commented May 3, 2025

It's definitely feasible the only reason I didn't, is because I am not certain that will ultimately be less work to review. But i could start by splitting out the PBR directional light part since it's fairly isolated. But the new ray-marching function is used throughout, so that part might be more challenging. How many smaller pieces would make sense?

@jnhyatt
Copy link
Contributor

jnhyatt commented May 3, 2025

Eh, I guess not all that important after my first pass. It's honestly pretty well-structured and somewhat interrelated. I'll trust you're right about what's less review work. If you want to break it up, I think the light probe stuff looks pretty well-isolated, and the directional light stuff like you said. But again, I'm no longer convinced it'd be better like that.

@JMS55 JMS55 self-requested a review May 3, 2025 01:23
@mate-h
Copy link
Contributor Author

mate-h commented May 3, 2025

After some discussion I decided it's best to de-couple the SPD and pre-filtering pipeline changes in a separate PR, but keeping everything atmosphere-related here, and in the other PR I will still reference this one for the description and use-case.

@alice-i-cecile alice-i-cecile added C-Feature A new feature, making something new possible A-Rendering Drawing game state to the screen D-Complex Quite challenging from either a design or technical perspective. Ask for help! X-Contentious There are nontrivial implications that should be thought through S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels May 5, 2025
@alice-i-cecile alice-i-cecile added the M-Needs-Release-Note Work that should be called out in the blog due to impact label May 5, 2025
@alice-i-cecile alice-i-cecile added this to the 0.17 milestone May 5, 2025
Copy link
Contributor

github-actions bot commented May 5, 2025

It looks like your PR has been selected for a highlight in the next release blog post, but you didn't provide a release note.

Please review the instructions for writing release notes, then expand or revise the content in the release notes directory to showcase your changes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-Rendering Drawing game state to the screen C-Feature A new feature, making something new possible D-Complex Quite challenging from either a design or technical perspective. Ask for help! M-Needs-Release-Note Work that should be called out in the blog due to impact S-Needs-Review Needs reviewer attention (from anyone!) to move forward X-Contentious There are nontrivial implications that should be thought through
Projects
Status: No status
Development

Successfully merging this pull request may close these issues.

3 participants