Skip to content

Commit

Permalink
Apply codebase changes in preparation for StandardMaterial transmis…
Browse files Browse the repository at this point in the history
…sion (#8704)

# Objective

- Make #8015 easier to review;

## Solution

- This commit contains changes not directly related to transmission
required by #8015, in easier-to-review, one-change-per-commit form.

---

## Changelog

### Fixed

- Clear motion vector prepass using `0.0` instead of `1.0`, to avoid TAA
artifacts on transparent objects against the background;

### Added

- The `E` mathematical constant is now available for use in shaders,
exposed under `bevy_pbr::utils`;
- A new `TAA` shader def is now available, for conditionally enabling
shader logic via `#ifdef` when TAA is enabled; (e.g. for jittering
texture samples)
- A new `FallbackImageZero` resource is introduced, for when a fallback
image filled with zeroes is required;
- A new `RenderPhase<I>::render_range()` method is introduced, for
render phases that need to render their items in multiple parceled out
“steps”;

### Changed

- The `MainTargetTextures` struct now holds both `Texture` and
`TextureViews` for the main textures;
- The fog shader functions under `bevy_pbr::fog` now take the a `Fog`
structure as their first argument, instead of relying on the global
`fog` uniform;
- The main textures can now be used as copy sources;

## Migration Guide

- `ViewTarget::main_texture()` and `ViewTarget::main_texture_other()`
now return `&Texture` instead of `&TextureView`. If you were relying on
these methods, replace your usage with
`ViewTarget::main_texture_view()`and
`ViewTarget::main_texture_other_view()`, respectively;
- `ViewTarget::sampled_main_texture()` now returns `Option<&Texture>`
instead of a `Option<&TextureView>`. If you were relying on this method,
replace your usage with `ViewTarget::sampled_main_texture_view()`;
- The `apply_fog()`, `linear_fog()`, `exponential_fog()`,
`exponential_squared_fog()` and `atmospheric_fog()` functions now take a
configurable `Fog` struct. If you were relying on them, update your
usage by adding the global `fog` uniform as their first argument;
  • Loading branch information
coreh authored May 30, 2023
1 parent c8deedb commit 292e069
Show file tree
Hide file tree
Showing 13 changed files with 207 additions and 91 deletions.
4 changes: 3 additions & 1 deletion crates/bevy_core_pipeline/src/bloom/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,9 @@ impl ViewNode for BloomNode {
BindGroupEntry {
binding: 0,
// Read from main texture directly
resource: BindingResource::TextureView(view_target.main_texture()),
resource: BindingResource::TextureView(
view_target.main_texture_view(),
),
},
BindGroupEntry {
binding: 1,
Expand Down
5 changes: 3 additions & 2 deletions crates/bevy_core_pipeline/src/prepass/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,10 @@ impl ViewNode for PrepassNode {
view: &view_motion_vectors_texture.default_view,
resolve_target: None,
ops: Operations {
// Blue channel doesn't matter, but set to 1.0 for possible faster clear
// Red and Green channels are X and Y components of the motion vectors
// Blue channel doesn't matter, but set to 0.0 for possible faster clear
// https://gpuopen.com/performance/#clears
load: LoadOp::Clear(Color::rgb_linear(1.0, 1.0, 1.0).into()),
load: LoadOp::Clear(Color::rgb_linear(0.0, 0.0, 0.0).into()),
store: true,
},
},
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_core_pipeline/src/upscaling/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ impl ViewNode for UpscalingNode {
LoadOp::Clear(Default::default())
};

let upscaled_texture = target.main_texture();
let upscaled_texture = target.main_texture_view();

let mut cached_bind_group = self.cached_texture_bind_group.lock().unwrap();
let bind_group = match &mut *cached_bind_group {
Expand Down
7 changes: 7 additions & 0 deletions crates/bevy_pbr/src/material.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use bevy_app::{App, Plugin};
use bevy_asset::{AddAsset, AssetEvent, AssetServer, Assets, Handle};
use bevy_core_pipeline::{
core_3d::{AlphaMask3d, Opaque3d, Transparent3d},
experimental::taa::TemporalAntiAliasSettings,
prepass::NormalPrepass,
tonemapping::{DebandDither, Tonemapping},
};
Expand Down Expand Up @@ -387,6 +388,7 @@ pub fn queue_material_meshes<M: Material>(
Option<&DebandDither>,
Option<&EnvironmentMapLight>,
Option<&NormalPrepass>,
Option<&TemporalAntiAliasSettings>,
&mut RenderPhase<Opaque3d>,
&mut RenderPhase<AlphaMask3d>,
&mut RenderPhase<Transparent3d>,
Expand All @@ -401,6 +403,7 @@ pub fn queue_material_meshes<M: Material>(
dither,
environment_map,
normal_prepass,
taa_settings,
mut opaque_phase,
mut alpha_mask_phase,
mut transparent_phase,
Expand All @@ -417,6 +420,10 @@ pub fn queue_material_meshes<M: Material>(
view_key |= MeshPipelineKey::NORMAL_PREPASS;
}

if taa_settings.is_some() {
view_key |= MeshPipelineKey::TAA;
}

let environment_map_loaded = match environment_map {
Some(environment_map) => environment_map.is_loaded(&images),
None => false,
Expand Down
35 changes: 20 additions & 15 deletions crates/bevy_pbr/src/render/fog.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -6,61 +6,66 @@
// https://iquilezles.org/articles/fog/ (Atmospheric Fog and Scattering)

fn scattering_adjusted_fog_color(
fog_params: Fog,
scattering: vec3<f32>,
) -> vec4<f32> {
if (fog.directional_light_color.a > 0.0) {
if (fog_params.directional_light_color.a > 0.0) {
return vec4<f32>(
fog.base_color.rgb
+ scattering * fog.directional_light_color.rgb * fog.directional_light_color.a,
fog.base_color.a,
fog_params.base_color.rgb
+ scattering * fog_params.directional_light_color.rgb * fog_params.directional_light_color.a,
fog_params.base_color.a,
);
} else {
return fog.base_color;
return fog_params.base_color;
}
}

fn linear_fog(
fog_params: Fog,
input_color: vec4<f32>,
distance: f32,
scattering: vec3<f32>,
) -> vec4<f32> {
var fog_color = scattering_adjusted_fog_color(scattering);
let start = fog.be.x;
let end = fog.be.y;
var fog_color = scattering_adjusted_fog_color(fog_params, scattering);
let start = fog_params.be.x;
let end = fog_params.be.y;
fog_color.a *= 1.0 - clamp((end - distance) / (end - start), 0.0, 1.0);
return vec4<f32>(mix(input_color.rgb, fog_color.rgb, fog_color.a), input_color.a);
}

fn exponential_fog(
fog_params: Fog,
input_color: vec4<f32>,
distance: f32,
scattering: vec3<f32>,
) -> vec4<f32> {
var fog_color = scattering_adjusted_fog_color(scattering);
let density = fog.be.x;
var fog_color = scattering_adjusted_fog_color(fog_params, scattering);
let density = fog_params.be.x;
fog_color.a *= 1.0 - 1.0 / exp(distance * density);
return vec4<f32>(mix(input_color.rgb, fog_color.rgb, fog_color.a), input_color.a);
}

fn exponential_squared_fog(
fog_params: Fog,
input_color: vec4<f32>,
distance: f32,
scattering: vec3<f32>,
) -> vec4<f32> {
var fog_color = scattering_adjusted_fog_color(scattering);
let distance_times_density = distance * fog.be.x;
var fog_color = scattering_adjusted_fog_color(fog_params, scattering);
let distance_times_density = distance * fog_params.be.x;
fog_color.a *= 1.0 - 1.0 / exp(distance_times_density * distance_times_density);
return vec4<f32>(mix(input_color.rgb, fog_color.rgb, fog_color.a), input_color.a);
}

fn atmospheric_fog(
fog_params: Fog,
input_color: vec4<f32>,
distance: f32,
scattering: vec3<f32>,
) -> vec4<f32> {
var fog_color = scattering_adjusted_fog_color(scattering);
let extinction_factor = 1.0 - 1.0 / exp(distance * fog.be);
let inscattering_factor = 1.0 - 1.0 / exp(distance * fog.bi);
var fog_color = scattering_adjusted_fog_color(fog_params, scattering);
let extinction_factor = 1.0 - 1.0 / exp(distance * fog_params.be);
let inscattering_factor = 1.0 - 1.0 / exp(distance * fog_params.bi);
return vec4<f32>(
input_color.rgb * (1.0 - extinction_factor * fog_color.a)
+ fog_color.rgb * inscattering_factor * fog_color.a,
Expand Down
5 changes: 5 additions & 0 deletions crates/bevy_pbr/src/render/mesh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -586,6 +586,7 @@ bitflags::bitflags! {
// See: https://www.khronos.org/opengl/wiki/Early_Fragment_Test
const ENVIRONMENT_MAP = (1 << 7);
const DEPTH_CLAMP_ORTHO = (1 << 8);
const TAA = (1 << 9);
const BLEND_RESERVED_BITS = Self::BLEND_MASK_BITS << Self::BLEND_SHIFT_BITS; // ← Bitmask reserving bits for the blend state
const BLEND_OPAQUE = (0 << Self::BLEND_SHIFT_BITS); // ← Values are just sequential within the mask, and can range from 0 to 3
const BLEND_PREMULTIPLIED_ALPHA = (1 << Self::BLEND_SHIFT_BITS); //
Expand Down Expand Up @@ -804,6 +805,10 @@ impl SpecializedMeshPipeline for MeshPipeline {
shader_defs.push("ENVIRONMENT_MAP".into());
}

if key.contains(MeshPipelineKey::TAA) {
shader_defs.push("TAA".into());
}

let format = if key.contains(MeshPipelineKey::HDR) {
ViewTarget::TEXTURE_FORMAT_HDR
} else {
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_pbr/src/render/pbr.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {

// fog
if (fog.mode != FOG_MODE_OFF && (material.flags & STANDARD_MATERIAL_FLAGS_FOG_ENABLED_BIT) != 0u) {
output_color = apply_fog(output_color, in.world_position.xyz, view.world_position.xyz);
output_color = apply_fog(fog, output_color, in.world_position.xyz, view.world_position.xyz);
}

#ifdef TONEMAP_IN_SHADER
Expand Down
22 changes: 11 additions & 11 deletions crates/bevy_pbr/src/render/pbr_functions.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ fn pbr(
#endif // PREPASS_FRAGMENT

#ifndef PREPASS_FRAGMENT
fn apply_fog(input_color: vec4<f32>, fragment_world_position: vec3<f32>, view_world_position: vec3<f32>) -> vec4<f32> {
fn apply_fog(fog_params: Fog, input_color: vec4<f32>, fragment_world_position: vec3<f32>, view_world_position: vec3<f32>) -> vec4<f32> {
let view_to_world = fragment_world_position.xyz - view_world_position.xyz;

// `length()` is used here instead of just `view_to_world.z` since that produces more
Expand All @@ -285,7 +285,7 @@ fn apply_fog(input_color: vec4<f32>, fragment_world_position: vec3<f32>, view_wo
let distance = length(view_to_world);

var scattering = vec3<f32>(0.0);
if fog.directional_light_color.a > 0.0 {
if fog_params.directional_light_color.a > 0.0 {
let view_to_world_normalized = view_to_world / distance;
let n_directional_lights = lights.n_directional_lights;
for (var i: u32 = 0u; i < n_directional_lights; i = i + 1u) {
Expand All @@ -295,19 +295,19 @@ fn apply_fog(input_color: vec4<f32>, fragment_world_position: vec3<f32>, view_wo
dot(view_to_world_normalized, light.direction_to_light),
0.0
),
fog.directional_light_exponent
fog_params.directional_light_exponent
) * light.color.rgb;
}
}

if fog.mode == FOG_MODE_LINEAR {
return linear_fog(input_color, distance, scattering);
} else if fog.mode == FOG_MODE_EXPONENTIAL {
return exponential_fog(input_color, distance, scattering);
} else if fog.mode == FOG_MODE_EXPONENTIAL_SQUARED {
return exponential_squared_fog(input_color, distance, scattering);
} else if fog.mode == FOG_MODE_ATMOSPHERIC {
return atmospheric_fog(input_color, distance, scattering);
if fog_params.mode == FOG_MODE_LINEAR {
return linear_fog(fog_params, input_color, distance, scattering);
} else if fog_params.mode == FOG_MODE_EXPONENTIAL {
return exponential_fog(fog_params, input_color, distance, scattering);
} else if fog_params.mode == FOG_MODE_EXPONENTIAL_SQUARED {
return exponential_squared_fog(fog_params, input_color, distance, scattering);
} else if fog_params.mode == FOG_MODE_ATMOSPHERIC {
return atmospheric_fog(fog_params, input_color, distance, scattering);
} else {
return input_color;
}
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_pbr/src/render/utils.wgsl
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#define_import_path bevy_pbr::utils

const PI: f32 = 3.141592653589793;
const E: f32 = 2.718281828459045;

fn hsv2rgb(hue: f32, saturation: f32, value: f32) -> vec3<f32> {
let rgb = clamp(
Expand Down
23 changes: 23 additions & 0 deletions crates/bevy_render/src/render_phase/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,29 @@ impl<I: PhaseItem> RenderPhase<I> {
draw_function.draw(world, render_pass, view, item);
}
}

/// Renders all [`PhaseItem`]s in the provided `range` (based on their index in `self.items`) using their corresponding draw functions.
pub fn render_range<'w>(
&self,
render_pass: &mut TrackedRenderPass<'w>,
world: &'w World,
view: Entity,
range: Range<usize>,
) {
let draw_functions = world.resource::<DrawFunctions<I>>();
let mut draw_functions = draw_functions.write();
draw_functions.prepare(world);

for item in self
.items
.get(range)
.expect("`Range` provided to `render_range()` is out of bounds")
.iter()
{
let draw_function = draw_functions.get_mut(item.draw_function()).unwrap();
draw_function.draw(world, render_pass, view, item);
}
}
}

impl<I: BatchedPhaseItem> RenderPhase<I> {
Expand Down
39 changes: 36 additions & 3 deletions crates/bevy_render/src/texture/fallback_image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,21 @@ use crate::{
/// A [`RenderApp`](crate::RenderApp) resource that contains the default "fallback image",
/// which can be used in situations where an image was not explicitly defined. The most common
/// use case is [`AsBindGroup`] implementations (such as materials) that support optional textures.
/// [`FallbackImage`] defaults to a 1x1 fully white texture, making blending colors with it a no-op.
///
/// Defaults to a 1x1 fully opaque white texture, (1.0, 1.0, 1.0, 1.0) which makes multiplying
/// it with other colors a no-op.
#[derive(Resource, Deref)]
pub struct FallbackImage(GpuImage);

/// A [`RenderApp`](crate::RenderApp) resource that contains a _zero-filled_ "fallback image",
/// which can be used in place of [`FallbackImage`], when a fully transparent or black fallback
/// is required instead of fully opaque white.
///
/// Defaults to a 1x1 fully transparent black texture, (0.0, 0.0, 0.0, 0.0) which makes adding
/// or alpha-blending it to other colors a no-op.
#[derive(Resource, Deref)]
pub struct FallbackImageZero(GpuImage);

/// A [`RenderApp`](crate::RenderApp) resource that contains a "cubemap fallback image",
/// which can be used in situations where an image was not explicitly defined. The most common
/// use case is [`AsBindGroup`] implementations (such as materials) that support optional textures.
Expand All @@ -34,9 +45,10 @@ fn fallback_image_new(
format: TextureFormat,
dimension: TextureViewDimension,
samples: u32,
value: u8,
) -> GpuImage {
// TODO make this configurable
let data = vec![255; format.pixel_size()];
// TODO make this configurable per channel
let data = vec![value; format.pixel_size()];

let extents = Extent3d {
width: 1,
Expand Down Expand Up @@ -92,6 +104,24 @@ impl FromWorld for FallbackImage {
TextureFormat::bevy_default(),
TextureViewDimension::D2,
1,
255,
))
}
}

impl FromWorld for FallbackImageZero {
fn from_world(world: &mut bevy_ecs::prelude::World) -> Self {
let render_device = world.resource::<RenderDevice>();
let render_queue = world.resource::<RenderQueue>();
let default_sampler = world.resource::<DefaultImageSampler>();
Self(fallback_image_new(
render_device,
render_queue,
default_sampler,
TextureFormat::bevy_default(),
TextureViewDimension::D2,
1,
0,
))
}
}
Expand All @@ -108,6 +138,7 @@ impl FromWorld for FallbackImageCubemap {
TextureFormat::bevy_default(),
TextureViewDimension::Cube,
1,
255,
))
}
}
Expand Down Expand Up @@ -148,6 +179,7 @@ impl<'w> FallbackImagesMsaa<'w> {
TextureFormat::bevy_default(),
TextureViewDimension::D2,
sample_count,
255,
)
})
}
Expand All @@ -171,6 +203,7 @@ impl<'w> FallbackImagesDepth<'w> {
TextureFormat::Depth32Float,
TextureViewDimension::D2,
sample_count,
255,
)
})
}
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_render/src/texture/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ impl Plugin for ImagePlugin {
render_app
.insert_resource(DefaultImageSampler(default_sampler))
.init_resource::<FallbackImage>()
.init_resource::<FallbackImageZero>()
.init_resource::<FallbackImageCubemap>()
.init_resource::<FallbackImageMsaaCache>()
.init_resource::<FallbackImageDepthCache>();
Expand Down
Loading

0 comments on commit 292e069

Please sign in to comment.