adjust cluster index for viewport origin (#5947)

# Objective

fixes #5946

## Solution

adjust cluster index calculation for viewport origin.

from reading point 2 of the rasterization algorithm description in https://gpuweb.github.io/gpuweb/#rasterization, it looks like framebuffer space (and so @bulitin(position)) is not meant to be adjusted for viewport origin, so we need to subtract that to get the right cluster index.

- add viewport origin to rust `ExtractedView` and wgsl `View` structs
- subtract from frag coord for cluster index calculation
This commit is contained in:
robtfm 2022-09-15 21:58:14 +00:00
parent deeab3fc90
commit 503c2a9677
12 changed files with 65 additions and 34 deletions

View File

@ -1,4 +1,5 @@
#import bevy_pbr::mesh_view_bindings #import bevy_pbr::mesh_view_bindings
#import bevy_pbr::utils
@group(1) @binding(0) @group(1) @binding(0)
var texture: texture_2d<f32>; var texture: texture_2d<f32>;
@ -12,7 +13,7 @@ fn fragment(
#import bevy_sprite::mesh2d_vertex_output #import bevy_sprite::mesh2d_vertex_output
) -> @location(0) vec4<f32> { ) -> @location(0) vec4<f32> {
// Get screen position with coordinates from 0 to 1 // Get screen position with coordinates from 0 to 1
let uv = position.xy / vec2<f32>(view.width, view.height); let uv = coords_to_viewport_uv(position.xy, view.viewport);
let offset_strength = 0.02; let offset_strength = 0.02;
// Sample each color channel with an arbitrary shift // Sample each color channel with an arbitrary shift

View File

@ -1,4 +1,5 @@
#import bevy_pbr::mesh_view_bindings #import bevy_pbr::mesh_view_bindings
#import bevy_pbr::utils
@group(1) @binding(0) @group(1) @binding(0)
var texture: texture_2d<f32>; var texture: texture_2d<f32>;
@ -10,7 +11,7 @@ fn fragment(
@builtin(position) position: vec4<f32>, @builtin(position) position: vec4<f32>,
#import bevy_pbr::mesh_vertex_output #import bevy_pbr::mesh_vertex_output
) -> @location(0) vec4<f32> { ) -> @location(0) vec4<f32> {
let uv = position.xy / vec2<f32>(view.width, view.height); let uv = coords_to_viewport_uv(position.xy, view.viewport);
let color = textureSample(texture, texture_sampler, uv); let color = textureSample(texture, texture_sampler, uv);
return color; return color;
} }

View File

@ -16,7 +16,7 @@ fn view_z_to_z_slice(view_z: f32, is_orthographic: bool) -> u32 {
} }
fn fragment_cluster_index(frag_coord: vec2<f32>, view_z: f32, is_orthographic: bool) -> u32 { fn fragment_cluster_index(frag_coord: vec2<f32>, view_z: f32, is_orthographic: bool) -> u32 {
let xy = vec2<u32>(floor(frag_coord * lights.cluster_factors.xy)); let xy = vec2<u32>(floor((frag_coord - view.viewport.xy) * lights.cluster_factors.xy));
let z_slice = view_z_to_z_slice(view_z, is_orthographic); let z_slice = view_z_to_z_slice(view_z, is_orthographic);
// NOTE: Restricting cluster index to avoid undefined behavior when accessing uniform buffer // NOTE: Restricting cluster index to avoid undefined behavior when accessing uniform buffer
// arrays based on the cluster index. // arrays based on the cluster index.

View File

@ -967,8 +967,8 @@ pub fn prepare_lights(
ambient_color: Vec4::from_slice(&ambient_light.color.as_linear_rgba_f32()) ambient_color: Vec4::from_slice(&ambient_light.color.as_linear_rgba_f32())
* ambient_light.brightness, * ambient_light.brightness,
cluster_factors: Vec4::new( cluster_factors: Vec4::new(
clusters.dimensions.x as f32 / extracted_view.width as f32, clusters.dimensions.x as f32 / extracted_view.viewport.z as f32,
clusters.dimensions.y as f32 / extracted_view.height as f32, clusters.dimensions.y as f32 / extracted_view.viewport.w as f32,
cluster_factors_zw.x, cluster_factors_zw.x,
cluster_factors_zw.y, cluster_factors_zw.y,
), ),
@ -1024,8 +1024,12 @@ pub fn prepare_lights(
), ),
}, },
ExtractedView { ExtractedView {
width: point_light_shadow_map.size as u32, viewport: UVec4::new(
height: point_light_shadow_map.size as u32, 0,
0,
point_light_shadow_map.size as u32,
point_light_shadow_map.size as u32,
),
transform: view_translation * *view_rotation, transform: view_translation * *view_rotation,
projection: cube_face_projection, projection: cube_face_projection,
}, },
@ -1076,8 +1080,12 @@ pub fn prepare_lights(
pass_name: format!("shadow pass spot light {}", light_index,), pass_name: format!("shadow pass spot light {}", light_index,),
}, },
ExtractedView { ExtractedView {
width: directional_light_shadow_map.size as u32, viewport: UVec4::new(
height: directional_light_shadow_map.size as u32, 0,
0,
directional_light_shadow_map.size as u32,
directional_light_shadow_map.size as u32,
),
transform: spot_view_transform, transform: spot_view_transform,
projection: spot_projection, projection: spot_projection,
}, },
@ -1156,8 +1164,12 @@ pub fn prepare_lights(
pass_name: format!("shadow pass directional light {}", i), pass_name: format!("shadow pass directional light {}", i),
}, },
ExtractedView { ExtractedView {
width: directional_light_shadow_map.size as u32, viewport: UVec4::new(
height: directional_light_shadow_map.size as u32, 0,
0,
directional_light_shadow_map.size as u32,
directional_light_shadow_map.size as u32,
),
transform: GlobalTransform::from(view.inverse()), transform: GlobalTransform::from(view.inverse()),
projection, projection,
}, },

