mirror of
https://github.com/godotengine/godot.git
synced 2024-11-27 06:33:16 +00:00
Sphere occluders - self occlusion and improvements
Sphere occluders are now tested for self occlusion. Spheres that are behind another sphere in the current view are superfluous so can be removed, cutting down on the runtime calculations. AABBs are now maintained for Occluders as well as individual spheres, meaning a bunch of occluder spheres can be frustum rejected as a block.
This commit is contained in:
parent
f85ad007c0
commit
d878fe7b90
@ -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
Block a user