Add support for skybox transformation (#14267)

# Objective

- Fixes https://github.com/bevyengine/bevy/issues/14036

## Solution

- Add a view space transformation for the skybox

## Testing

- I have tested the newly added `transform` field using the `skybox`
example.
```
diff --git a/examples/3d/skybox.rs b/examples/3d/skybox.rs
index beaf5b268..d16cbe988 100644
--- a/examples/3d/skybox.rs
+++ b/examples/3d/skybox.rs
@@ -81,6 +81,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
         Skybox {
             image: skybox_handle.clone(),
             brightness: 1000.0,
+            rotation: Quat::from_rotation_x(PI * -0.5),
         },
     ));
```
<img width="1280" alt="image"
src="https://github.com/bevyengine/bevy/assets/6300263/1230a608-58ea-492d-a811-90c54c3b43ef">


## Migration Guide
- Since we have added a new filed to the Skybox struct, users will need
to include `..Default::default()` or some rotation value in their
initialization code.
This commit is contained in:
Sou1gh0st 2024-07-15 23:53:20 +08:00 committed by GitHub
parent a79df7b124
commit 65aae92127
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 36 additions and 1 deletions

View file

@ -6,6 +6,7 @@ use bevy_ecs::{
schedule::IntoSystemConfigs, schedule::IntoSystemConfigs,
system::{Commands, Query, Res, ResMut, Resource}, system::{Commands, Query, Res, ResMut, Resource},
}; };
use bevy_math::{Mat4, Quat};
use bevy_render::{ use bevy_render::{
camera::Exposure, camera::Exposure,
extract_component::{ extract_component::{
@ -22,6 +23,7 @@ use bevy_render::{
view::{ExtractedView, Msaa, ViewTarget, ViewUniform, ViewUniforms}, view::{ExtractedView, Msaa, ViewTarget, ViewUniform, ViewUniforms},
Render, RenderApp, RenderSet, Render, RenderApp, RenderSet,
}; };
use bevy_transform::components::Transform;
use prepass::{SkyboxPrepassPipeline, SKYBOX_PREPASS_SHADER_HANDLE}; use prepass::{SkyboxPrepassPipeline, SKYBOX_PREPASS_SHADER_HANDLE};
use crate::{core_3d::CORE_3D_DEPTH_FORMAT, prepass::PreviousViewUniforms}; use crate::{core_3d::CORE_3D_DEPTH_FORMAT, prepass::PreviousViewUniforms};
@ -90,6 +92,21 @@ pub struct Skybox {
/// After applying this multiplier to the image samples, the resulting values should /// After applying this multiplier to the image samples, the resulting values should
/// be in units of [cd/m^2](https://en.wikipedia.org/wiki/Candela_per_square_metre). /// be in units of [cd/m^2](https://en.wikipedia.org/wiki/Candela_per_square_metre).
pub brightness: f32, pub brightness: f32,
/// View space rotation applied to the skybox cubemap.
/// This is useful for users who require a different axis, such as the Z-axis, to serve
/// as the vertical axis.
pub rotation: Quat,
}
impl Default for Skybox {
fn default() -> Self {
Skybox {
image: Handle::default(),
brightness: 0.0,
rotation: Quat::IDENTITY,
}
}
} }
impl ExtractComponent for Skybox { impl ExtractComponent for Skybox {
@ -106,6 +123,9 @@ impl ExtractComponent for Skybox {
skybox.clone(), skybox.clone(),
SkyboxUniforms { SkyboxUniforms {
brightness: skybox.brightness * exposure, brightness: skybox.brightness * exposure,
transform: Transform::from_rotation(skybox.rotation)
.compute_matrix()
.inverse(),
#[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))] #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
_wasm_padding_8b: 0, _wasm_padding_8b: 0,
#[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))] #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
@ -121,6 +141,7 @@ impl ExtractComponent for Skybox {
#[derive(Component, ShaderType, Clone)] #[derive(Component, ShaderType, Clone)]
pub struct SkyboxUniforms { pub struct SkyboxUniforms {
brightness: f32, brightness: f32,
transform: Mat4,
#[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))] #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
_wasm_padding_8b: u32, _wasm_padding_8b: u32,
#[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))] #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]

View file

@ -3,6 +3,7 @@
struct SkyboxUniforms { struct SkyboxUniforms {
brightness: f32, brightness: f32,
transform: mat4x4<f32>,
#ifdef SIXTEEN_BYTE_ALIGNMENT #ifdef SIXTEEN_BYTE_ALIGNMENT
_wasm_padding_8b: u32, _wasm_padding_8b: u32,
_wasm_padding_12b: u32, _wasm_padding_12b: u32,
@ -29,7 +30,12 @@ fn coords_to_ray_direction(position: vec2<f32>, viewport: vec4<f32>) -> vec3<f32
1.0, 1.0,
1.0, 1.0,
); );
let view_ray_direction = view_position_homogeneous.xyz / view_position_homogeneous.w;
// Transforming the view space ray direction by the skybox transform matrix, it is
// equivalent to rotating the skybox itself.
var view_ray_direction = view_position_homogeneous.xyz / view_position_homogeneous.w;
view_ray_direction = (uniforms.transform * vec4(view_ray_direction, 1.0)).xyz;
// Transforming the view space ray direction by the view matrix, transforms the // Transforming the view space ray direction by the view matrix, transforms the
// direction to world space. Note that the w element is set to 0.0, as this is a // direction to world space. Note that the w element is set to 0.0, as this is a
// vector direction, not a position, That causes the matrix multiplication to ignore // vector direction, not a position, That causes the matrix multiplication to ignore

View file

@ -240,6 +240,7 @@ fn add_skybox_and_environment_map(
.insert(Skybox { .insert(Skybox {
brightness: 5000.0, brightness: 5000.0,
image: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"), image: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"),
..Default::default()
}) })
.insert(EnvironmentMapLight { .insert(EnvironmentMapLight {
diffuse_map: asset_server.load("environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2"), diffuse_map: asset_server.load("environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2"),

View file

@ -54,6 +54,7 @@ fn setup(
Skybox { Skybox {
image: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"), image: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"),
brightness: bevy::pbr::light_consts::lux::DIRECT_SUNLIGHT, brightness: bevy::pbr::light_consts::lux::DIRECT_SUNLIGHT,
..Default::default()
}, },
)); ));

View file

@ -224,6 +224,7 @@ fn spawn_camera(commands: &mut Commands, asset_server: &AssetServer) {
.insert(Skybox { .insert(Skybox {
brightness: 5000.0, brightness: 5000.0,
image: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"), image: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"),
..Default::default()
}) })
.insert(EnvironmentMapLight { .insert(EnvironmentMapLight {
diffuse_map: asset_server.load("environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2"), diffuse_map: asset_server.load("environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2"),

View file

@ -239,6 +239,7 @@ fn spawn_camera(commands: &mut Commands, assets: &ExampleAssets) {
.insert(Skybox { .insert(Skybox {
image: assets.skybox.clone(), image: assets.skybox.clone(),
brightness: 150.0, brightness: 150.0,
..Default::default()
}); });
} }

View file

@ -187,6 +187,7 @@ fn add_environment_map_to_camera(
.insert(Skybox { .insert(Skybox {
image: cubemaps.skybox.clone(), image: cubemaps.skybox.clone(),
brightness: 5000.0, brightness: 5000.0,
..Default::default()
}); });
} }
} }

View file

@ -81,6 +81,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
Skybox { Skybox {
image: skybox_handle.clone(), image: skybox_handle.clone(),
brightness: 1000.0, brightness: 1000.0,
..Default::default()
}, },
)); ));

View file

@ -246,6 +246,7 @@ fn spawn_camera(commands: &mut Commands, asset_server: &AssetServer) {
.insert(Skybox { .insert(Skybox {
image: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"), image: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"),
brightness: 5000.0, brightness: 5000.0,
..Default::default()
}) })
.insert(ScreenSpaceReflectionsBundle::default()) .insert(ScreenSpaceReflectionsBundle::default())
.insert(Fxaa::default()); .insert(Fxaa::default());

View file

@ -52,6 +52,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
.insert(Skybox { .insert(Skybox {
image: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"), image: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"),
brightness: 1000.0, brightness: 1000.0,
..Default::default()
}) })
.insert(VolumetricFogSettings { .insert(VolumetricFogSettings {
// This value is explicitly set to 0 since we have no environment map light // This value is explicitly set to 0 since we have no environment map light