View File

@ -8,8 +8,8 @@ struct View {
projection: mat4x4<f32>, projection: mat4x4<f32>,
inverse_projection: mat4x4<f32>, inverse_projection: mat4x4<f32>,
world_position: vec3<f32>, world_position: vec3<f32>,
width: f32, // viewport(x_origin, y_origin, width, height)
height: f32, viewport: vec4<f32>,
}; };
struct PointLight { struct PointLight {

View File

@ -21,3 +21,11 @@ fn hsv2rgb(hue: f32, saturation: f32, value: f32) -> vec3<f32> {
fn random1D(s: f32) -> f32 { fn random1D(s: f32) -> f32 {
return fract(sin(s * 12.9898) * 43758.5453123); return fract(sin(s * 12.9898) * 43758.5453123);
} }
// returns the (0-1, 0-1) position within the given viewport for the current buffer coords .
// buffer coords can be obtained from `@builtin(position).xy`.
// the view uniform struct contains the current camera viewport in `view.viewport`.
// topleft = 0,0
fn coords_to_viewport_uv(position: vec2<f32>, viewport: vec4<f32>) -> vec2<f32> {
return (position - viewport.xy) / viewport.zw;
}

View File

@ -17,7 +17,7 @@ use bevy_ecs::{
reflect::ReflectComponent, reflect::ReflectComponent,
system::{Commands, ParamSet, Query, Res}, system::{Commands, ParamSet, Query, Res},
}; };
use bevy_math::{Mat4, UVec2, Vec2, Vec3}; use bevy_math::{Mat4, UVec2, UVec4, Vec2, Vec3};
use bevy_reflect::prelude::*; use bevy_reflect::prelude::*;
use bevy_reflect::FromReflect; use bevy_reflect::FromReflect;
use bevy_transform::components::GlobalTransform; use bevy_transform::components::GlobalTransform;
@ -418,7 +418,8 @@ pub fn extract_cameras(
if !camera.is_active { if !camera.is_active {
continue; continue;
} }
if let (Some(viewport_size), Some(target_size)) = ( if let (Some((viewport_origin, _)), Some(viewport_size), Some(target_size)) = (
camera.physical_viewport_rect(),
camera.physical_viewport_size(), camera.physical_viewport_size(),
camera.physical_target_size(), camera.physical_target_size(),
) { ) {
@ -437,8 +438,12 @@ pub fn extract_cameras(
ExtractedView { ExtractedView {
projection: camera.projection_matrix(), projection: camera.projection_matrix(),
transform: *transform, transform: *transform,
width: viewport_size.x, viewport: UVec4::new(
height: viewport_size.y, viewport_origin.x,
viewport_origin.y,
viewport_size.x,
viewport_size.y,
),
}, },
visible_entities.clone(), visible_entities.clone(),
)); ));

View File

@ -21,7 +21,7 @@ use crate::{
}; };
use bevy_app::{App, Plugin}; use bevy_app::{App, Plugin};
use bevy_ecs::prelude::*; use bevy_ecs::prelude::*;
use bevy_math::{Mat4, Vec3}; use bevy_math::{Mat4, UVec4, Vec3, Vec4};
use bevy_reflect::Reflect; use bevy_reflect::Reflect;
use bevy_transform::components::GlobalTransform; use bevy_transform::components::GlobalTransform;
use bevy_utils::HashMap; use bevy_utils::HashMap;
@ -81,8 +81,8 @@ impl Default for Msaa {
pub struct ExtractedView { pub struct ExtractedView {
pub projection: Mat4, pub projection: Mat4,
pub transform: GlobalTransform, pub transform: GlobalTransform,
pub width: u32, // uvec4(origin.x, origin.y, width, height)
pub height: u32, pub viewport: UVec4,
} }
impl ExtractedView { impl ExtractedView {
@ -101,8 +101,8 @@ pub struct ViewUniform {
projection: Mat4, projection: Mat4,
inverse_projection: Mat4, inverse_projection: Mat4,
world_position: Vec3, world_position: Vec3,
width: f32, // viewport(x_origin, y_origin, width, height)
height: f32, viewport: Vec4,
} }
#[derive(Resource, Default)] #[derive(Resource, Default)]
@ -163,8 +163,7 @@ fn prepare_view_uniforms(
projection, projection,
inverse_projection, inverse_projection,
world_position: camera.transform.translation(), world_position: camera.transform.translation(),
width: camera.width as f32, viewport: camera.viewport.as_vec4(),
height: camera.height as f32,
}), }),
}; };

