Tiled Forward Rendering #5
|
@ -182,6 +182,7 @@ async fn main() {
|
||||||
color: Vec3::new(1.0, 0.0, 0.0),
|
color: Vec3::new(1.0, 0.0, 0.0),
|
||||||
intensity: 1.0,
|
intensity: 1.0,
|
||||||
range: 1.5,
|
range: 1.5,
|
||||||
|
//cutoff: math::Angle::Degrees(45.0),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Transform::new(
|
Transform::new(
|
||||||
|
|
|
@ -44,6 +44,7 @@ pub struct CameraUniform {
|
||||||
pub inverse_projection: glam::Mat4,
|
pub inverse_projection: glam::Mat4,
|
||||||
/// The view projection matrix
|
/// The view projection matrix
|
||||||
pub view_projection: glam::Mat4,
|
pub view_projection: glam::Mat4,
|
||||||
|
pub projection: glam::Mat4,
|
||||||
/// The position of the camera
|
/// The position of the camera
|
||||||
pub position: glam::Vec3,
|
pub position: glam::Vec3,
|
||||||
pub tile_debug: u32,
|
pub tile_debug: u32,
|
||||||
|
@ -57,6 +58,7 @@ impl Default for CameraUniform {
|
||||||
view: glam::Mat4::IDENTITY,
|
view: glam::Mat4::IDENTITY,
|
||||||
inverse_projection: glam::Mat4::IDENTITY,
|
inverse_projection: glam::Mat4::IDENTITY,
|
||||||
view_projection: glam::Mat4::IDENTITY,
|
view_projection: glam::Mat4::IDENTITY,
|
||||||
|
projection: glam::Mat4::IDENTITY,
|
||||||
position: Default::default(),
|
position: Default::default(),
|
||||||
tile_debug: 0,
|
tile_debug: 0,
|
||||||
//_padding: 0,
|
//_padding: 0,
|
||||||
|
@ -65,11 +67,12 @@ impl Default for CameraUniform {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CameraUniform {
|
impl CameraUniform {
|
||||||
pub fn new(view: glam::Mat4, inverse_projection: glam::Mat4, view_projection: glam::Mat4, position: glam::Vec3) -> Self {
|
pub fn new(view: glam::Mat4, inverse_projection: glam::Mat4, view_projection: glam::Mat4, projection: glam::Mat4, position: glam::Vec3) -> Self {
|
||||||
Self {
|
Self {
|
||||||
view,
|
view,
|
||||||
inverse_projection,
|
inverse_projection,
|
||||||
view_projection,
|
view_projection,
|
||||||
|
projection,
|
||||||
position,
|
position,
|
||||||
tile_debug: 0
|
tile_debug: 0
|
||||||
}
|
}
|
||||||
|
@ -129,6 +132,7 @@ impl RenderCamera {
|
||||||
view,
|
view,
|
||||||
inverse_projection: proj.inverse(),
|
inverse_projection: proj.inverse(),
|
||||||
view_projection: self.view_proj,
|
view_projection: self.view_proj,
|
||||||
|
projection: proj,
|
||||||
position,
|
position,
|
||||||
tile_debug: camera.debug as u32,
|
tile_debug: camera.debug as u32,
|
||||||
}
|
}
|
||||||
|
@ -151,6 +155,7 @@ impl RenderCamera {
|
||||||
view,
|
view,
|
||||||
inverse_projection: proj.inverse(),
|
inverse_projection: proj.inverse(),
|
||||||
view_projection: self.view_proj,
|
view_projection: self.view_proj,
|
||||||
|
projection: proj,
|
||||||
position,
|
position,
|
||||||
tile_debug: camera.debug as u32,
|
tile_debug: camera.debug as u32,
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@ const LIGHT_TY_DIRECTIONAL = 0u;
|
||||||
const LIGHT_TY_POINT = 1u;
|
const LIGHT_TY_POINT = 1u;
|
||||||
const LIGHT_TY_SPOT = 2u;
|
const LIGHT_TY_SPOT = 2u;
|
||||||
|
|
||||||
|
const ALPHA_CUTOFF = 0.1;
|
||||||
|
|
||||||
struct VertexInput {
|
struct VertexInput {
|
||||||
@location(0) position: vec3<f32>,
|
@location(0) position: vec3<f32>,
|
||||||
@location(1) tex_coords: vec2<f32>,
|
@location(1) tex_coords: vec2<f32>,
|
||||||
|
@ -23,6 +25,7 @@ struct CameraUniform {
|
||||||
view: mat4x4<f32>,
|
view: mat4x4<f32>,
|
||||||
inverse_projection: mat4x4<f32>,
|
inverse_projection: mat4x4<f32>,
|
||||||
view_projection: mat4x4<f32>,
|
view_projection: mat4x4<f32>,
|
||||||
|
projection: mat4x4<f32>,
|
||||||
position: vec3<f32>,
|
position: vec3<f32>,
|
||||||
tile_debug: u32,
|
tile_debug: u32,
|
||||||
};
|
};
|
||||||
|
@ -132,13 +135,17 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||||
return debug_grid(in);
|
return debug_grid(in);
|
||||||
}
|
}
|
||||||
|
|
||||||
let tile_index = vec2<u32>(floor(in.clip_position.xy / 16.0));
|
|
||||||
let tile: vec2<u32> = textureLoad(t_light_grid, tile_index).xy;
|
|
||||||
|
|
||||||
let object_color: vec4<f32> = textureSample(t_diffuse, s_diffuse, in.tex_coords);
|
let object_color: vec4<f32> = textureSample(t_diffuse, s_diffuse, in.tex_coords);
|
||||||
let specular_color: vec3<f32> = textureSample(t_specular, s_specular, in.tex_coords).xyz;
|
let specular_color: vec3<f32> = textureSample(t_specular, s_specular, in.tex_coords).xyz;
|
||||||
var light_res = vec3<f32>(0.0);
|
var light_res = vec3<f32>(0.0);
|
||||||
|
|
||||||
|
if (object_color.a < ALPHA_CUTOFF) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
|
||||||
|
let tile_index = vec2<u32>(floor(in.clip_position.xy / 16.0));
|
||||||
|
let tile: vec2<u32> = textureLoad(t_light_grid, tile_index).xy;
|
||||||
|
|
||||||
let light_offset = tile.x;
|
let light_offset = tile.x;
|
||||||
let light_count = tile.y;
|
let light_count = tile.y;
|
||||||
|
|
||||||
|
@ -269,20 +276,19 @@ fn blinn_phong_spot_light(world_pos: vec3<f32>, world_norm: vec3<f32>, spot_ligh
|
||||||
//// end of specular ////
|
//// end of specular ////
|
||||||
|
|
||||||
//// spot light soft edges ////
|
//// spot light soft edges ////
|
||||||
let theta = dot(light_dir, normalize(-spot_light.direction));
|
let min_cos = cos(spot_light.spot_cutoff);
|
||||||
let epsilon = spot_light.spot_cutoff - spot_light.spot_outer_cutoff;
|
let max_cos = lerp(min_cos, 1.0, 0.5);
|
||||||
let intensity = clamp((theta - spot_light.spot_outer_cutoff) / epsilon, 0.0, 1.0);
|
let cos_angle = dot(spot_light.direction, -light_dir);
|
||||||
//diffuse_color *= intensity;
|
let cone = smoothstep(min_cos, max_cos, cos_angle);
|
||||||
//specular_color *= intensity;
|
|
||||||
//// end of spot light soft edges ////
|
//// end of spot light soft edges ////
|
||||||
|
|
||||||
//// spot light attenuation ////
|
//// spot light attenuation ////
|
||||||
let distance = length(light_pos - world_pos);
|
let distance = length(light_pos - world_pos);
|
||||||
let attenuation = calc_attenuation(spot_light, distance);
|
let attenuation = calc_attenuation(spot_light, distance);
|
||||||
|
|
||||||
ambient_color *= attenuation * intensity;
|
ambient_color *= attenuation * spot_light.intensity * cone;
|
||||||
diffuse_color *= attenuation * intensity;
|
diffuse_color *= attenuation * spot_light.intensity * cone;
|
||||||
specular_color *= attenuation * intensity;
|
specular_color *= attenuation * spot_light.intensity * cone;
|
||||||
//// end of spot light attenuation ////
|
//// end of spot light attenuation ////
|
||||||
|
|
||||||
|
|
||||||
|
@ -291,4 +297,8 @@ fn blinn_phong_spot_light(world_pos: vec3<f32>, world_norm: vec3<f32>, spot_ligh
|
||||||
|
|
||||||
fn calc_attenuation(light: Light, distance: f32) -> f32 {
|
fn calc_attenuation(light: Light, distance: f32) -> f32 {
|
||||||
return 1.0 - smoothstep(light.range * light.smoothness, light.range, distance);
|
return 1.0 - smoothstep(light.range * light.smoothness, light.range, distance);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lerp(start: f32, end: f32, alpha: f32) -> f32 {
|
||||||
|
return (start + (end - start) * alpha);
|
||||||
}
|
}
|
|
@ -5,20 +5,25 @@ const LIGHT_TY_DIRECTIONAL = 0u;
|
||||||
const LIGHT_TY_POINT = 1u;
|
const LIGHT_TY_POINT = 1u;
|
||||||
const LIGHT_TY_SPOT = 2u;
|
const LIGHT_TY_SPOT = 2u;
|
||||||
|
|
||||||
|
type vec2f = vec2<f32>;
|
||||||
|
type vec3f = vec3<f32>;
|
||||||
|
type vec4f = vec4<f32>;
|
||||||
|
|
||||||
struct CameraUniform {
|
struct CameraUniform {
|
||||||
view: mat4x4<f32>,
|
view: mat4x4<f32>,
|
||||||
inverse_projection: mat4x4<f32>,
|
inverse_projection: mat4x4<f32>,
|
||||||
//projection: mat4x4<f32>,
|
|
||||||
view_projection: mat4x4<f32>,
|
view_projection: mat4x4<f32>,
|
||||||
position: vec3<f32>,
|
projection: mat4x4<f32>,
|
||||||
|
position: vec3f,
|
||||||
|
tile_debug: u32,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Light {
|
struct Light {
|
||||||
position: vec3<f32>,
|
position: vec3f,
|
||||||
light_ty: u32,
|
light_ty: u32,
|
||||||
direction: vec3<f32>,
|
direction: vec3f,
|
||||||
enabled: u32,
|
enabled: u32,
|
||||||
color: vec3<f32>,
|
color: vec3f,
|
||||||
|
|
||||||
range: f32,
|
range: f32,
|
||||||
intensity: f32,
|
intensity: f32,
|
||||||
|
@ -34,16 +39,21 @@ struct Lights {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Cone {
|
struct Cone {
|
||||||
tip: vec3<f32>,
|
tip: vec3f,
|
||||||
height: f32,
|
height: f32,
|
||||||
direction: vec3<f32>,
|
direction: vec3f,
|
||||||
radius: f32,
|
radius: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Plane {
|
||||||
|
normal: vec3f,
|
||||||
|
origin_distance: f32,
|
||||||
|
}
|
||||||
|
|
||||||
var<workgroup> wg_min_depth: atomic<u32>;
|
var<workgroup> wg_min_depth: atomic<u32>;
|
||||||
var<workgroup> wg_max_depth: atomic<u32>;
|
var<workgroup> wg_max_depth: atomic<u32>;
|
||||||
var<workgroup> wg_light_index_start: atomic<u32>;
|
var<workgroup> wg_light_index_start: atomic<u32>;
|
||||||
var<workgroup> wg_frustum_planes: array<vec4<f32>, 6>;
|
var<workgroup> wg_frustum_planes: array<Plane, 6>;
|
||||||
|
|
||||||
// index list of visible light sources for this tile
|
// index list of visible light sources for this tile
|
||||||
var<workgroup> wg_visible_light_indices: array<u32, MAX_TILE_VISIBLE_LIGHTS>;
|
var<workgroup> wg_visible_light_indices: array<u32, MAX_TILE_VISIBLE_LIGHTS>;
|
||||||
|
@ -107,41 +117,50 @@ fn cs_main(
|
||||||
|
|
||||||
// Create the frustum planes that will be used for this time
|
// Create the frustum planes that will be used for this time
|
||||||
if (local_invocation_index == 0u) {
|
if (local_invocation_index == 0u) {
|
||||||
// Compute the 4 corner points on the far clipping plane to use as the frustum vertices.
|
// this algorithm is adapted from Google's filament:
|
||||||
var screen_space: array<vec4<f32>, 4>;
|
// https://github.com/google/filament/blob/3644e7f80827f1cd2caef4a21e410a2243eb6e84/filament/src/Froxelizer.cpp#L402C57-L402C73
|
||||||
|
let tile_width_clip_space = f32(2u * BLOCK_SIZE) / f32(u_screen_size.x);
|
||||||
|
let tile_height_clip_space = f32(2u * BLOCK_SIZE) / f32(u_screen_size.y);
|
||||||
|
|
||||||
// top left point
|
let tr_projection = transpose(u_camera.projection);
|
||||||
var temp: vec2<u32> = workgroup_id.xy * BLOCK_SIZE;
|
|
||||||
screen_space[0] = vec4<f32>(f32(temp.x), f32(temp.y), -1.0, 1.0);
|
|
||||||
|
|
||||||
// top right point
|
var planes: array<vec4f, 4>;
|
||||||
var temp2 = vec2<f32>(f32(workgroup_id.x) + 1.0, f32(workgroup_id.y)) * f32(BLOCK_SIZE);
|
|
||||||
screen_space[1] = vec4<f32>(temp2.x, temp2.y, -1.0, 1.0);
|
|
||||||
|
|
||||||
// bottom left point
|
|
||||||
temp2 = vec2<f32>(f32(workgroup_id.x), f32(workgroup_id.y) + 1.0) * f32(BLOCK_SIZE);
|
|
||||||
screen_space[2] = vec4<f32>(temp2.x, temp2.y, -1.0, 1.0);
|
|
||||||
|
|
||||||
// bottom right point
|
|
||||||
temp2 = vec2<f32>(f32(workgroup_id.x) + 1.0, f32(workgroup_id.y) + 1.0) * f32(BLOCK_SIZE);
|
|
||||||
screen_space[3] = vec4<f32>(temp2.x, temp2.y, -1.0, 1.0);
|
|
||||||
|
|
||||||
// convert screenspace to view space
|
// left plane
|
||||||
var view_space: array<vec3<f32>, 4>;
|
{
|
||||||
for (var i = 0u; i < 4u; i++) {
|
let x = (f32(workgroup_id.x) * tile_width_clip_space) - 1.0;
|
||||||
view_space[i] = screen_to_view(screen_space[i]).xyz;
|
let p = tr_projection * vec4f(-1.0, 0.0, 0.0, x);
|
||||||
|
planes[0] = -vec4f(normalize(p.xyz), 0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// View space eye is always at the origin
|
// right plane
|
||||||
let eye_pos = vec3<f32>(0.0, 0.0, 0.0);
|
{
|
||||||
|
let x = (f32(workgroup_id.x + 1u) * tile_width_clip_space) - 1.0;
|
||||||
|
let p = tr_projection * vec4f(-1.0, 0.0, 0.0, x);
|
||||||
|
planes[1] = vec4f(normalize(p.xyz), 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
wg_frustum_planes[0] = compute_plane(eye_pos, view_space[2], view_space[0]); // left plane
|
// top plane
|
||||||
wg_frustum_planes[1] = compute_plane(eye_pos, view_space[1], view_space[3]); // right plane
|
{
|
||||||
wg_frustum_planes[2] = compute_plane(eye_pos, view_space[0], view_space[1]); // top plane
|
let y = (f32(workgroup_id.y) * tile_height_clip_space) - 1.0;
|
||||||
wg_frustum_planes[3] = compute_plane(eye_pos, view_space[3], view_space[2]); // bottom plane
|
let p = tr_projection * vec4f(0.0, 1.0, 0.0, y);
|
||||||
|
planes[2] = -vec4f(normalize(p.xyz), 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
wg_frustum_planes[4] = vec4<f32>(0.0, 0.0, -1.0, -min_depth);
|
// bottom plane
|
||||||
wg_frustum_planes[5] = vec4<f32>(0.0, 0.0, 1.0, -max_depth);
|
{
|
||||||
|
let y = (f32(workgroup_id.y + 1u) * tile_height_clip_space) - 1.0;
|
||||||
|
let p = tr_projection * vec4f(0.0, 1.0, 0.0, y);
|
||||||
|
planes[3] = vec4f(normalize(p.xyz), 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
wg_frustum_planes[0] = Plane(planes[0].xyz, planes[0].w);
|
||||||
|
wg_frustum_planes[1] = Plane(planes[1].xyz, planes[1].w);
|
||||||
|
wg_frustum_planes[2] = Plane(planes[2].xyz, planes[2].w);
|
||||||
|
wg_frustum_planes[3] = Plane(planes[3].xyz, planes[3].w);
|
||||||
|
|
||||||
|
wg_frustum_planes[4] = Plane(vec3f(0.0, 0.0, -1.0), -min_depth);
|
||||||
|
wg_frustum_planes[5] = Plane(vec3f(0.0, 0.0, 1.0), -max_depth);
|
||||||
}
|
}
|
||||||
|
|
||||||
workgroupBarrier();
|
workgroupBarrier();
|
||||||
|
@ -157,29 +176,27 @@ fn cs_main(
|
||||||
let light = u_lights.data[light_index];
|
let light = u_lights.data[light_index];
|
||||||
|
|
||||||
if (light.enabled == 1u) {
|
if (light.enabled == 1u) {
|
||||||
let position_vec4 = u_camera.view * vec4<f32>(light.position, 1.0);
|
let position_vs = (u_camera.view * vec4f(light.position, 1.0)).xyz;
|
||||||
let position = position_vec4.xyz;
|
|
||||||
let radius = light.range;
|
|
||||||
|
|
||||||
if (light.light_ty == LIGHT_TY_DIRECTIONAL) {
|
if (light.light_ty == LIGHT_TY_DIRECTIONAL) {
|
||||||
add_light(light_index);
|
add_light(light_index);
|
||||||
} else if (light.light_ty == LIGHT_TY_POINT
|
} else if (light.light_ty == LIGHT_TY_POINT
|
||||||
&& sphere_inside_frustrum(wg_frustum_planes, position, radius)) {
|
&& sphere_inside_frustrum(wg_frustum_planes, position_vs, light.range)) {
|
||||||
// TODO: add the light to the transparent geometry list
|
// TODO: add the light to the transparent geometry list
|
||||||
|
|
||||||
if (!sphere_inside_plane(position, radius, wg_frustum_planes[4])) {
|
if (!sphere_inside_plane(position_vs, light.range, wg_frustum_planes[4])) {
|
||||||
add_light(light_index);
|
add_light(light_index);
|
||||||
}
|
}
|
||||||
} else if (light.light_ty == LIGHT_TY_SPOT) {
|
} else if (light.light_ty == LIGHT_TY_SPOT) {
|
||||||
let dir_vs = u_camera.view * vec4<f32>(light.direction, 1.0);
|
let dir_vs = (u_camera.view * vec4f(light.direction, 1.0)).xyz;
|
||||||
let cone_radius = tan(light.spot_cutoff) * light.range;
|
let cone_radius = tan(light.spot_cutoff) * light.range;
|
||||||
let cone = Cone(position, radius, dir_vs.xyz, cone_radius);
|
let cone = Cone(position_vs, light.range, dir_vs, cone_radius);
|
||||||
|
|
||||||
if (cone_inside_frustum(cone, wg_frustum_planes)) {
|
if (cone_inside_frustum(cone, wg_frustum_planes)) {
|
||||||
// TODO: add the light to the transparent geometry list
|
// TODO: add the light to the transparent geometry list
|
||||||
|
|
||||||
|
add_light(light_index);
|
||||||
if (!cone_inside_plane(cone, wg_frustum_planes[4])) {
|
if (!cone_inside_plane(cone, wg_frustum_planes[4])) {
|
||||||
add_light(light_index);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -220,7 +237,7 @@ fn add_light(light_index: u32) -> bool {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sphere_inside_frustrum(frustum: array<vec4<f32>, 6>, sphere_origin: vec3<f32>, radius: f32) -> bool {
|
fn sphere_inside_frustrum(frustum: array<Plane, 6>, sphere_origin: vec3f, radius: f32) -> bool {
|
||||||
// to be able to index this array with a non-const value,
|
// to be able to index this array with a non-const value,
|
||||||
// it must be defined as a var
|
// it must be defined as a var
|
||||||
var frustum_v = frustum;
|
var frustum_v = frustum;
|
||||||
|
@ -239,11 +256,11 @@ fn sphere_inside_frustrum(frustum: array<vec4<f32>, 6>, sphere_origin: vec3<f32>
|
||||||
///
|
///
|
||||||
/// Source: Real-time collision detection, Christer Ericson (2005)
|
/// Source: Real-time collision detection, Christer Ericson (2005)
|
||||||
/// (https://www.3dgep.com/forward-plus/#light-culling-compute-shader)
|
/// (https://www.3dgep.com/forward-plus/#light-culling-compute-shader)
|
||||||
fn sphere_inside_plane(sphere_origin: vec3<f32>, radius: f32, plane: vec4<f32>) -> bool {
|
fn sphere_inside_plane(sphere_origin: vec3f, radius: f32, plane: Plane) -> bool {
|
||||||
return dot(plane.xyz, sphere_origin) - plane.w < -radius;
|
return dot(plane.normal, sphere_origin) - plane.origin_distance < -radius;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clip_to_view(clip: vec4<f32>) -> vec4<f32> {
|
fn clip_to_view(clip: vec4f) -> vec4f {
|
||||||
// view space position
|
// view space position
|
||||||
var view = u_camera.inverse_projection * clip;
|
var view = u_camera.inverse_projection * clip;
|
||||||
|
|
||||||
|
@ -251,12 +268,12 @@ fn clip_to_view(clip: vec4<f32>) -> vec4<f32> {
|
||||||
return view / view.w;
|
return view / view.w;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn screen_to_view(screen: vec4<f32>) -> vec4<f32> {
|
fn screen_to_view(screen: vec4f) -> vec4f {
|
||||||
// convert to normalized texture coordinates
|
// convert to normalized texture coordinates
|
||||||
let tex_coord = screen.xy / vec2<f32>(u_screen_size);
|
let tex_coord = screen.xy / vec2<f32>(u_screen_size);
|
||||||
|
|
||||||
// convert to clip space
|
// convert to clip space
|
||||||
let clip = vec4<f32>( vec2<f32>(tex_coord.x, 1.0 - tex_coord.y) * 2.0 - 1.0, screen.z, screen.w);
|
let clip = vec4f( vec2<f32>(tex_coord.x, 1.0 - tex_coord.y) * 2.0 - 1.0, screen.z, screen.w);
|
||||||
|
|
||||||
return clip_to_view(clip);
|
return clip_to_view(clip);
|
||||||
}
|
}
|
||||||
|
@ -264,52 +281,46 @@ fn screen_to_view(screen: vec4<f32>) -> vec4<f32> {
|
||||||
/// Compute a plane from 3 noncollinear points that form a triangle.
|
/// Compute a plane from 3 noncollinear points that form a triangle.
|
||||||
/// This equation assumes a right-handed (counter-clockwise winding order)
|
/// This equation assumes a right-handed (counter-clockwise winding order)
|
||||||
/// coordinate system to determine the direction of the plane normal.
|
/// coordinate system to determine the direction of the plane normal.
|
||||||
fn compute_plane(p0: vec3<f32>, p1: vec3<f32>, p2: vec3<f32>) -> vec4<f32> {
|
fn compute_plane(p0: vec3f, p1: vec3f, p2: vec3f) -> Plane {
|
||||||
let v0 = p1 - p0;
|
let v0 = p1 - p0;
|
||||||
let v2 = p2 - p0;
|
let v2 = p2 - p0;
|
||||||
|
|
||||||
var plane = vec4<f32>(normalize(cross(v0, v2)), 0.0);
|
let normal = vec4f(normalize(cross(v0, v2)), 0.0);
|
||||||
|
|
||||||
// find the distance to the origin
|
// find the distance to the origin
|
||||||
plane.w = dot(plane.xyz, p0);
|
let distance = dot(normal.xyz, p0);
|
||||||
|
|
||||||
return plane;
|
return Plane(normal.xyz, distance);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn point_inside_plane(point: vec3<f32>, plane: vec4<f32>) -> bool {
|
fn point_inside_plane(point: vec3f, plane: Plane) -> bool {
|
||||||
return dot(plane.xyz, point) - plane.w < 0.0;
|
return dot(plane.normal, point) + plane.origin_distance < 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn point_intersect_plane(point: vec3f, plane: Plane) -> f32 {
|
||||||
|
return dot(plane.normal, point) + plane.origin_distance;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check to see if a cone if fully behind (inside the negative halfspace of) a plane.
|
/// Check to see if a cone if fully behind (inside the negative halfspace of) a plane.
|
||||||
///
|
///
|
||||||
/// Source: Real-time collision detection, Christer Ericson (2005)
|
/// Source: Real-time collision detection, Christer Ericson (2005)
|
||||||
/// (https://www.3dgep.com/forward-plus/#light-culling-compute-shader)
|
/// (https://www.3dgep.com/forward-plus/#light-culling-compute-shader)
|
||||||
fn cone_inside_plane(cone: Cone, plane: vec4<f32>) -> bool {
|
fn cone_inside_plane(cone: Cone, plane: Plane) -> bool {
|
||||||
// Compute the farthest point on the end of the cone to the positive space of the plane.
|
let dir = cone.direction;
|
||||||
let m = cross(cross(plane.xyz, cone.direction), cone.direction);
|
let furthest_direction = cross(cross(plane.normal, dir), dir);
|
||||||
let farthest = cone.tip + cone.direction * cone.height - m * cone.radius;
|
let furthest = cone.tip + dir * cone.height - furthest_direction * cone.radius;
|
||||||
|
|
||||||
// The cone is in the negative halfspace of the plane if the tip of the cone,
|
// The cone is in the negative halfspace of the plane if the tip of the cone,
|
||||||
// and the farthest point on the end of the cone are inside the negative halfspace
|
// and the farthest point on the end of the cone are inside the negative halfspace
|
||||||
// of the plane.
|
// of the plane.
|
||||||
return point_inside_plane(cone.tip, plane) && point_inside_plane(farthest, plane);
|
return point_inside_plane(cone.tip, plane) && point_inside_plane(furthest, plane);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cone_inside_frustum(cone: Cone, frustum: array<vec4<f32>, 6>) -> bool {
|
fn cone_inside_frustum(cone: Cone, frustum: array<Plane, 6>) -> bool {
|
||||||
//let near_plane = frustum[4];
|
var frustum = frustum;
|
||||||
//let far_plane = frustum[5];
|
|
||||||
|
|
||||||
// check near and far clipping planes first
|
|
||||||
//if (cone_inside_plane(cone, near_plane) || cone_inside_plane(cone, far_plane)) {
|
|
||||||
// return false;
|
|
||||||
//}
|
|
||||||
|
|
||||||
// to be able to index this array with a non-const value,
|
|
||||||
// it must be defined as a var
|
|
||||||
var frustum_v = frustum;
|
|
||||||
|
|
||||||
for (var i = 0u; i < 4u; i++) {
|
for (var i = 0u; i < 4u; i++) {
|
||||||
if (cone_inside_plane(cone, frustum_v[i])) {
|
// TODO: better cone checking
|
||||||
|
if (sphere_inside_plane(cone.tip, cone.radius, frustum[i])) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue