Use preprocessed chunk bounding heights for chunk culling when possible

This commit is contained in:
Erik Broberg
2016-06-08 22:03:29 -04:00
parent 37b500b001
commit b9c4c80491
6 changed files with 98 additions and 206 deletions

View File

@@ -109,6 +109,32 @@ namespace openspace {
else return Status::DO_NOTHING;
}
Chunk::BoundingHeights Chunk::getBoundingHeights() const {
BoundingHeights boundingHeights;
boundingHeights.max = _owner->chunkHeight;
boundingHeights.min = 0;
boundingHeights.available = false;
// In the future, this should be abstracted away and more easily queryable.
// One must also handle how to sample pick one out of multiplte heightmaps
auto tileProvidermanager = owner()->getTileProviderManager();
auto heightMapProviders = tileProvidermanager->getActiveHeightMapProviders();
if (heightMapProviders.size() > 0) {
TileAndTransform tileAndTransform = heightMapProviders[0]->getHighestResolutionTile(_index);
if (tileAndTransform.tile.status == Tile::Status::OK) {
std::shared_ptr<TilePreprocessData> preprocessData = tileAndTransform.tile.preprocessData;
if ((preprocessData != nullptr) && preprocessData->maxValues.size() > 0) {
boundingHeights.max = preprocessData->maxValues[0];
boundingHeights.min = preprocessData->minValues[0];
boundingHeights.available = true;
}
}
}
return boundingHeights;
}
void Chunk::render(const RenderData& data) const {
_owner->getPatchRenderer().renderChunk(*this, data);
}

View File