View File

@ -8,6 +8,6 @@ struct View {
projection: mat4x4<f32>, projection: mat4x4<f32>,
inverse_projection: mat4x4<f32>, inverse_projection: mat4x4<f32>,
world_position: vec3<f32>, world_position: vec3<f32>,
width: f32, // viewport(x_origin, y_origin, width, height)
height: f32, viewport: vec4<f32>,
}; };

View File

@ -6,8 +6,8 @@ struct View {
projection: mat4x4<f32>, projection: mat4x4<f32>,
inverse_projection: mat4x4<f32>, inverse_projection: mat4x4<f32>,
world_position: vec3<f32>, world_position: vec3<f32>,
width: f32, // viewport(x_origin, y_origin, width, height)
height: f32, viewport: vec4<f32>,
}; };
@group(0) @binding(0) @group(0) @binding(0)
var<uniform> view: View; var<uniform> view: View;

View File

@ -9,7 +9,7 @@ use crate::{prelude::UiCameraConfig, CalculatedClip, Node, UiColor, UiImage};
use bevy_app::prelude::*; use bevy_app::prelude::*;
use bevy_asset::{load_internal_asset, AssetEvent, Assets, Handle, HandleUntyped}; use bevy_asset::{load_internal_asset, AssetEvent, Assets, Handle, HandleUntyped};
use bevy_ecs::prelude::*; use bevy_ecs::prelude::*;
use bevy_math::{Mat4, Rect, Vec2, Vec3, Vec4Swizzles}; use bevy_math::{Mat4, Rect, UVec4, Vec2, Vec3, Vec4Swizzles};
use bevy_reflect::TypeUuid; use bevy_reflect::TypeUuid;
use bevy_render::{ use bevy_render::{
camera::{Camera, CameraProjection, OrthographicProjection, WindowOrigin}, camera::{Camera, CameraProjection, OrthographicProjection, WindowOrigin},
@ -238,8 +238,9 @@ pub fn extract_default_ui_camera_view<T: Component>(
if matches!(camera_ui, Some(&UiCameraConfig { show_ui: false, .. })) { if matches!(camera_ui, Some(&UiCameraConfig { show_ui: false, .. })) {
continue; continue;
} }
if let (Some(logical_size), Some(physical_size)) = ( if let (Some(logical_size), Some((physical_origin, _)), Some(physical_size)) = (
camera.logical_viewport_size(), camera.logical_viewport_size(),
camera.physical_viewport_rect(),
camera.physical_viewport_size(), camera.physical_viewport_size(),
) { ) {
let mut projection = OrthographicProjection { let mut projection = OrthographicProjection {
@ -257,8 +258,12 @@ pub fn extract_default_ui_camera_view<T: Component>(
0.0, 0.0,
UI_CAMERA_FAR + UI_CAMERA_TRANSFORM_OFFSET, UI_CAMERA_FAR + UI_CAMERA_TRANSFORM_OFFSET,
), ),
width: physical_size.x, viewport: UVec4::new(
height: physical_size.y, physical_origin.x,
physical_origin.y,
physical_size.x,
physical_size.y,
),
}) })
.id(); .id();
commands.get_or_spawn(entity).insert_bundle(( commands.get_or_spawn(entity).insert_bundle((

View File

@ -6,8 +6,8 @@ struct View {
projection: mat4x4<f32>, projection: mat4x4<f32>,
inverse_projection: mat4x4<f32>, inverse_projection: mat4x4<f32>,
world_position: vec3<f32>, world_position: vec3<f32>,
width: f32, // viewport(x_origin, y_origin, width, height)
height: f32, viewport: vec4<f32>,
}; };
@group(0) @binding(0) @group(0) @binding(0)
var<uniform> view: View; var<uniform> view: View;