mirror of
https://github.com/bevyengine/bevy
synced 2024-11-22 04:33:37 +00:00
Add support for environment map transformation (#14290)
# Objective - Fixes: https://github.com/bevyengine/bevy/issues/14036 ## Solution - Add a world space transformation for the environment sample direction. ## Testing - I have tested the newly added `transform` field using the newly added `rotate_environment_map` example. https://github.com/user-attachments/assets/2de77c65-14bc-48ee-b76a-fb4e9782dbdb ## Migration Guide - Since we have added a new filed to the `EnvironmentMapLight` struct, users will need to include `..default()` or some rotation value in their initialization code.
This commit is contained in:
parent
d8d49fdd13
commit
9da18cce2a
35 changed files with 373 additions and 63 deletions
12
Cargo.toml
12
Cargo.toml
|
@ -3315,6 +3315,18 @@ description = "Demonstrates the built-in postprocessing features"
|
|||
category = "3D Rendering"
|
||||
wasm = true
|
||||
|
||||
[[example]]
|
||||
name = "rotate_environment_map"
|
||||
path = "examples/3d/rotate_environment_map.rs"
|
||||
doc-scrape-examples = true
|
||||
required-features = ["pbr_multi_layer_material_textures"]
|
||||
|
||||
[package.metadata.example.rotate_environment_map]
|
||||
name = "Rotate Environment Map"
|
||||
description = "Demonstrates how to rotate the skybox and the environment map simultaneously"
|
||||
category = "3D Rendering"
|
||||
wasm = false
|
||||
|
||||
[profile.wasm-release]
|
||||
inherits = "release"
|
||||
opt-level = "z"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::{
|
||||
graph::NodePbr, irradiance_volume::IrradianceVolume, prelude::EnvironmentMapLight,
|
||||
MeshPipeline, MeshViewBindGroup, RenderViewLightProbes, ScreenSpaceAmbientOcclusionSettings,
|
||||
ScreenSpaceReflectionsUniform, ViewLightProbesUniformOffset,
|
||||
ScreenSpaceReflectionsUniform, ViewEnvironmentMapUniformOffset, ViewLightProbesUniformOffset,
|
||||
ViewScreenSpaceReflectionsUniformOffset,
|
||||
};
|
||||
use bevy_app::prelude::*;
|
||||
|
@ -149,6 +149,7 @@ impl ViewNode for DeferredOpaquePass3dPbrLightingNode {
|
|||
&'static ViewFogUniformOffset,
|
||||
&'static ViewLightProbesUniformOffset,
|
||||
&'static ViewScreenSpaceReflectionsUniformOffset,
|
||||
&'static ViewEnvironmentMapUniformOffset,
|
||||
&'static MeshViewBindGroup,
|
||||
&'static ViewTarget,
|
||||
&'static DeferredLightingIdDepthTexture,
|
||||
|
@ -165,6 +166,7 @@ impl ViewNode for DeferredOpaquePass3dPbrLightingNode {
|
|||
view_fog_offset,
|
||||
view_light_probes_offset,
|
||||
view_ssr_offset,
|
||||
view_environment_map_offset,
|
||||
mesh_view_bind_group,
|
||||
target,
|
||||
deferred_lighting_id_depth_texture,
|
||||
|
@ -220,6 +222,7 @@ impl ViewNode for DeferredOpaquePass3dPbrLightingNode {
|
|||
view_fog_offset.offset,
|
||||
**view_light_probes_offset,
|
||||
**view_ssr_offset,
|
||||
**view_environment_map_offset,
|
||||
],
|
||||
);
|
||||
render_pass.set_bind_group(1, &bind_group_1, &[]);
|
||||
|
@ -256,11 +259,11 @@ impl SpecializedRenderPipeline for DeferredLightingLayout {
|
|||
shader_defs.push("TONEMAP_IN_SHADER".into());
|
||||
shader_defs.push(ShaderDefVal::UInt(
|
||||
"TONEMAPPING_LUT_TEXTURE_BINDING_INDEX".into(),
|
||||
20,
|
||||
21,
|
||||
));
|
||||
shader_defs.push(ShaderDefVal::UInt(
|
||||
"TONEMAPPING_LUT_SAMPLER_BINDING_INDEX".into(),
|
||||
21,
|
||||
22,
|
||||
));
|
||||
|
||||
let method = key.intersection(MeshPipelineKey::TONEMAP_METHOD_RESERVED_BITS);
|
||||
|
|
|
@ -50,13 +50,15 @@ use bevy_asset::{AssetId, Handle};
|
|||
use bevy_ecs::{
|
||||
bundle::Bundle, component::Component, query::QueryItem, system::lifetimeless::Read,
|
||||
};
|
||||
use bevy_math::Quat;
|
||||
use bevy_reflect::Reflect;
|
||||
use bevy_render::{
|
||||
extract_instances::ExtractInstance,
|
||||
prelude::SpatialBundle,
|
||||
render_asset::RenderAssets,
|
||||
render_resource::{
|
||||
binding_types, BindGroupLayoutEntryBuilder, Sampler, SamplerBindingType, Shader,
|
||||
binding_types::{self, uniform_buffer},
|
||||
BindGroupLayoutEntryBuilder, Sampler, SamplerBindingType, Shader, ShaderStages,
|
||||
TextureSampleType, TextureView,
|
||||
},
|
||||
renderer::RenderDevice,
|
||||
|
@ -67,7 +69,8 @@ use std::num::NonZeroU32;
|
|||
use std::ops::Deref;
|
||||
|
||||
use crate::{
|
||||
add_cubemap_texture_view, binding_arrays_are_usable, LightProbe, MAX_VIEW_LIGHT_PROBES,
|
||||
add_cubemap_texture_view, binding_arrays_are_usable, EnvironmentMapUniform, LightProbe,
|
||||
MAX_VIEW_LIGHT_PROBES,
|
||||
};
|
||||
|
||||
use super::{LightProbeComponent, RenderViewLightProbes};
|
||||
|
@ -96,6 +99,22 @@ pub struct EnvironmentMapLight {
|
|||
///
|
||||
/// See also <https://google.github.io/filament/Filament.html#lighting/imagebasedlights/iblunit>.
|
||||
pub intensity: f32,
|
||||
|
||||
/// World space rotation applied to the environment light cubemaps.
|
||||
/// 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 EnvironmentMapLight {
|
||||
fn default() -> Self {
|
||||
EnvironmentMapLight {
|
||||
diffuse_map: Handle::default(),
|
||||
specular_map: Handle::default(),
|
||||
intensity: 0.0,
|
||||
rotation: Quat::IDENTITY,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Like [`EnvironmentMapLight`], but contains asset IDs instead of handles.
|
||||
|
@ -193,7 +212,7 @@ impl ExtractInstance for EnvironmentMapIds {
|
|||
/// specular binding arrays respectively, in addition to the sampler.
|
||||
pub(crate) fn get_bind_group_layout_entries(
|
||||
render_device: &RenderDevice,
|
||||
) -> [BindGroupLayoutEntryBuilder; 3] {
|
||||
) -> [BindGroupLayoutEntryBuilder; 4] {
|
||||
let mut texture_cube_binding =
|
||||
binding_types::texture_cube(TextureSampleType::Float { filterable: true });
|
||||
if binding_arrays_are_usable(render_device) {
|
||||
|
@ -205,6 +224,7 @@ pub(crate) fn get_bind_group_layout_entries(
|
|||
texture_cube_binding,
|
||||
texture_cube_binding,
|
||||
binding_types::sampler(SamplerBindingType::Filtering),
|
||||
uniform_buffer::<EnvironmentMapUniform>(true).visibility(ShaderStages::FRAGMENT),
|
||||
]
|
||||
}
|
||||
|
||||
|
@ -312,6 +332,7 @@ impl LightProbeComponent for EnvironmentMapLight {
|
|||
diffuse_map: diffuse_map_handle,
|
||||
specular_map: specular_map_handle,
|
||||
intensity,
|
||||
..
|
||||
}) = view_component
|
||||
{
|
||||
if let (Some(_), Some(specular_map)) = (
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#import bevy_pbr::light_probe::query_light_probe
|
||||
#import bevy_pbr::mesh_view_bindings as bindings
|
||||
#import bevy_pbr::mesh_view_bindings::light_probes
|
||||
#import bevy_pbr::mesh_view_bindings::environment_map_uniform
|
||||
#import bevy_pbr::lighting::{
|
||||
F_Schlick_vec, LayerLightingInput, LightingInput, LAYER_BASE, LAYER_CLEARCOAT
|
||||
}
|
||||
|
@ -57,17 +58,29 @@ fn compute_radiances(
|
|||
bindings::specular_environment_maps[query_result.texture_index]) - 1u);
|
||||
|
||||
if (!found_diffuse_indirect) {
|
||||
var irradiance_sample_dir = N;
|
||||
// Rotating the world space ray direction by the environment light map transform matrix, it is
|
||||
// equivalent to rotating the diffuse environment cubemap itself.
|
||||
irradiance_sample_dir = (environment_map_uniform.transform * vec4(irradiance_sample_dir, 1.0)).xyz;
|
||||
// Cube maps are left-handed so we negate the z coordinate.
|
||||
irradiance_sample_dir.z = -irradiance_sample_dir.z;
|
||||
radiances.irradiance = textureSampleLevel(
|
||||
bindings::diffuse_environment_maps[query_result.texture_index],
|
||||
bindings::environment_map_sampler,
|
||||
vec3(N.xy, -N.z),
|
||||
irradiance_sample_dir,
|
||||
0.0).rgb * query_result.intensity;
|
||||
}
|
||||
|
||||
var radiance_sample_dir = R;
|
||||
// Rotating the world space ray direction by the environment light map transform matrix, it is
|
||||
// equivalent to rotating the specular environment cubemap itself.
|
||||
radiance_sample_dir = (environment_map_uniform.transform * vec4(radiance_sample_dir, 1.0)).xyz;
|
||||
// Cube maps are left-handed so we negate the z coordinate.
|
||||
radiance_sample_dir.z = -radiance_sample_dir.z;
|
||||
radiances.radiance = textureSampleLevel(
|
||||
bindings::specular_environment_maps[query_result.texture_index],
|
||||
bindings::environment_map_sampler,
|
||||
vec3(R.xy, -R.z),
|
||||
radiance_sample_dir,
|
||||
radiance_level).rgb * query_result.intensity;
|
||||
|
||||
return radiances;
|
||||
|
@ -102,17 +115,29 @@ fn compute_radiances(
|
|||
let intensity = light_probes.intensity_for_view;
|
||||
|
||||
if (!found_diffuse_indirect) {
|
||||
var irradiance_sample_dir = N;
|
||||
// Rotating the world space ray direction by the environment light map transform matrix, it is
|
||||
// equivalent to rotating the diffuse environment cubemap itself.
|
||||
irradiance_sample_dir = (environment_map_uniform.transform * vec4(irradiance_sample_dir, 1.0)).xyz;
|
||||
// Cube maps are left-handed so we negate the z coordinate.
|
||||
irradiance_sample_dir.z = -irradiance_sample_dir.z;
|
||||
radiances.irradiance = textureSampleLevel(
|
||||
bindings::diffuse_environment_map,
|
||||
bindings::environment_map_sampler,
|
||||
vec3(N.xy, -N.z),
|
||||
irradiance_sample_dir,
|
||||
0.0).rgb * intensity;
|
||||
}
|
||||
|
||||
var radiance_sample_dir = R;
|
||||
// Rotating the world space ray direction by the environment light map transform matrix, it is
|
||||
// equivalent to rotating the specular environment cubemap itself.
|
||||
radiance_sample_dir = (environment_map_uniform.transform * vec4(radiance_sample_dir, 1.0)).xyz;
|
||||
// Cube maps are left-handed so we negate the z coordinate.
|
||||
radiance_sample_dir.z = -radiance_sample_dir.z;
|
||||
radiances.radiance = textureSampleLevel(
|
||||
bindings::specular_environment_map,
|
||||
bindings::environment_map_sampler,
|
||||
vec3(R.xy, -R.z),
|
||||
radiance_sample_dir,
|
||||
radiance_level).rgb * intensity;
|
||||
|
||||
return radiances;
|
||||
|
|
|
@ -25,7 +25,7 @@ use bevy_render::{
|
|||
view::ExtractedView,
|
||||
Extract, ExtractSchedule, Render, RenderApp, RenderSet,
|
||||
};
|
||||
use bevy_transform::prelude::GlobalTransform;
|
||||
use bevy_transform::{components::Transform, prelude::GlobalTransform};
|
||||
use bevy_utils::{tracing::error, HashMap};
|
||||
|
||||
use std::hash::Hash;
|
||||
|
@ -296,6 +296,31 @@ impl LightProbe {
|
|||
}
|
||||
}
|
||||
|
||||
/// The uniform struct extracted from [`EnvironmentMapLight`].
|
||||
/// Will be available for use in the Environment Map shader.
|
||||
#[derive(Component, ShaderType, Clone)]
|
||||
pub struct EnvironmentMapUniform {
|
||||
/// The world space transformation matrix of the sample ray for environment cubemaps.
|
||||
transform: Mat4,
|
||||
}
|
||||
|
||||
impl Default for EnvironmentMapUniform {
|
||||
fn default() -> Self {
|
||||
EnvironmentMapUniform {
|
||||
transform: Mat4::IDENTITY,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A GPU buffer that stores the environment map settings for each view.
|
||||
#[derive(Resource, Default, Deref, DerefMut)]
|
||||
pub struct EnvironmentMapUniformBuffer(pub DynamicUniformBuffer<EnvironmentMapUniform>);
|
||||
|
||||
/// A component that stores the offset within the
|
||||
/// [`EnvironmentMapUniformBuffer`] for each view.
|
||||
#[derive(Component, Default, Deref, DerefMut)]
|
||||
pub struct ViewEnvironmentMapUniformOffset(u32);
|
||||
|
||||
impl Plugin for LightProbePlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
load_internal_asset!(
|
||||
|
@ -330,15 +355,41 @@ impl Plugin for LightProbePlugin {
|
|||
render_app
|
||||
.add_plugins(ExtractInstancesPlugin::<EnvironmentMapIds>::new())
|
||||
.init_resource::<LightProbesBuffer>()
|
||||
.init_resource::<EnvironmentMapUniformBuffer>()
|
||||
.add_systems(ExtractSchedule, gather_environment_map_uniform)
|
||||
.add_systems(ExtractSchedule, gather_light_probes::<EnvironmentMapLight>)
|
||||
.add_systems(ExtractSchedule, gather_light_probes::<IrradianceVolume>)
|
||||
.add_systems(
|
||||
Render,
|
||||
upload_light_probes.in_set(RenderSet::PrepareResources),
|
||||
(upload_light_probes, prepare_environment_uniform_buffer)
|
||||
.in_set(RenderSet::PrepareResources),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Extracts [`EnvironmentMapLight`] from views and creates [`EnvironmentMapUniform`] for them.
|
||||
/// Compared to the `ExtractComponentPlugin`, this implementation will create a default instance
|
||||
/// if one does not already exist.
|
||||
fn gather_environment_map_uniform(
|
||||
view_query: Extract<Query<(Entity, Option<&EnvironmentMapLight>), With<Camera3d>>>,
|
||||
mut commands: Commands,
|
||||
) {
|
||||
for (view_entity, environment_map_light) in view_query.iter() {
|
||||
let environment_map_uniform = if let Some(environment_map_light) = environment_map_light {
|
||||
EnvironmentMapUniform {
|
||||
transform: Transform::from_rotation(environment_map_light.rotation)
|
||||
.compute_matrix()
|
||||
.inverse(),
|
||||
}
|
||||
} else {
|
||||
EnvironmentMapUniform::default()
|
||||
};
|
||||
commands
|
||||
.get_or_spawn(view_entity)
|
||||
.insert(environment_map_uniform);
|
||||
}
|
||||
}
|
||||
|
||||
/// Gathers up all light probes of a single type in the scene and assigns them
|
||||
/// to views, performing frustum culling and distance sorting in the process.
|
||||
fn gather_light_probes<C>(
|
||||
|
@ -395,6 +446,32 @@ fn gather_light_probes<C>(
|
|||
}
|
||||
}
|
||||
|
||||
/// Gathers up environment map settings for each applicable view and
|
||||
/// writes them into a GPU buffer.
|
||||
pub fn prepare_environment_uniform_buffer(
|
||||
mut commands: Commands,
|
||||
views: Query<(Entity, Option<&EnvironmentMapUniform>), With<ExtractedView>>,
|
||||
mut environment_uniform_buffer: ResMut<EnvironmentMapUniformBuffer>,
|
||||
render_device: Res<RenderDevice>,
|
||||
render_queue: Res<RenderQueue>,
|
||||
) {
|
||||
let Some(mut writer) =
|
||||
environment_uniform_buffer.get_writer(views.iter().len(), &render_device, &render_queue)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
for (view, environment_uniform) in views.iter() {
|
||||
let uniform_offset = match environment_uniform {
|
||||
None => 0,
|
||||
Some(environment_uniform) => writer.write(environment_uniform),
|
||||
};
|
||||
commands
|
||||
.entity(view)
|
||||
.insert(ViewEnvironmentMapUniformOffset(uniform_offset));
|
||||
}
|
||||
}
|
||||
|
||||
// A system that runs after [`gather_light_probes`] and populates the GPU
|
||||
// uniforms with the results.
|
||||
//
|
||||
|
|
|
@ -7,8 +7,8 @@ use super::{
|
|||
MeshletGpuScene,
|
||||
};
|
||||
use crate::{
|
||||
MeshViewBindGroup, PrepassViewBindGroup, ViewFogUniformOffset, ViewLightProbesUniformOffset,
|
||||
ViewLightsUniformOffset, ViewScreenSpaceReflectionsUniformOffset,
|
||||
MeshViewBindGroup, PrepassViewBindGroup, ViewEnvironmentMapUniformOffset, ViewFogUniformOffset,
|
||||
ViewLightProbesUniformOffset, ViewLightsUniformOffset, ViewScreenSpaceReflectionsUniformOffset,
|
||||
};
|
||||
use bevy_core_pipeline::prepass::{
|
||||
MotionVectorPrepass, PreviousViewUniformOffset, ViewPrepassTextures,
|
||||
|
@ -41,6 +41,7 @@ impl ViewNode for MeshletMainOpaquePass3dNode {
|
|||
&'static ViewFogUniformOffset,
|
||||
&'static ViewLightProbesUniformOffset,
|
||||
&'static ViewScreenSpaceReflectionsUniformOffset,
|
||||
&'static ViewEnvironmentMapUniformOffset,
|
||||
&'static MeshletViewMaterialsMainOpaquePass,
|
||||
&'static MeshletViewBindGroups,
|
||||
&'static MeshletViewResources,
|
||||
|
@ -59,6 +60,7 @@ impl ViewNode for MeshletMainOpaquePass3dNode {
|
|||
view_fog_offset,
|
||||
view_light_probes_offset,
|
||||
view_ssr_offset,
|
||||
view_environment_map_offset,
|
||||
meshlet_view_materials,
|
||||
meshlet_view_bind_groups,
|
||||
meshlet_view_resources,
|
||||
|
@ -111,6 +113,7 @@ impl ViewNode for MeshletMainOpaquePass3dNode {
|
|||
view_fog_offset.offset,
|
||||
**view_light_probes_offset,
|
||||
**view_ssr_offset,
|
||||
**view_environment_map_offset,
|
||||
],
|
||||
);
|
||||
render_pass.set_bind_group(1, meshlet_material_draw_bind_group, &[]);
|
||||
|
|
|
@ -1774,11 +1774,11 @@ impl SpecializedMeshPipeline for MeshPipeline {
|
|||
shader_defs.push("TONEMAP_IN_SHADER".into());
|
||||
shader_defs.push(ShaderDefVal::UInt(
|
||||
"TONEMAPPING_LUT_TEXTURE_BINDING_INDEX".into(),
|
||||
20,
|
||||
21,
|
||||
));
|
||||
shader_defs.push(ShaderDefVal::UInt(
|
||||
"TONEMAPPING_LUT_SAMPLER_BINDING_INDEX".into(),
|
||||
21,
|
||||
22,
|
||||
));
|
||||
|
||||
let method = key.intersection(MeshPipelineKey::TONEMAP_METHOD_RESERVED_BITS);
|
||||
|
@ -2105,6 +2105,7 @@ impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetMeshViewBindGroup<I>
|
|||
Read<ViewFogUniformOffset>,
|
||||
Read<ViewLightProbesUniformOffset>,
|
||||
Read<ViewScreenSpaceReflectionsUniformOffset>,
|
||||
Read<ViewEnvironmentMapUniformOffset>,
|
||||
Read<MeshViewBindGroup>,
|
||||
);
|
||||
type ItemQuery = ();
|
||||
|
@ -2112,10 +2113,15 @@ impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetMeshViewBindGroup<I>
|
|||
#[inline]
|
||||
fn render<'w>(
|
||||
_item: &P,
|
||||
(view_uniform, view_lights, view_fog, view_light_probes, view_ssr, mesh_view_bind_group): ROQueryItem<
|
||||
'w,
|
||||
Self::ViewQuery,
|
||||
>,
|
||||
(
|
||||
view_uniform,
|
||||
view_lights,
|
||||
view_fog,
|
||||
view_light_probes,
|
||||
view_ssr,
|
||||
view_environment_map,
|
||||
mesh_view_bind_group,
|
||||
): ROQueryItem<'w, Self::ViewQuery>,
|
||||
_entity: Option<()>,
|
||||
_: SystemParamItem<'w, '_, Self::Param>,
|
||||
pass: &mut TrackedRenderPass<'w>,
|
||||
|
@ -2129,6 +2135,7 @@ impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetMeshViewBindGroup<I>
|
|||
view_fog.offset,
|
||||
**view_light_probes,
|
||||
**view_ssr,
|
||||
**view_environment_map,
|
||||
],
|
||||
);
|
||||
|
||||
|
|
|
@ -41,11 +41,11 @@ use crate::{
|
|||
self, IrradianceVolume, RenderViewIrradianceVolumeBindGroupEntries,
|
||||
IRRADIANCE_VOLUMES_ARE_USABLE,
|
||||
},
|
||||
prepass, FogMeta, GlobalClusterableObjectMeta, GpuClusterableObjects, GpuFog, GpuLights,
|
||||
LightMeta, LightProbesBuffer, LightProbesUniform, MeshPipeline, MeshPipelineKey,
|
||||
RenderViewLightProbes, ScreenSpaceAmbientOcclusionTextures, ScreenSpaceReflectionsBuffer,
|
||||
ScreenSpaceReflectionsUniform, ShadowSamplers, ViewClusterBindings, ViewShadowBindings,
|
||||
CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT,
|
||||
prepass, EnvironmentMapUniformBuffer, FogMeta, GlobalClusterableObjectMeta,
|
||||
GpuClusterableObjects, GpuFog, GpuLights, LightMeta, LightProbesBuffer, LightProbesUniform,
|
||||
MeshPipeline, MeshPipelineKey, RenderViewLightProbes, ScreenSpaceAmbientOcclusionTextures,
|
||||
ScreenSpaceReflectionsBuffer, ScreenSpaceReflectionsUniform, ShadowSamplers,
|
||||
ViewClusterBindings, ViewShadowBindings, CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -299,6 +299,7 @@ fn layout_entries(
|
|||
(15, environment_map_entries[0]),
|
||||
(16, environment_map_entries[1]),
|
||||
(17, environment_map_entries[2]),
|
||||
(18, environment_map_entries[3]),
|
||||
));
|
||||
|
||||
// Irradiance volumes
|
||||
|
@ -306,16 +307,16 @@ fn layout_entries(
|
|||
let irradiance_volume_entries =
|
||||
irradiance_volume::get_bind_group_layout_entries(render_device);
|
||||
entries = entries.extend_with_indices((
|
||||
(18, irradiance_volume_entries[0]),
|
||||
(19, irradiance_volume_entries[1]),
|
||||
(19, irradiance_volume_entries[0]),
|
||||
(20, irradiance_volume_entries[1]),
|
||||
));
|
||||
}
|
||||
|
||||
// Tonemapping
|
||||
let tonemapping_lut_entries = get_lut_bind_group_layout_entries();
|
||||
entries = entries.extend_with_indices((
|
||||
(20, tonemapping_lut_entries[0]),
|
||||
(21, tonemapping_lut_entries[1]),
|
||||
(21, tonemapping_lut_entries[0]),
|
||||
(22, tonemapping_lut_entries[1]),
|
||||
));
|
||||
|
||||
// Prepass
|
||||
|
@ -325,7 +326,7 @@ fn layout_entries(
|
|||
{
|
||||
for (entry, binding) in prepass::get_bind_group_layout_entries(layout_key)
|
||||
.iter()
|
||||
.zip([22, 23, 24, 25])
|
||||
.zip([23, 24, 25, 26])
|
||||
{
|
||||
if let Some(entry) = entry {
|
||||
entries = entries.extend_with_indices(((binding as u32, *entry),));
|
||||
|
@ -336,10 +337,10 @@ fn layout_entries(
|
|||
// View Transmission Texture
|
||||
entries = entries.extend_with_indices((
|
||||
(
|
||||
26,
|
||||
27,
|
||||
texture_2d(TextureSampleType::Float { filterable: true }),
|
||||
),
|
||||
(27, sampler(SamplerBindingType::Filtering)),
|
||||
(28, sampler(SamplerBindingType::Filtering)),
|
||||
));
|
||||
|
||||
entries.to_vec()
|
||||
|
@ -450,7 +451,7 @@ pub fn prepare_mesh_view_bind_groups(
|
|||
light_meta: Res<LightMeta>,
|
||||
global_light_meta: Res<GlobalClusterableObjectMeta>,
|
||||
fog_meta: Res<FogMeta>,
|
||||
view_uniforms: Res<ViewUniforms>,
|
||||
(view_uniforms, environment_map_uniform): (Res<ViewUniforms>, Res<EnvironmentMapUniformBuffer>),
|
||||
views: Query<(
|
||||
Entity,
|
||||
&ViewShadowBindings,
|
||||
|
@ -484,6 +485,7 @@ pub fn prepare_mesh_view_bind_groups(
|
|||
Some(light_probes_binding),
|
||||
Some(visibility_ranges_buffer),
|
||||
Some(ssr_binding),
|
||||
Some(environment_map_binding),
|
||||
) = (
|
||||
view_uniforms.uniforms.binding(),
|
||||
light_meta.view_gpu_lights.binding(),
|
||||
|
@ -493,6 +495,7 @@ pub fn prepare_mesh_view_bind_groups(
|
|||
light_probes_buffer.binding(),
|
||||
visibility_ranges.buffer().buffer(),
|
||||
ssr_buffer.binding(),
|
||||
environment_map_uniform.binding(),
|
||||
) {
|
||||
for (
|
||||
entity,
|
||||
|
@ -559,6 +562,7 @@ pub fn prepare_mesh_view_bind_groups(
|
|||
(15, diffuse_texture_view),
|
||||
(16, specular_texture_view),
|
||||
(17, sampler),
|
||||
(18, environment_map_binding.clone()),
|
||||
));
|
||||
}
|
||||
RenderViewEnvironmentMapBindGroupEntries::Multiple {
|
||||
|
@ -570,6 +574,7 @@ pub fn prepare_mesh_view_bind_groups(
|
|||
(15, diffuse_texture_views.as_slice()),
|
||||
(16, specular_texture_views.as_slice()),
|
||||
(17, sampler),
|
||||
(18, environment_map_binding.clone()),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -590,21 +595,21 @@ pub fn prepare_mesh_view_bind_groups(
|
|||
texture_view,
|
||||
sampler,
|
||||
}) => {
|
||||
entries = entries.extend_with_indices(((18, texture_view), (19, sampler)));
|
||||
entries = entries.extend_with_indices(((19, texture_view), (20, sampler)));
|
||||
}
|
||||
Some(RenderViewIrradianceVolumeBindGroupEntries::Multiple {
|
||||
ref texture_views,
|
||||
sampler,
|
||||
}) => {
|
||||
entries = entries
|
||||
.extend_with_indices(((18, texture_views.as_slice()), (19, sampler)));
|
||||
.extend_with_indices(((19, texture_views.as_slice()), (20, sampler)));
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
|
||||
let lut_bindings =
|
||||
get_lut_bindings(&images, &tonemapping_luts, tonemapping, &fallback_image);
|
||||
entries = entries.extend_with_indices(((20, lut_bindings.0), (21, lut_bindings.1)));
|
||||
entries = entries.extend_with_indices(((21, lut_bindings.0), (22, lut_bindings.1)));
|
||||
|
||||
// When using WebGL, we can't have a depth texture with multisampling
|
||||
let prepass_bindings;
|
||||
|
@ -614,7 +619,7 @@ pub fn prepare_mesh_view_bind_groups(
|
|||
for (binding, index) in prepass_bindings
|
||||
.iter()
|
||||
.map(Option::as_ref)
|
||||
.zip([22, 23, 24, 25])
|
||||
.zip([23, 24, 25, 26])
|
||||
.flat_map(|(b, i)| b.map(|b| (b, i)))
|
||||
{
|
||||
entries = entries.extend_with_indices(((index, binding),));
|
||||
|
@ -630,7 +635,7 @@ pub fn prepare_mesh_view_bind_groups(
|
|||
.unwrap_or(&fallback_image_zero.sampler);
|
||||
|
||||
entries =
|
||||
entries.extend_with_indices(((26, transmission_view), (27, transmission_sampler)));
|
||||
entries.extend_with_indices(((27, transmission_view), (28, transmission_sampler)));
|
||||
|
||||
commands.entity(entity).insert(MeshViewBindGroup {
|
||||
value: render_device.create_bind_group("mesh_view_bind_group", layout, &entries),
|
||||
|
|
|
@ -53,47 +53,48 @@ const VISIBILITY_RANGE_UNIFORM_BUFFER_SIZE: u32 = 64u;
|
|||
@group(0) @binding(16) var specular_environment_map: texture_cube<f32>;
|
||||
#endif
|
||||
@group(0) @binding(17) var environment_map_sampler: sampler;
|
||||
@group(0) @binding(18) var<uniform> environment_map_uniform: types::EnvironmentMapUniform;
|
||||
|
||||
#ifdef IRRADIANCE_VOLUMES_ARE_USABLE
|
||||
#ifdef MULTIPLE_LIGHT_PROBES_IN_ARRAY
|
||||
@group(0) @binding(18) var irradiance_volumes: binding_array<texture_3d<f32>, 8u>;
|
||||
@group(0) @binding(19) var irradiance_volumes: binding_array<texture_3d<f32>, 8u>;
|
||||
#else
|
||||
@group(0) @binding(18) var irradiance_volume: texture_3d<f32>;
|
||||
@group(0) @binding(19) var irradiance_volume: texture_3d<f32>;
|
||||
#endif
|
||||
@group(0) @binding(19) var irradiance_volume_sampler: sampler;
|
||||
@group(0) @binding(20) var irradiance_volume_sampler: sampler;
|
||||
#endif
|
||||
|
||||
@group(0) @binding(20) var dt_lut_texture: texture_3d<f32>;
|
||||
@group(0) @binding(21) var dt_lut_sampler: sampler;
|
||||
@group(0) @binding(21) var dt_lut_texture: texture_3d<f32>;
|
||||
@group(0) @binding(22) var dt_lut_sampler: sampler;
|
||||
|
||||
#ifdef MULTISAMPLED
|
||||
#ifdef DEPTH_PREPASS
|
||||
@group(0) @binding(22) var depth_prepass_texture: texture_depth_multisampled_2d;
|
||||
@group(0) @binding(23) var depth_prepass_texture: texture_depth_multisampled_2d;
|
||||
#endif // DEPTH_PREPASS
|
||||
#ifdef NORMAL_PREPASS
|
||||
@group(0) @binding(23) var normal_prepass_texture: texture_multisampled_2d<f32>;
|
||||
@group(0) @binding(24) var normal_prepass_texture: texture_multisampled_2d<f32>;
|
||||
#endif // NORMAL_PREPASS
|
||||
#ifdef MOTION_VECTOR_PREPASS
|
||||
@group(0) @binding(24) var motion_vector_prepass_texture: texture_multisampled_2d<f32>;
|
||||
@group(0) @binding(25) var motion_vector_prepass_texture: texture_multisampled_2d<f32>;
|
||||
#endif // MOTION_VECTOR_PREPASS
|
||||
|
||||
#else // MULTISAMPLED
|
||||
|
||||
#ifdef DEPTH_PREPASS
|
||||
@group(0) @binding(22) var depth_prepass_texture: texture_depth_2d;
|
||||
@group(0) @binding(23) var depth_prepass_texture: texture_depth_2d;
|
||||
#endif // DEPTH_PREPASS
|
||||
#ifdef NORMAL_PREPASS
|
||||
@group(0) @binding(23) var normal_prepass_texture: texture_2d<f32>;
|
||||
@group(0) @binding(24) var normal_prepass_texture: texture_2d<f32>;
|
||||
#endif // NORMAL_PREPASS
|
||||
#ifdef MOTION_VECTOR_PREPASS
|
||||
@group(0) @binding(24) var motion_vector_prepass_texture: texture_2d<f32>;
|
||||
@group(0) @binding(25) var motion_vector_prepass_texture: texture_2d<f32>;
|
||||
#endif // MOTION_VECTOR_PREPASS
|
||||
|
||||
#endif // MULTISAMPLED
|
||||
|
||||
#ifdef DEFERRED_PREPASS
|
||||
@group(0) @binding(25) var deferred_prepass_texture: texture_2d<u32>;
|
||||
@group(0) @binding(26) var deferred_prepass_texture: texture_2d<u32>;
|
||||
#endif // DEFERRED_PREPASS
|
||||
|
||||
@group(0) @binding(26) var view_transmission_texture: texture_2d<f32>;
|
||||
@group(0) @binding(27) var view_transmission_sampler: sampler;
|
||||
@group(0) @binding(27) var view_transmission_texture: texture_2d<f32>;
|
||||
@group(0) @binding(28) var view_transmission_sampler: sampler;
|
||||
|
|
|
@ -148,3 +148,8 @@ struct ScreenSpaceReflectionsSettings {
|
|||
bisection_steps: u32,
|
||||
use_secant: u32,
|
||||
};
|
||||
|
||||
struct EnvironmentMapUniform {
|
||||
// Transformation matrix for the environment cubemaps in world space.
|
||||
transform: mat4x4<f32>,
|
||||
};
|
|
@ -43,7 +43,8 @@ use bevy_utils::{info_once, prelude::default};
|
|||
use crate::{
|
||||
binding_arrays_are_usable, graph::NodePbr, prelude::EnvironmentMapLight,
|
||||
MeshPipelineViewLayoutKey, MeshPipelineViewLayouts, MeshViewBindGroup, RenderViewLightProbes,
|
||||
ViewFogUniformOffset, ViewLightProbesUniformOffset, ViewLightsUniformOffset,
|
||||
ViewEnvironmentMapUniformOffset, ViewFogUniformOffset, ViewLightProbesUniformOffset,
|
||||
ViewLightsUniformOffset,
|
||||
};
|
||||
|
||||
const SSR_SHADER_HANDLE: Handle<Shader> = Handle::weak_from_u128(10438925299917978850);
|
||||
|
@ -258,6 +259,7 @@ impl ViewNode for ScreenSpaceReflectionsNode {
|
|||
Read<ViewFogUniformOffset>,
|
||||
Read<ViewLightProbesUniformOffset>,
|
||||
Read<ViewScreenSpaceReflectionsUniformOffset>,
|
||||
Read<ViewEnvironmentMapUniformOffset>,
|
||||
Read<MeshViewBindGroup>,
|
||||
Read<ScreenSpaceReflectionsPipelineId>,
|
||||
);
|
||||
|
@ -273,6 +275,7 @@ impl ViewNode for ScreenSpaceReflectionsNode {
|
|||
view_fog_offset,
|
||||
view_light_probes_offset,
|
||||
view_ssr_offset,
|
||||
view_environment_map_offset,
|
||||
view_bind_group,
|
||||
ssr_pipeline_id,
|
||||
): QueryItem<'w, Self::ViewQuery>,
|
||||
|
@ -324,6 +327,7 @@ impl ViewNode for ScreenSpaceReflectionsNode {
|
|||
view_fog_offset.offset,
|
||||
**view_light_probes_offset,
|
||||
**view_ssr_offset,
|
||||
**view_environment_map_offset,
|
||||
],
|
||||
);
|
||||
|
||||
|
|
|
@ -46,8 +46,9 @@ use bitflags::bitflags;
|
|||
|
||||
use crate::{
|
||||
FogVolume, MeshPipelineViewLayoutKey, MeshPipelineViewLayouts, MeshViewBindGroup,
|
||||
ViewFogUniformOffset, ViewLightProbesUniformOffset, ViewLightsUniformOffset,
|
||||
ViewScreenSpaceReflectionsUniformOffset, VolumetricFogSettings, VolumetricLight,
|
||||
ViewEnvironmentMapUniformOffset, ViewFogUniformOffset, ViewLightProbesUniformOffset,
|
||||
ViewLightsUniformOffset, ViewScreenSpaceReflectionsUniformOffset, VolumetricFogSettings,
|
||||
VolumetricLight,
|
||||
};
|
||||
|
||||
bitflags! {
|
||||
|
@ -306,6 +307,7 @@ impl ViewNode for VolumetricFogNode {
|
|||
Read<ViewVolumetricFog>,
|
||||
Read<MeshViewBindGroup>,
|
||||
Read<ViewScreenSpaceReflectionsUniformOffset>,
|
||||
Read<ViewEnvironmentMapUniformOffset>,
|
||||
);
|
||||
|
||||
fn run<'w>(
|
||||
|
@ -323,6 +325,7 @@ impl ViewNode for VolumetricFogNode {
|
|||
view_fog_volumes,
|
||||
view_bind_group,
|
||||
view_ssr_offset,
|
||||
view_environment_map_offset,
|
||||
): QueryItem<'w, Self::ViewQuery>,
|
||||
world: &'w World,
|
||||
) -> Result<(), NodeRunError> {
|
||||
|
@ -445,6 +448,7 @@ impl ViewNode for VolumetricFogNode {
|
|||
view_fog_offset.offset,
|
||||
**view_light_probes_offset,
|
||||
**view_ssr_offset,
|
||||
**view_environment_map_offset,
|
||||
],
|
||||
);
|
||||
render_pass.set_bind_group(
|
||||
|
|
|
@ -26,6 +26,7 @@ fn setup(
|
|||
diffuse_map: asset_server.load("environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2"),
|
||||
specular_map: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"),
|
||||
intensity: 2_000.0,
|
||||
..default()
|
||||
},
|
||||
));
|
||||
|
||||
|
|
|
@ -240,12 +240,13 @@ fn add_skybox_and_environment_map(
|
|||
.insert(Skybox {
|
||||
brightness: 5000.0,
|
||||
image: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"),
|
||||
..Default::default()
|
||||
..default()
|
||||
})
|
||||
.insert(EnvironmentMapLight {
|
||||
diffuse_map: asset_server.load("environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2"),
|
||||
specular_map: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"),
|
||||
intensity: 2500.0,
|
||||
..default()
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -324,6 +324,7 @@ fn setup(
|
|||
diffuse_map: asset_server.load("environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2"),
|
||||
specular_map: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"),
|
||||
intensity: 150.0,
|
||||
..default()
|
||||
},
|
||||
FogSettings {
|
||||
color: Color::srgba_u8(43, 44, 47, 255),
|
||||
|
|
|
@ -54,7 +54,7 @@ fn setup(
|
|||
Skybox {
|
||||
image: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"),
|
||||
brightness: bevy::pbr::light_consts::lux::DIRECT_SUNLIGHT,
|
||||
..Default::default()
|
||||
..default()
|
||||
},
|
||||
));
|
||||
|
||||
|
|
|
@ -224,12 +224,13 @@ fn spawn_camera(commands: &mut Commands, asset_server: &AssetServer) {
|
|||
.insert(Skybox {
|
||||
brightness: 5000.0,
|
||||
image: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"),
|
||||
..Default::default()
|
||||
..default()
|
||||
})
|
||||
.insert(EnvironmentMapLight {
|
||||
diffuse_map: asset_server.load("environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2"),
|
||||
specular_map: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"),
|
||||
intensity: 2000.0,
|
||||
..default()
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -374,6 +374,7 @@ fn add_camera(commands: &mut Commands, asset_server: &AssetServer, color_grading
|
|||
diffuse_map: asset_server.load("environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2"),
|
||||
specular_map: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"),
|
||||
intensity: 2000.0,
|
||||
..default()
|
||||
},
|
||||
));
|
||||
}
|
||||
|
|
|
@ -56,6 +56,7 @@ fn setup(
|
|||
diffuse_map: asset_server.load("environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2"),
|
||||
specular_map: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"),
|
||||
intensity: 2000.0,
|
||||
..default()
|
||||
},
|
||||
DepthPrepass,
|
||||
MotionVectorPrepass,
|
||||
|
|
|
@ -239,7 +239,7 @@ fn spawn_camera(commands: &mut Commands, assets: &ExampleAssets) {
|
|||
.insert(Skybox {
|
||||
image: assets.skybox.clone(),
|
||||
brightness: 150.0,
|
||||
..Default::default()
|
||||
..default()
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
|||
diffuse_map: asset_server.load("environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2"),
|
||||
specular_map: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"),
|
||||
intensity: 250.0,
|
||||
..default()
|
||||
},
|
||||
));
|
||||
|
||||
|
|
|
@ -56,6 +56,7 @@ fn setup(
|
|||
diffuse_map: asset_server.load("environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2"),
|
||||
specular_map: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"),
|
||||
intensity: 150.0,
|
||||
..default()
|
||||
},
|
||||
CameraController::default(),
|
||||
));
|
||||
|
|
|
@ -130,6 +130,7 @@ fn setup(
|
|||
diffuse_map: asset_server.load("environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2"),
|
||||
specular_map: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"),
|
||||
intensity: 900.0,
|
||||
..default()
|
||||
},
|
||||
));
|
||||
}
|
||||
|
|
|
@ -80,6 +80,7 @@ fn spawn_camera(commands: &mut Commands, asset_server: &AssetServer) {
|
|||
diffuse_map: asset_server.load("environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2"),
|
||||
specular_map: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"),
|
||||
intensity: 2000.0,
|
||||
..default()
|
||||
},
|
||||
// Include the `ChromaticAberration` component.
|
||||
ChromaticAberration::default(),
|
||||
|
|
|
@ -151,6 +151,7 @@ fn spawn_reflection_probe(commands: &mut Commands, cubemaps: &Cubemaps) {
|
|||
diffuse_map: cubemaps.diffuse.clone(),
|
||||
specular_map: cubemaps.specular_reflection_probe.clone(),
|
||||
intensity: 5000.0,
|
||||
..default()
|
||||
},
|
||||
});
|
||||
}
|
||||
|
@ -187,7 +188,7 @@ fn add_environment_map_to_camera(
|
|||
.insert(Skybox {
|
||||
image: cubemaps.skybox.clone(),
|
||||
brightness: 5000.0,
|
||||
..Default::default()
|
||||
..default()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -299,6 +300,7 @@ fn create_camera_environment_map_light(cubemaps: &Cubemaps) -> EnvironmentMapLig
|
|||
diffuse_map: cubemaps.diffuse.clone(),
|
||||
specular_map: cubemaps.specular_environment_map.clone(),
|
||||
intensity: 5000.0,
|
||||
..default()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
124
examples/3d/rotate_environment_map.rs
Normal file
124
examples/3d/rotate_environment_map.rs
Normal file
|
@ -0,0 +1,124 @@
|
|||
//! Demonstrates how to rotate the skybox and the environment map simultaneously.
|
||||
|
||||
use std::f32::consts::PI;
|
||||
|
||||
use bevy::{
|
||||
color::palettes::css::{GOLD, WHITE},
|
||||
core_pipeline::{tonemapping::Tonemapping::AcesFitted, Skybox},
|
||||
prelude::*,
|
||||
render::texture::ImageLoaderSettings,
|
||||
};
|
||||
|
||||
/// Entry point.
|
||||
pub fn main() {
|
||||
App::new()
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_systems(Startup, setup)
|
||||
.add_systems(Update, rotate_skybox_and_environment_map)
|
||||
.run();
|
||||
}
|
||||
|
||||
/// Initializes the scene.
|
||||
fn setup(
|
||||
mut commands: Commands,
|
||||
mut meshes: ResMut<Assets<Mesh>>,
|
||||
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||
asset_server: Res<AssetServer>,
|
||||
) {
|
||||
let sphere_mesh = create_sphere_mesh(&mut meshes);
|
||||
spawn_sphere(&mut commands, &mut materials, &asset_server, &sphere_mesh);
|
||||
spawn_light(&mut commands);
|
||||
spawn_camera(&mut commands, &asset_server);
|
||||
}
|
||||
|
||||
/// Rotate the skybox and the environment map per frame.
|
||||
fn rotate_skybox_and_environment_map(
|
||||
mut environments: Query<(&mut Skybox, &mut EnvironmentMapLight)>,
|
||||
time: Res<Time>,
|
||||
) {
|
||||
let now = time.elapsed_seconds();
|
||||
let rotation = Quat::from_rotation_y(0.2 * now);
|
||||
for (mut skybox, mut environment_map) in environments.iter_mut() {
|
||||
skybox.rotation = rotation;
|
||||
environment_map.rotation = rotation;
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates a sphere.
|
||||
fn create_sphere_mesh(meshes: &mut Assets<Mesh>) -> Handle<Mesh> {
|
||||
// We're going to use normal maps, so make sure we've generated tangents, or
|
||||
// else the normal maps won't show up.
|
||||
|
||||
let mut sphere_mesh = Sphere::new(1.0).mesh().build();
|
||||
sphere_mesh
|
||||
.generate_tangents()
|
||||
.expect("Failed to generate tangents");
|
||||
meshes.add(sphere_mesh)
|
||||
}
|
||||
|
||||
/// Spawn a regular object with a clearcoat layer. This looks like car paint.
|
||||
fn spawn_sphere(
|
||||
commands: &mut Commands,
|
||||
materials: &mut Assets<StandardMaterial>,
|
||||
asset_server: &AssetServer,
|
||||
sphere_mesh: &Handle<Mesh>,
|
||||
) {
|
||||
commands.spawn(PbrBundle {
|
||||
mesh: sphere_mesh.clone(),
|
||||
material: materials.add(StandardMaterial {
|
||||
clearcoat: 1.0,
|
||||
clearcoat_perceptual_roughness: 0.3,
|
||||
clearcoat_normal_texture: Some(asset_server.load_with_settings(
|
||||
"textures/ScratchedGold-Normal.png",
|
||||
|settings: &mut ImageLoaderSettings| settings.is_srgb = false,
|
||||
)),
|
||||
metallic: 0.9,
|
||||
perceptual_roughness: 0.1,
|
||||
base_color: GOLD.into(),
|
||||
..default()
|
||||
}),
|
||||
transform: Transform::from_xyz(0.0, 0.0, 0.0).with_scale(Vec3::splat(1.25)),
|
||||
..default()
|
||||
});
|
||||
}
|
||||
|
||||
/// Spawns a light.
|
||||
fn spawn_light(commands: &mut Commands) {
|
||||
commands.spawn(PointLightBundle {
|
||||
point_light: PointLight {
|
||||
color: WHITE.into(),
|
||||
intensity: 100000.0,
|
||||
..default()
|
||||
},
|
||||
..default()
|
||||
});
|
||||
}
|
||||
|
||||
/// Spawns a camera with associated skybox and environment map.
|
||||
fn spawn_camera(commands: &mut Commands, asset_server: &AssetServer) {
|
||||
commands
|
||||
.spawn(Camera3dBundle {
|
||||
camera: Camera {
|
||||
hdr: true,
|
||||
..default()
|
||||
},
|
||||
projection: Projection::Perspective(PerspectiveProjection {
|
||||
fov: 27.0 / 180.0 * PI,
|
||||
..default()
|
||||
}),
|
||||
transform: Transform::from_xyz(0.0, 0.0, 10.0),
|
||||
tonemapping: AcesFitted,
|
||||
..default()
|
||||
})
|
||||
.insert(Skybox {
|
||||
brightness: 5000.0,
|
||||
image: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"),
|
||||
..default()
|
||||
})
|
||||
.insert(EnvironmentMapLight {
|
||||
diffuse_map: asset_server.load("environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2"),
|
||||
specular_map: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"),
|
||||
intensity: 2000.0,
|
||||
..default()
|
||||
});
|
||||
}
|
|
@ -81,7 +81,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
|||
Skybox {
|
||||
image: skybox_handle.clone(),
|
||||
brightness: 1000.0,
|
||||
..Default::default()
|
||||
..default()
|
||||
},
|
||||
));
|
||||
|
||||
|
|
|
@ -242,11 +242,12 @@ fn spawn_camera(commands: &mut Commands, asset_server: &AssetServer) {
|
|||
diffuse_map: asset_server.load("environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2"),
|
||||
specular_map: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"),
|
||||
intensity: 5000.0,
|
||||
..default()
|
||||
})
|
||||
.insert(Skybox {
|
||||
image: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"),
|
||||
brightness: 5000.0,
|
||||
..Default::default()
|
||||
..default()
|
||||
})
|
||||
.insert(ScreenSpaceReflectionsBundle::default())
|
||||
.insert(Fxaa::default());
|
||||
|
|
|
@ -78,6 +78,7 @@ fn setup(
|
|||
diffuse_map: asset_server.load("environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2"),
|
||||
specular_map: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"),
|
||||
intensity: 2000.0,
|
||||
..default()
|
||||
},
|
||||
));
|
||||
|
||||
|
|
|
@ -360,6 +360,7 @@ fn setup(
|
|||
intensity: 25.0,
|
||||
diffuse_map: asset_server.load("environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2"),
|
||||
specular_map: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"),
|
||||
..default()
|
||||
},
|
||||
BloomSettings::default(),
|
||||
));
|
||||
|
|
|
@ -34,6 +34,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
|||
diffuse_map: asset_server.load("environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2"),
|
||||
specular_map: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"),
|
||||
intensity: 150.0,
|
||||
..default()
|
||||
},
|
||||
));
|
||||
|
||||
|
|
|
@ -152,6 +152,7 @@ fn setup(
|
|||
diffuse_map: asset_server.load("environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2"),
|
||||
specular_map: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"),
|
||||
intensity: 150.0,
|
||||
..default()
|
||||
});
|
||||
|
||||
// Create the text.
|
||||
|
|
|
@ -52,7 +52,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
|||
.insert(Skybox {
|
||||
image: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"),
|
||||
brightness: 1000.0,
|
||||
..Default::default()
|
||||
..default()
|
||||
})
|
||||
.insert(VolumetricFogSettings {
|
||||
// This value is explicitly set to 0 since we have no environment map light
|
||||
|
|
|
@ -157,6 +157,7 @@ Example | Description
|
|||
[Physically Based Rendering](../examples/3d/pbr.rs) | Demonstrates use of Physically Based Rendering (PBR) properties
|
||||
[Reflection Probes](../examples/3d/reflection_probes.rs) | Demonstrates reflection probes
|
||||
[Render to Texture](../examples/3d/render_to_texture.rs) | Shows how to render to a texture, useful for mirrors, UI, or exporting images
|
||||
[Rotate Environment Map](../examples/3d/rotate_environment_map.rs) | Demonstrates how to rotate the skybox and the environment map simultaneously
|
||||
[Screen Space Ambient Occlusion](../examples/3d/ssao.rs) | A scene showcasing screen space ambient occlusion
|
||||
[Screen Space Reflections](../examples/3d/ssr.rs) | Demonstrates screen space reflections with water ripples
|
||||
[Shadow Biases](../examples/3d/shadow_biases.rs) | Demonstrates how shadow biases affect shadows in a 3d scene
|
||||
|
|
|
@ -145,6 +145,7 @@ fn setup_scene_after_load(
|
|||
specular_map: asset_server
|
||||
.load("assets/environment_maps/pisa_specular_rgb9e5_zstd.ktx2"),
|
||||
intensity: 150.0,
|
||||
..default()
|
||||
},
|
||||
camera_controller,
|
||||
));
|
||||
|
|
Loading…
Reference in a new issue