-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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
Conversation
More robust test scene includes camera and sun controller(with right click) and switching between earth, mars, raymarched vs default rendering: |
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 |
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? |
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. |
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. |
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. |
# Objective This PR implements a robust GPU-based pipeline for dynamically generating environment maps in Bevy. It builds upon PR #19037, allowing these changes to be evaluated independently from the atmosphere implementation. While existing offline tools can process environment maps, generate mip levels, and calculate specular lighting with importance sampling, they're limited to static file-based workflows. This PR introduces a real-time GPU pipeline that dynamically generates complete environment maps from a single cubemap texture on each frame. Closes #9380 ## Solution Implemented a Single Pass Downsampling (SPD) pipeline that processes textures without pre-existing mip levels or pre-filtered lighting data. Single Pass Downsampling (SPD) pipeline: - accepts any square, power-of-two cubemap up to 8192 × 8192 per face and generates the complete mip chain in one frame; - copies the base mip (level 0) in a dedicated compute dispatch (`copy_mip0`) before the down-sampling pass; - performs the down-sampling itself in two compute dispatches to fit within subgroup limits; - heavily inspired by Jasmine's prototype code. Pre-filtering pipeline: - generates the specular Radiance Map using bounded-VNDF GGX importance sampling for higher quality highlights and fewer fireflies; - computes the diffuse Irradiance Map with cosine-weighted hemisphere sampling; - mirrors the forward-/reverse-tonemap workflow used by TAA instead of exposing a separate *white-point* parameter; - is based on the resources below together with the “Bounded VNDF Sampling for Smith-GGX Reflections” paper. The pre-filtering pipeline is largely based on these articles: - https://placeholderart.wordpress.com/2015/07/28/implementation-notes-runtime-environment-map-filtering-for-image-based-lighting/ - https://bruop.github.io/ibl/ - https://gpuopen.com/download/Bounded_VNDF_Sampling_for_Smith-GGX_Reflections.pdf > The forward-/reverse-tonemap trick removes almost all fireflies without the need for a separate white-point parameter. Previous work: #9414 ## Testing The `reflection_probes.rs` example has been updated: - The camera starts closer to the spheres so the reflections are easier to see. - The GLTF scene is spawned only when the reflection probe mode is active (press Space). - The third display mode (toggled with Space) shows the generated cubemap chain. - You can change the roughness of the center sphere with the Up/Down keys. ## Render Graph Composed of two nodes and a graph edge: ``` Downsampling -> Filtering ``` Pass breakdown: ``` dowsampling_first_pass -> dowsampling_second_pass -> radiance_map_pass -> irradiance_map_pass ``` <img width="1601" height="2281" alt="render-graph" src="https://github.com/user-attachments/assets/3c240688-32f7-447a-9ede-6050b77c0bd1" /> --- ## Showcase <img width="2564" height="1500" alt="image" src="https://github.com/user-attachments/assets/56e68dd7-9488-4d35-9bba-7f713a3e2831" /> User facing API: ```rust commands.entity(camera) .insert(GeneratedEnvironmentMapLight { environment_map: world.load_asset("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"), ..default() }); ``` ## Computed Environment Maps To use fully dynamic environment maps, create a new placeholder image handle with `Image::new_fill`, extract it to the render world. Then dispatch a compute shader, bind the image as a 2d array storage texture. Anything can be rendered to the custom dynamic environment map. This is already demonstrated in PR #19037 with the `atmosphere.rs` example. We can extend this idea further and run the entire PBR pipeline from the perspective of the light probe, and it is possible to have some form of global illumination or baked lighting information this way, especially if we make use of irradiance volumes for the realtime aspect. This method could very well be extended to bake indirect lighting in the scene. #13840 should make this possible! ## Notes for reviewers This PR no longer bundles any large test textures. --------- Co-authored-by: atlas <email@atlasdostal.com>
Work has been continued on a different set of Git branches. |
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:
More specific implementation details:
rendering_method
parameter to theAtmosphereSettings
which includes more precise volumetric shadows and enables space viewsorigin
parameter to theAtmosphere
which controls the observers position, enabling space views for using the raymarched rendering methodSunLight
component that controls the size of the sun disk rendered in the atmosphere and defaulted it to the mean value taken from scientific dataShared 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:
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:
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.
pbr_lighting.wgsl
file'sdirectional_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:
LightProbe
andAtmosphereEnvironmentMapLight
AtmosphereEnvironmentMapLight
is extracted withExtractComponentPlugin
prepare_atmosphere_probe_components
addsFilteredEnvironmentMapLight
andAtmosphereEnvironmentMap
create_environment_map_from_prefilter
addsEnvironmentMapLight
based on theFilteredEnvironmentMapLight
Single Pass downsampling (SPD) pipeline:
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:

Testing
Showcase
Usage:
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:
Next Steps
ground
,jitter
, andshadow
parameters per-render pass and screenshots that show the results