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::utils
@group(1) @binding(0)
var texture: texture_2d<f32>;
@ -12,7 +13,7 @@ fn fragment(
#import bevy_sprite::mesh2d_vertex_output
) -> @location(0) vec4<f32> {
// 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;
// Sample each color channel with an arbitrary shift

View file

@ -1,4 +1,5 @@
#import bevy_pbr::mesh_view_bindings
#import bevy_pbr::utils
@group(1) @binding(0)
var texture: texture_2d<f32>;
@ -10,7 +11,7 @@ fn fragment(
@builtin(position) position: vec4<f32>,
#import bevy_pbr::mesh_vertex_output
) -> @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);
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 {
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);
// NOTE: Restricting cluster index to avoid undefined behavior when accessing uniform buffer
// 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_light.brightness,
cluster_factors: Vec4::new(
clusters.dimensions.x as f32 / extracted_view.width as f32,
clusters.dimensions.y as f32 / extracted_view.height as f32,
clusters.dimensions.x as f32 / extracted_view.viewport.z as f32,
clusters.dimensions.y as f32 / extracted_view.viewport.w as f32,
cluster_factors_zw.x,
cluster_factors_zw.y,
),
@ -1024,8 +1024,12 @@ pub fn prepare_lights(
),
},
ExtractedView {
width: point_light_shadow_map.size as u32,
height: point_light_shadow_map.size as u32,
viewport: UVec4::new(
0,
0,
point_light_shadow_map.size as u32,
point_light_shadow_map.size as u32,
),
transform: view_translation * *view_rotation,
projection: cube_face_projection,
},
@ -1076,8 +1080,12 @@ pub fn prepare_lights(
pass_name: format!("shadow pass spot light {}", light_index,),
},
ExtractedView {
width: directional_light_shadow_map.size as u32,
height: directional_light_shadow_map.size as u32,
viewport: UVec4::new(
0,
0,
directional_light_shadow_map.size as u32,
directional_light_shadow_map.size as u32,
),
transform: spot_view_transform,
projection: spot_projection,
},
@ -1156,8 +1164,12 @@ pub fn prepare_lights(
pass_name: format!("shadow pass directional light {}", i),
},
ExtractedView {
width: directional_light_shadow_map.size as u32,
height: directional_light_shadow_map.size as u32,
viewport: UVec4::new(
0,
0,
directional_light_shadow_map.size as u32,
directional_light_shadow_map.size as u32,
),
transform: GlobalTransform::from(view.inverse()),
projection,
},

View file

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

View file

@ -21,3 +21,11 @@ fn hsv2rgb(hue: f32, saturation: f32, value: f32) -> vec3<f32> {
fn random1D(s: f32) -> f32 {
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,
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::FromReflect;
use bevy_transform::components::GlobalTransform;
@ -418,7 +418,8 @@ pub fn extract_cameras(
if !camera.is_active {
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_target_size(),
) {
@ -437,8 +438,12 @@ pub fn extract_cameras(
ExtractedView {
projection: camera.projection_matrix(),
transform: *transform,
width: viewport_size.x,
height: viewport_size.y,
viewport: UVec4::new(
viewport_origin.x,
viewport_origin.y,
viewport_size.x,
viewport_size.y,
),
},
visible_entities.clone(),
));

View file

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

View file

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

View file

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

View file

@ -9,7 +9,7 @@ use crate::{prelude::UiCameraConfig, CalculatedClip, Node, UiColor, UiImage};
use bevy_app::prelude::*;
use bevy_asset::{load_internal_asset, AssetEvent, Assets, Handle, HandleUntyped};
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_render::{
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, .. })) {
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.physical_viewport_rect(),
camera.physical_viewport_size(),
) {
let mut projection = OrthographicProjection {
@ -257,8 +258,12 @@ pub fn extract_default_ui_camera_view<T: Component>(
0.0,
UI_CAMERA_FAR + UI_CAMERA_TRANSFORM_OFFSET,
),
width: physical_size.x,
height: physical_size.y,
viewport: UVec4::new(
physical_origin.x,
physical_origin.y,
physical_size.x,
physical_size.y,
),
})
.id();
commands.get_or_spawn(entity).insert_bundle((

View file

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