@@ -40,9 +40,12 @@ namespace openspace {
class ChunkedLodGlobe;
class Chunk {
public:
struct BoundingHeights {
float min, max;
bool available;
};
enum class Status{
DO_NOTHING,
@@ -60,6 +63,7 @@ namespace openspace {
ChunkedLodGlobe* const owner() const;
const ChunkIndex index() const;
bool isVisible() const;
BoundingHeights getBoundingHeights() const;
void setIndex(const ChunkIndex& index);
void setOwner(ChunkedLodGlobe* newOwner);

View File

@@ -62,6 +62,7 @@ namespace openspace {
, minSplitDepth(2)
, maxSplitDepth(22)
, _savedCamera(nullptr)
, _tileProviderManager(tileProviderManager)
{
auto geometry = std::shared_ptr<SkirtedGrid>(new SkirtedGrid(
@@ -71,8 +72,9 @@ namespace openspace {
TriangleSoup::TextureCoordinates::Yes,
TriangleSoup::Normals::No));
_chunkCullers.push_back(new FrustumCuller(AABB3(vec3(-1, -1, 0), vec3(1, 1, 1e35))));
_chunkCullers.push_back(new HorizonCuller());
_chunkCullers.push_back(new FrustumCuller(AABB3(vec3(-1, -1, 0), vec3(1, 1, 1e35))));
_patchRenderer.reset(new ChunkRenderer(geometry, tileProviderManager));
}
@@ -94,15 +96,20 @@ namespace openspace {
return ready;
}
std::shared_ptr<TileProviderManager> ChunkedLodGlobe::getTileProviderManager() const {
return _tileProviderManager;
}
ChunkRenderer& ChunkedLodGlobe::getPatchRenderer() const{
return *_patchRenderer;
}
bool ChunkedLodGlobe::testIfCullable(const Chunk& chunk, const RenderData& renderData) {
if (doFrustumCulling && _chunkCullers[0]->isCullable(chunk, renderData)) {
if (doHorizonCulling && _chunkCullers[0]->isCullable(chunk, renderData)) {
return true;
}
if (doHorizonCulling && _chunkCullers[1]->isCullable(chunk, renderData)) {
if (doFrustumCulling && _chunkCullers[1]->isCullable(chunk, renderData)) {
return true;
}
return false;

View File

@@ -51,8 +51,7 @@ namespace ghoul {
namespace openspace {
class ChunkedLodGlobe :
public Renderable, public std::enable_shared_from_this<ChunkedLodGlobe>{
class ChunkedLodGlobe : public Renderable {
public:
ChunkedLodGlobe(
const Ellipsoid& ellipsoid,
@@ -83,6 +82,8 @@ namespace openspace {
const int maxSplitDepth;
std::shared_ptr<TileProviderManager> getTileProviderManager() const;
Camera* getSavedCamera() const { return _savedCamera; }
void setSaveCamera(Camera* c) {
@@ -129,6 +130,7 @@ namespace openspace {
Camera* _savedCamera;
std::shared_ptr<TileProviderManager> _tileProviderManager;
};
} // namespace openspace

View File

@@ -47,123 +47,47 @@ namespace openspace {
}
bool FrustumCuller::isCullable(const Chunk& chunk, const RenderData& renderData) {
return !isVisible(renderData, chunk.surfacePatch(), chunk.owner()->ellipsoid(), chunk.owner()->chunkHeight);
}
bool FrustumCuller::isVisible(
const RenderData& data,
const dvec3& point) {
dmat4 modelTransform = translate(dmat4(1), data.position.dvec3());
dmat4 viewTransform = dmat4(data.camera.combinedViewMatrix());
mat4 modelViewProjectionTransform = dmat4(data.camera.projectionMatrix())
* viewTransform * modelTransform;
vec2 pointScreenSpace =
transformToScreenSpace(point, modelViewProjectionTransform);
return testPoint(pointScreenSpace, vec2(0));
}
bool FrustumCuller::isVisible(const RenderData& data, const GeodeticPatch& patch,
const Ellipsoid& ellipsoid)
{
// Calculate the MVP matrix
dmat4 modelTransform = translate(dmat4(1), data.position.dvec3());
dmat4 viewTransform = dmat4(data.camera.combinedViewMatrix());
mat4 modelViewProjectionTransform = dmat4(data.camera.projectionMatrix())
* viewTransform * modelTransform;
// Calculate the patch's center point in screen space
dvec4 patchCenterModelSpace =
dvec4(ellipsoid.cartesianSurfacePosition(patch.center()), 1);
vec4 patchCenterClippingSpace =
modelViewProjectionTransform * patchCenterModelSpace;
vec2 pointScreenSpace =
(1.0f / patchCenterClippingSpace.w) * patchCenterClippingSpace.xy();
// Calculate the screen space margin that represents an axis aligned bounding
// box based on the patch's minimum boudning sphere
float boundingRadius = patch.minimalBoundingRadius(ellipsoid);
vec4 marginClippingSpace =
vec4(vec3(boundingRadius), 0) * mat4(data.camera.projectionMatrix());
vec2 marginScreenSpace =
(1.0f / patchCenterClippingSpace.w) * marginClippingSpace.xy();
// Test the bounding box by testing the center point and the corresponding margin
PointLocation res = testPoint(pointScreenSpace, marginScreenSpace);
return res == PointLocation::Inside;
}
bool FrustumCuller::isVisible(const RenderData& data, const GeodeticPatch& patch,
const Ellipsoid& ellipsoid, const Scalar maxHeight)
{
// Calculate the MVP matrix
dmat4 modelTransform = translate(dmat4(1), data.position.dvec3());
dmat4 viewTransform = dmat4(data.camera.combinedViewMatrix());
mat4 modelViewProjectionTransform = dmat4(data.camera.projectionMatrix())
* viewTransform * modelTransform;
double centerRadius = ellipsoid.maximumRadius();
//double centerRadius = glm::length(ellipsoid.cartesianSurfacePosition(patch.center()));
double maxCenterRadius = centerRadius + maxHeight;
bool FrustumCuller::isCullable(const Chunk& chunk, const RenderData& data) {
const Ellipsoid& ellipsoid = chunk.owner()->ellipsoid();
const GeodeticPatch& patch = chunk.surfacePatch();
// Calculate the MVP matrix
dmat4 modelTransform = translate(dmat4(1), data.position.dvec3());
dmat4 viewTransform = dmat4(data.camera.combinedViewMatrix());
mat4 modelViewProjectionTransform = dmat4(data.camera.projectionMatrix())
* viewTransform * modelTransform;
Chunk::BoundingHeights boundingHeight = chunk.getBoundingHeights();
double patchCenterRadius = ellipsoid.maximumRadius(); // assume worst case
// As the patch is curved, the maximum height offsets at the corners must be long
// enough to cover large enough to cover a boundingHeight.max at the center of the
// patch.
double maxCenterRadius = patchCenterRadius + boundingHeight.max;
double maximumPatchSide = max(patch.halfSize().lat, patch.halfSize().lon);
double maxHeightOffset = maxCenterRadius / cos(maximumPatchSide) - centerRadius;
double minHeightOffset = 0; // for now
double maxCornerHeight = maxCenterRadius / cos(maximumPatchSide) - patchCenterRadius;
// The minimum height offset, however, we can simply
double minCornerHeight = boundingHeight.min;
// Create a bounding box that fits the patch corners
AABB3 bounds; // in screen space
int numPositiveZ = 0;
for (size_t i = 0; i < 8; i++) {
Quad q = (Quad) (i%4);
double offset = i < 4 ? minHeightOffset : maxHeightOffset;
Geodetic3 cornerGeodetic = { patch.getCorner(q), offset };
Quad q = (Quad)(i % 4);
double cornerHeight = i < 4 ? minCornerHeight : maxCornerHeight;
const Geodetic3& cornerGeodetic = { patch.getCorner(q), cornerHeight };
dvec4 cornerModelSpace = dvec4(ellipsoid.cartesianPosition(cornerGeodetic), 1);
vec4 cornerClippingSpace = modelViewProjectionTransform * cornerModelSpace;
vec3 cornerScreenSpace = (1.0f / glm::abs(cornerClippingSpace.w)) * cornerClippingSpace.xyz();
bounds.expand(cornerScreenSpace);
}
return bounds.intersects(_viewFrustum);
}
PointLocation FrustumCuller::testPoint(const glm::vec2& pointScreenSpace,
const glm::vec2& marginScreenSpace)
{
const vec2& p = pointScreenSpace;
vec2 cullBounds = vec2(1) + marginScreenSpace;
int x = p.x <= -cullBounds.x ? 0 : p.x < cullBounds.x ? 1 : 2;
int y = p.y <= -cullBounds.y ? 0 : p.y < cullBounds.y ? 1 : 2;
PointLocation res = (PointLocation) (3 * y + x);
return res;
}
bool FrustumCuller::testPoint(const glm::vec3& pointScreenSpace,
const glm::vec3& marginScreenSpace)
{
const vec3& p = pointScreenSpace;
vec3 cullBounds = vec3(1) + marginScreenSpace;
int x = p.x <= -cullBounds.x ? 0 : p.x < cullBounds.x ? 1 : 2;
int y = p.y <= -cullBounds.y ? 0 : p.y < cullBounds.y ? 1 : 2;
int z = p.z <= -cullBounds.z ? 0 : p.z < cullBounds.z ? 1 : 2;
return x == 1 && y == 1 && z == 1;
}
glm::vec2 FrustumCuller::transformToScreenSpace(const dvec3& point,
const dmat4x4& modelViewProjection)
{
vec4 pointProjectionSpace = modelViewProjection * dvec4(point, 1.0);
vec2 pointScreenSpace =
(1.0f / pointProjectionSpace.w) * pointProjectionSpace.xy();
return pointScreenSpace;
return !bounds.intersects(_viewFrustum);
}
@@ -178,11 +102,28 @@ namespace openspace {
}
bool HorizonCuller::isCullable(const Chunk& chunk, const RenderData& renderData) {
return !isVisible(renderData, chunk.surfacePatch(), chunk.owner()->ellipsoid(), chunk.owner()->chunkHeight);
bool HorizonCuller::isCullable(const Chunk& chunk, const RenderData& data) {
//return !isVisible(data, chunk.surfacePatch(), chunk.owner()->ellipsoid(), chunk.owner()->chunkHeight);
const Ellipsoid& ellipsoid = chunk.owner()->ellipsoid();
const GeodeticPatch& patch = chunk.surfacePatch();
float maxHeight = chunk.getBoundingHeights().max;
Vec3 globePosition = data.position.dvec3();
Scalar minimumGlobeRadius = ellipsoid.minimumRadius();
Vec3 cameraPosition = data.camera.positionVec3();
Vec3 globeToCamera = cameraPosition - globePosition;
Geodetic2 cameraPositionOnGlobe =
ellipsoid.cartesianToGeodetic2(globeToCamera);
Geodetic2 closestPatchPoint = patch.closestPoint(cameraPositionOnGlobe);
const Vec3& objectPosition = ellipsoid.cartesianSurfacePosition(closestPatchPoint);
return isCullable(cameraPosition, globePosition, objectPosition,
maxHeight, minimumGlobeRadius);
}
bool HorizonCuller::isVisible(
bool HorizonCuller::isCullable(
const Vec3& cameraPosition,
const Vec3& globePosition,
const Vec3& objectPosition,
@@ -199,32 +140,7 @@ namespace openspace {
pow(distanceToHorizon + minimumAllowedDistanceToObjectFromHorizon, 2)
+ pow(objectBoundingSphereRadius, 2);
Scalar distanceToObjectSquared = pow(length(objectPosition - cameraPosition), 2);
return distanceToObjectSquared < minimumAllowedDistanceToObjectSquared;
}
bool HorizonCuller::isVisible(
const RenderData& data,
const GeodeticPatch& patch,
const Ellipsoid& ellipsoid,
float height)
{
Vec3 globePosition = data.position.dvec3();
Scalar minimumGlobeRadius = ellipsoid.minimumRadius();
Vec3 cameraPosition = data.camera.positionVec3();
Vec3 globeToCamera = cameraPosition - globePosition;
Geodetic2 cameraPositionOnGlobe =
ellipsoid.cartesianToGeodetic2(globeToCamera);
Geodetic2 closestPatchPoint = patch.closestPoint(cameraPositionOnGlobe);
return HorizonCuller::isVisible(
cameraPosition,
globePosition,
ellipsoid.cartesianSurfacePosition(closestPatchPoint),
height,
minimumGlobeRadius);
return distanceToObjectSquared > minimumAllowedDistanceToObjectSquared;
}
} // namespace openspace

View File

@@ -44,20 +44,13 @@ namespace openspace {
class Chunk;
class ChunkCuller {
public:
virtual void update() { }
virtual bool isCullable(const Chunk& chunk, const RenderData& renderData) = 0;
};
enum PointLocation {
AboveLeft = 0, Above, AboveRight, // 0, 1, 2
Left, Inside, Right, // 3, 4, 5
BelowLeft, Below, BelowRight // 6, 7, 8
};
class FrustumCuller : public ChunkCuller {
public:
@@ -67,53 +60,6 @@ namespace openspace {
virtual bool isCullable(const Chunk& chunk, const RenderData& renderData);
/**
Returns true if the point is inside the view frustrum defined in RenderData.
The third argument marginScreenSpace is added to the default screen space
boundaries. E.g. for marginScreenSpace = {0.2, 0.2}, all points with
1.2 < x,y < 1.2 would cause isVisible to return true.
*/
bool isVisible(
const RenderData& data,
const dvec3& point);
/**
Returns false if the patch element is guaranteed to be outside the view
frustrum, and true is the patch element MAY be inside the view frustrum.
*/
bool isVisible(
const RenderData& data,
const GeodeticPatch& patch,
const Ellipsoid& ellipsoid);
bool isVisible(
const RenderData& data,
const GeodeticPatch& patch,
const Ellipsoid& ellipsoid,
const Scalar maxHeight);
/**
The optional screen space margin vector is used to resize area defining
what is considered to be inside the view frustrum.
*/
PointLocation testPoint(
const glm::vec2& pointScreenSpace,
const glm::vec2& marginScreenSpace = dvec2(0));
bool testPoint(
const glm::vec3& pointScreenSpace,
const glm::vec3& marginScreenSpace = dvec3(0));
glm::vec2 transformToScreenSpace(
const dvec3& point,
const dmat4x4& modelViewProjection);
private:
const AABB3 _viewFrustum;
@@ -125,6 +71,7 @@ namespace openspace {
//sphere, the better this will make the splitting. Using the minimum radius to
//be safe. This means that if the ellipsoid has high difference between radii,
//splitting might accur even though it is not needed.
class HorizonCuller : public ChunkCuller {
public:
HorizonCuller();
@@ -132,20 +79,10 @@ namespace openspace {
virtual bool isCullable(const Chunk& chunk, const RenderData& renderData);
bool isVisible(
const Vec3& cameraPosition,
const Vec3& globePosition,
const Vec3& objectPosition,
Scalar objectBoundingSphereRadius,
bool isCullable(const Vec3& cameraPosition, const Vec3& globePosition,
const Vec3& objectPosition, Scalar objectBoundingSphereRadius,
Scalar minimumGlobeRadius);
bool isVisible(
const RenderData& data,
const GeodeticPatch& patch,
const Ellipsoid& ellipsoid,
float height);
private:
};