Merge pull request #52661 from lawnjelly/portals_sphere_improvements
Sphere occluders - self occlusion and improvements
This commit is contained in:
commit
555108d378
|
@ -33,7 +33,7 @@
|
|||
#include "core/project_settings.h"
|
||||
#include "portal_renderer.h"
|
||||
|
||||
void PortalOcclusionCuller::prepare_generic(PortalRenderer &p_portal_renderer, const LocalVector<uint32_t, uint32_t> &p_occluder_pool_ids, const Vector3 &pt_camera, const LocalVector<Plane> &p_planes, const Plane *p_near_plane) {
|
||||
void PortalOcclusionCuller::prepare_generic(PortalRenderer &p_portal_renderer, const LocalVector<uint32_t, uint32_t> &p_occluder_pool_ids, const Vector3 &pt_camera, const LocalVector<Plane> &p_planes) {
|
||||
_num_spheres = 0;
|
||||
_pt_camera = pt_camera;
|
||||
|
||||
|
@ -65,22 +65,22 @@ void PortalOcclusionCuller::prepare_generic(PortalRenderer &p_portal_renderer, c
|
|||
// make sure world space spheres are up to date
|
||||
p_portal_renderer.occluder_ensure_up_to_date_sphere(occ);
|
||||
|
||||
// cull entire AABB
|
||||
if (is_aabb_culled(occ.aabb, p_planes)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// multiple spheres
|
||||
for (int n = 0; n < occ.list_ids.size(); n++) {
|
||||
const Occlusion::Sphere &occluder_sphere = p_portal_renderer.get_pool_occluder_sphere(occ.list_ids[n]).world;
|
||||
|
||||
// is the occluder sphere culled?
|
||||
if (is_sphere_culled(occluder_sphere.pos, occluder_sphere.radius, p_planes, p_near_plane)) {
|
||||
if (is_sphere_culled(occluder_sphere.pos, occluder_sphere.radius, p_planes)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
real_t dist = (occluder_sphere.pos - pt_camera).length();
|
||||
|
||||
// keep a record of the closest sphere for quick rejects
|
||||
if (dist < _sphere_closest_dist) {
|
||||
_sphere_closest_dist = dist;
|
||||
}
|
||||
|
||||
// calculate the goodness of fit .. smaller distance better, and larger radius
|
||||
// calculate adjusted radius at 100.0
|
||||
real_t fit = 100 / MAX(dist, 0.01);
|
||||
|
@ -98,6 +98,11 @@ void PortalOcclusionCuller::prepare_generic(PortalRenderer &p_portal_renderer, c
|
|||
weakest_sphere = _num_spheres;
|
||||
}
|
||||
|
||||
// keep a record of the closest sphere for quick rejects
|
||||
if (dist < _sphere_closest_dist) {
|
||||
_sphere_closest_dist = dist;
|
||||
}
|
||||
|
||||
_num_spheres++;
|
||||
} else {
|
||||
// must beat the weakest
|
||||
|
@ -106,6 +111,11 @@ void PortalOcclusionCuller::prepare_generic(PortalRenderer &p_portal_renderer, c
|
|||
_sphere_distances[weakest_sphere] = dist;
|
||||
goodness_of_fit[weakest_sphere] = fit;
|
||||
|
||||
// keep a record of the closest sphere for quick rejects
|
||||
if (dist < _sphere_closest_dist) {
|
||||
_sphere_closest_dist = dist;
|
||||
}
|
||||
|
||||
// the weakest may have changed (this could be done more efficiently)
|
||||
weakest_fit = FLT_MAX;
|
||||
for (int s = 0; s < _max_spheres; s++) {
|
||||
|
@ -123,9 +133,26 @@ void PortalOcclusionCuller::prepare_generic(PortalRenderer &p_portal_renderer, c
|
|||
// force the sphere closest distance to above zero to prevent
|
||||
// divide by zero in the quick reject
|
||||
_sphere_closest_dist = MAX(_sphere_closest_dist, 0.001);
|
||||
|
||||
// sphere self occlusion.
|
||||
// we could avoid testing the closest sphere, but the complexity isn't worth any speed benefit
|
||||
for (int n = 0; n < _num_spheres; n++) {
|
||||
const Occlusion::Sphere &sphere = _spheres[n];
|
||||
|
||||
// is it occluded by another sphere?
|
||||
if (cull_sphere(sphere.pos, sphere.radius, n)) {
|
||||
// yes, unordered remove
|
||||
_num_spheres--;
|
||||
_spheres[n] = _spheres[_num_spheres];
|
||||
_sphere_distances[n] = _sphere_distances[_num_spheres];
|
||||
|
||||
// repeat this n
|
||||
n--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool PortalOcclusionCuller::cull_sphere(const Vector3 &p_occludee_center, real_t p_occludee_radius) const {
|
||||
bool PortalOcclusionCuller::cull_sphere(const Vector3 &p_occludee_center, real_t p_occludee_radius, int p_ignore_sphere) const {
|
||||
if (!_num_spheres) {
|
||||
return false;
|
||||
}
|
||||
|
@ -169,7 +196,7 @@ bool PortalOcclusionCuller::cull_sphere(const Vector3 &p_occludee_center, real_t
|
|||
real_t dist;
|
||||
|
||||
if (occluder_sphere.intersect_ray(_pt_camera, ray_dir, dist, occluder_radius)) {
|
||||
if (dist < dist_to_occludee) {
|
||||
if ((dist < dist_to_occludee) && (s != p_ignore_sphere)) {
|
||||
// occluded
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -42,10 +42,25 @@ class PortalOcclusionCuller {
|
|||
public:
|
||||
PortalOcclusionCuller();
|
||||
void prepare(PortalRenderer &p_portal_renderer, const VSRoom &p_room, const Vector3 &pt_camera, const LocalVector<Plane> &p_planes, const Plane *p_near_plane) {
|
||||
prepare_generic(p_portal_renderer, p_room._occluder_pool_ids, pt_camera, p_planes, p_near_plane);
|
||||
if (p_near_plane) {
|
||||
static LocalVector<Plane> local_planes;
|
||||
int size_wanted = p_planes.size() + 1;
|
||||
|
||||
if ((int)local_planes.size() != size_wanted) {
|
||||
local_planes.resize(size_wanted);
|
||||
}
|
||||
for (int n = 0; n < (int)p_planes.size(); n++) {
|
||||
local_planes[n] = p_planes[n];
|
||||
}
|
||||
local_planes[size_wanted - 1] = *p_near_plane;
|
||||
|
||||
prepare_generic(p_portal_renderer, p_room._occluder_pool_ids, pt_camera, local_planes);
|
||||
} else {
|
||||
prepare_generic(p_portal_renderer, p_room._occluder_pool_ids, pt_camera, p_planes);
|
||||
}
|
||||
}
|
||||
|
||||
void prepare_generic(PortalRenderer &p_portal_renderer, const LocalVector<uint32_t, uint32_t> &p_occluder_pool_ids, const Vector3 &pt_camera, const LocalVector<Plane> &p_planes, const Plane *p_near_plane);
|
||||
void prepare_generic(PortalRenderer &p_portal_renderer, const LocalVector<uint32_t, uint32_t> &p_occluder_pool_ids, const Vector3 &pt_camera, const LocalVector<Plane> &p_planes);
|
||||
bool cull_aabb(const AABB &p_aabb) const {
|
||||
if (!_num_spheres) {
|
||||
return false;
|
||||
|
@ -53,21 +68,34 @@ public:
|
|||
|
||||
return cull_sphere(p_aabb.get_center(), p_aabb.size.length() * 0.5);
|
||||
}
|
||||
bool cull_sphere(const Vector3 &p_occludee_center, real_t p_occludee_radius) const;
|
||||
bool cull_sphere(const Vector3 &p_occludee_center, real_t p_occludee_radius, int p_ignore_sphere = -1) const;
|
||||
|
||||
private:
|
||||
// if a sphere is entirely in front of any of the culling planes, it can't be seen so returns false
|
||||
bool is_sphere_culled(const Vector3 &p_pos, real_t p_radius, const LocalVector<Plane> &p_planes, const Plane *p_near_plane) const {
|
||||
if (p_near_plane) {
|
||||
real_t dist = p_near_plane->distance_to(p_pos);
|
||||
bool is_sphere_culled(const Vector3 &p_pos, real_t p_radius, const LocalVector<Plane> &p_planes) const {
|
||||
for (unsigned int p = 0; p < p_planes.size(); p++) {
|
||||
real_t dist = p_planes[p].distance_to(p_pos);
|
||||
if (dist > p_radius) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned int p = 0; p < p_planes.size(); p++) {
|
||||
real_t dist = p_planes[p].distance_to(p_pos);
|
||||
if (dist > p_radius) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_aabb_culled(const AABB &p_aabb, const LocalVector<Plane> &p_planes) const {
|
||||
const Vector3 &size = p_aabb.size;
|
||||
Vector3 half_extents = size * 0.5;
|
||||
Vector3 ofs = p_aabb.position + half_extents;
|
||||
|
||||
for (unsigned int i = 0; i < p_planes.size(); i++) {
|
||||
const Plane &p = p_planes[i];
|
||||
Vector3 point(
|
||||
(p.normal.x > 0) ? -half_extents.x : half_extents.x,
|
||||
(p.normal.y > 0) ? -half_extents.y : half_extents.y,
|
||||
(p.normal.z > 0) ? -half_extents.z : half_extents.z);
|
||||
point += ofs;
|
||||
if (p.is_point_over(point)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -335,6 +335,10 @@ inline void PortalRenderer::occluder_ensure_up_to_date_sphere(VSOccluder &r_occl
|
|||
Vector3 scale3 = tr.basis.get_scale_abs();
|
||||
real_t scale = (scale3.x + scale3.y + scale3.z) / 3.0;
|
||||
|
||||
// update the AABB
|
||||
Vector3 bb_min = Vector3(FLT_MAX, FLT_MAX, FLT_MAX);
|
||||
Vector3 bb_max = Vector3(-FLT_MAX, -FLT_MAX, -FLT_MAX);
|
||||
|
||||
// transform spheres
|
||||
for (int n = 0; n < r_occluder.list_ids.size(); n++) {
|
||||
uint32_t pool_id = r_occluder.list_ids[n];
|
||||
|
@ -343,7 +347,21 @@ inline void PortalRenderer::occluder_ensure_up_to_date_sphere(VSOccluder &r_occl
|
|||
// transform position and radius
|
||||
osphere.world.pos = tr.xform(osphere.local.pos);
|
||||
osphere.world.radius = osphere.local.radius * scale;
|
||||
|
||||
Vector3 bradius = Vector3(osphere.world.radius, osphere.world.radius, osphere.world.radius);
|
||||
Vector3 bmin = osphere.world.pos - bradius;
|
||||
Vector3 bmax = osphere.world.pos + bradius;
|
||||
|
||||
bb_min.x = MIN(bb_min.x, bmin.x);
|
||||
bb_min.y = MIN(bb_min.y, bmin.y);
|
||||
bb_min.z = MIN(bb_min.z, bmin.z);
|
||||
bb_max.x = MAX(bb_max.x, bmax.x);
|
||||
bb_max.y = MAX(bb_max.y, bmax.y);
|
||||
bb_max.z = MAX(bb_max.z, bmax.z);
|
||||
}
|
||||
|
||||
r_occluder.aabb.position = bb_min;
|
||||
r_occluder.aabb.size = bb_max - bb_min;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -544,7 +544,7 @@ int PortalTracer::occlusion_cull(PortalRenderer &p_portal_renderer, const Vector
|
|||
local_planes[n] = p_convex[n];
|
||||
}
|
||||
|
||||
_occlusion_culler.prepare_generic(p_portal_renderer, p_portal_renderer.get_occluders_active_list(), p_point, local_planes, nullptr);
|
||||
_occlusion_culler.prepare_generic(p_portal_renderer, p_portal_renderer.get_occluders_active_list(), p_point, local_planes);
|
||||
|
||||
// cull each instance
|
||||
int count = p_num_results;
|
||||
|
|
|
@ -400,6 +400,9 @@ struct VSOccluder {
|
|||
// location for finding the room
|
||||
Vector3 pt_center;
|
||||
|
||||
// world space aabb, only updated when dirty
|
||||
AABB aabb;
|
||||
|
||||
// global xform
|
||||
Transform xform;
|
||||
|
||||
|
|
Loading…
Reference in New Issue