mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-04-19 00:12:43 -05:00
View frustrum culling done directly in chunk node tree update traversal
This commit is contained in:
@@ -76,7 +76,7 @@ void ChunkNode::render(const RenderData& data, ChunkIndex traverseData) {
|
||||
// Returns true or false wether this node can be merge or not
|
||||
bool ChunkNode::internalUpdateChunkTree(const RenderData& data, ChunkIndex& traverseData) {
|
||||
using namespace glm;
|
||||
LatLon center = _patch.center;
|
||||
LatLon center = _patch.center();
|
||||
|
||||
|
||||
//LDEBUG("x: " << patch.x << " y: " << patch.y << " level: " << patch.level << " lat: " << center.lat << " lon: " << center.lon);
|
||||
@@ -117,6 +117,7 @@ bool ChunkNode::internalUpdateChunkTree(const RenderData& data, ChunkIndex& trav
|
||||
void ChunkNode::internalRender(const RenderData& data, ChunkIndex& traverseData) {
|
||||
if (isLeaf()) {
|
||||
PatchRenderer& patchRenderer = _owner.getPatchRenderer();
|
||||
|
||||
patchRenderer.renderPatch(_patch, data, _owner.globeRadius);
|
||||
}
|
||||
else {
|
||||
@@ -130,7 +131,7 @@ void ChunkNode::internalRender(const RenderData& data, ChunkIndex& traverseData)
|
||||
int ChunkNode::calculateDesiredLevel(const RenderData& data, const ChunkIndex& traverseData) {
|
||||
|
||||
|
||||
Vec3 patchNormal = _patch.center.asUnitCartesian();
|
||||
Vec3 patchNormal = _patch.center().asUnitCartesian();
|
||||
Vec3 patchPosition = data.position.dvec3() + _owner.globeRadius * patchNormal;
|
||||
|
||||
Vec3 cameraPosition = data.camera.position().dvec3();
|
||||
@@ -146,6 +147,14 @@ int ChunkNode::calculateDesiredLevel(const RenderData& data, const ChunkIndex& t
|
||||
}
|
||||
|
||||
|
||||
// Do frustrum culling
|
||||
FrustrumCuller& culler = _owner.getFrustrumCuller();
|
||||
|
||||
if (!culler.isVisible(data, _patch, _owner.globeRadius)) {
|
||||
return traverseData.level - 1;
|
||||
}
|
||||
|
||||
// Calculate desired level based on distance
|
||||
Scalar distance = glm::length(cameraToChunk);
|
||||
_owner.minDistToCamera = fmin(_owner.minDistToCamera, distance);
|
||||
|
||||
@@ -161,8 +170,8 @@ void ChunkNode::split(int depth) {
|
||||
if (depth > 0 && isLeaf()) {
|
||||
|
||||
// Defining short handles for center, halfSize and quarterSize
|
||||
const LatLon& c = _patch.center;
|
||||
const LatLon& hs = _patch.halfSize;
|
||||
const LatLon& c = _patch.center();
|
||||
const LatLon& hs = _patch.halfSize();
|
||||
LatLon qs = LatLon(0.5 * hs.lat, 0.5 * hs.lon);
|
||||
|
||||
// Subdivide bounds
|
||||
|
||||
@@ -55,7 +55,7 @@ namespace openspace {
|
||||
}
|
||||
|
||||
|
||||
Vec3 LatLon::asUnitCartesian() {
|
||||
Vec3 LatLon::asUnitCartesian() const{
|
||||
return Vec3(
|
||||
glm::cos(lat) * glm::cos(lon),
|
||||
glm::cos(lat) * glm::sin(lon),
|
||||
@@ -75,47 +75,70 @@ namespace openspace {
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
LatLonPatch::LatLonPatch(Scalar centerLat, Scalar centerLon, Scalar halfSizeLat, Scalar halfSizeLon)
|
||||
: center(LatLon(centerLat, centerLon))
|
||||
, halfSize(LatLon(halfSizeLat, halfSizeLon))
|
||||
: _center(LatLon(centerLat, centerLon))
|
||||
, _halfSize(LatLon(halfSizeLat, halfSizeLon))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
LatLonPatch::LatLonPatch(const LatLon& center, const LatLon& halfSize)
|
||||
: center(center)
|
||||
, halfSize(halfSize)
|
||||
: _center(center)
|
||||
, _halfSize(halfSize)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
LatLonPatch::LatLonPatch(const LatLonPatch& patch)
|
||||
: center(patch.center)
|
||||
, halfSize(patch.halfSize)
|
||||
: _center(patch._center)
|
||||
, _halfSize(patch._halfSize)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
void LatLonPatch::setCenter(const LatLon& center) {
|
||||
_center = center;
|
||||
}
|
||||
|
||||
void LatLonPatch::setHalfSize(const LatLon& halfSize) {
|
||||
_halfSize = halfSize;
|
||||
}
|
||||
|
||||
Scalar LatLonPatch::minimalBoundingRadius() const {
|
||||
const LatLon& cornerNearEquator = _center.lat > 0 ? southWestCorner() : northWestCorner();
|
||||
return glm::length(_center.asUnitCartesian() - cornerNearEquator.asUnitCartesian());
|
||||
}
|
||||
|
||||
Scalar LatLonPatch::unitArea() const {
|
||||
Scalar deltaTheta = 2 * halfSize.lon;
|
||||
Scalar phiMin = center.lat - halfSize.lat;
|
||||
Scalar phiMax = center.lat + halfSize.lat;
|
||||
Scalar deltaTheta = 2 * _halfSize.lon;
|
||||
Scalar phiMin = _center.lat - _halfSize.lat;
|
||||
Scalar phiMax = _center.lat + _halfSize.lat;
|
||||
return deltaTheta * (sin(phiMax) - sin(phiMin));
|
||||
}
|
||||
|
||||
const LatLon& LatLonPatch::center() const {
|
||||
return _center;
|
||||
}
|
||||
|
||||
const LatLon& LatLonPatch::halfSize() const {
|
||||
return _halfSize;
|
||||
}
|
||||
|
||||
|
||||
LatLon LatLonPatch::northWestCorner() const{
|
||||
return LatLon(center.lat + halfSize.lat, center.lon - halfSize.lon);
|
||||
return LatLon(_center.lat + _halfSize.lat, _center.lon - _halfSize.lon);
|
||||
}
|
||||
|
||||
LatLon LatLonPatch::northEastCorner() const{
|
||||
return LatLon(center.lat + halfSize.lat, center.lon + halfSize.lon);
|
||||
return LatLon(_center.lat + _halfSize.lat, _center.lon + _halfSize.lon);
|
||||
}
|
||||
|
||||
LatLon LatLonPatch::southWestCorner() const{
|
||||
return LatLon(center.lat - halfSize.lat, center.lon - halfSize.lon);
|
||||
return LatLon(_center.lat - _halfSize.lat, _center.lon - _halfSize.lon);
|
||||
}
|
||||
|
||||
LatLon LatLonPatch::southEastCorner() const{
|
||||
return LatLon(center.lat - halfSize.lat, center.lon + halfSize.lon);
|
||||
return LatLon(_center.lat - _halfSize.lat, _center.lon + _halfSize.lon);
|
||||
}
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
@@ -43,7 +43,7 @@ struct LatLon {
|
||||
LatLon(const LatLon& src);
|
||||
|
||||
static LatLon fromCartesian(const Vec3& v);
|
||||
Vec3 asUnitCartesian();
|
||||
Vec3 asUnitCartesian() const;
|
||||
Vec2 asVec2() const;
|
||||
|
||||
inline bool operator==(const LatLon& other);
|
||||
@@ -53,21 +53,39 @@ struct LatLon {
|
||||
Scalar lon;
|
||||
};
|
||||
|
||||
struct LatLonPatch {
|
||||
|
||||
|
||||
|
||||
class LatLonPatch {
|
||||
public:
|
||||
LatLonPatch(Scalar, Scalar, Scalar, Scalar);
|
||||
LatLonPatch(const LatLon& center, const LatLon& halfSize);
|
||||
LatLonPatch(const LatLonPatch& patch);
|
||||
|
||||
|
||||
void setCenter(const LatLon&);
|
||||
void setHalfSize(const LatLon&);
|
||||
|
||||
|
||||
// Returns the minimal bounding radius that together with the LatLonPatch's
|
||||
// center point represents a sphere in which the patch is completely contained
|
||||
Scalar minimalBoundingRadius() const;
|
||||
|
||||
// Returns the area of the patch with unit radius
|
||||
Scalar unitArea() const;
|
||||
|
||||
|
||||
LatLon northWestCorner() const;
|
||||
LatLon northEastCorner() const;
|
||||
LatLon southWestCorner() const;
|
||||
LatLon southEastCorner() const;
|
||||
|
||||
LatLon center;
|
||||
LatLon halfSize;
|
||||
const LatLon& center() const;
|
||||
const LatLon& halfSize() const;
|
||||
|
||||
private:
|
||||
LatLon _center;
|
||||
LatLon _halfSize;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -87,8 +87,7 @@ namespace openspace {
|
||||
|
||||
_patchRenderer.reset(new LatLonPatchRenderer(geometry));
|
||||
|
||||
std::shared_ptr<FrustrumCuller> fc(new FrustrumCuller(1.3f));
|
||||
_patchRenderer->setFrustrumCuller(fc);
|
||||
_frustrumCuller = std::shared_ptr<FrustrumCuller>(new FrustrumCuller());
|
||||
|
||||
}
|
||||
|
||||
@@ -113,12 +112,18 @@ namespace openspace {
|
||||
return *_patchRenderer;
|
||||
}
|
||||
|
||||
FrustrumCuller& ChunkLodGlobe::getFrustrumCuller() {
|
||||
return *_frustrumCuller;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ChunkLodGlobe::render(const RenderData& data){
|
||||
minDistToCamera = INFINITY;
|
||||
PatchIndex leftRootTileIndex = { 0, 0, 1 };
|
||||
ChunkIndex leftRootTileIndex = { 0, 0, 1 };
|
||||
_leftRoot->render(data, leftRootTileIndex);
|
||||
|
||||
PatchIndex rightRootTileIndex = { 1, 0, 1 };
|
||||
ChunkIndex rightRootTileIndex = { 1, 0, 1 };
|
||||
_rightRoot->render(data, rightRootTileIndex);
|
||||
|
||||
//LDEBUG("min distnace to camera: " << minDistToCamera);
|
||||
|
||||
@@ -57,6 +57,7 @@ namespace openspace {
|
||||
~ChunkLodGlobe();
|
||||
|
||||
PatchRenderer& getPatchRenderer();
|
||||
FrustrumCuller& getFrustrumCuller();
|
||||
|
||||
bool initialize() override;
|
||||
bool deinitialize() override;
|
||||
@@ -81,6 +82,9 @@ namespace openspace {
|
||||
// Covers all positive longitudes
|
||||
std::unique_ptr<ChunkNode> _rightRoot;
|
||||
|
||||
// Frustrum culler
|
||||
std::shared_ptr<FrustrumCuller> _frustrumCuller;
|
||||
|
||||
// the patch used for actual rendering
|
||||
std::unique_ptr<PatchRenderer> _patchRenderer;
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ namespace openspace {
|
||||
// Set patches to follow camera
|
||||
for (size_t i = 0; i < _patches.size(); i++)
|
||||
{
|
||||
_patches[i].center = LatLon::fromCartesian(data.camera.position().dvec3());
|
||||
_patches[i].setCenter(LatLon::fromCartesian(data.camera.position().dvec3()));
|
||||
}
|
||||
// render patches
|
||||
for (size_t i = 0; i < _patches.size(); i++)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*****************************************************************************************
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
@@ -34,8 +34,7 @@ namespace openspace {
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
// PATCH RENDERER //
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
FrustrumCuller::FrustrumCuller(float tolerance)
|
||||
: _tolerance(tolerance)
|
||||
FrustrumCuller::FrustrumCuller()
|
||||
{
|
||||
|
||||
}
|
||||
@@ -45,23 +44,66 @@ namespace openspace {
|
||||
}
|
||||
|
||||
|
||||
bool FrustrumCuller::isVisible(const vec3& point, const mat4x4& modelViewProjection) {
|
||||
vec4 pointModelSpace(point, 1.0f);
|
||||
vec4 pointProjectionSpace = modelViewProjection * pointModelSpace;
|
||||
vec2 pointScreenSpace = (1.0f / pointProjectionSpace.w) * pointProjectionSpace.xy;
|
||||
|
||||
bool FrustrumCuller::isVisible(const RenderData& data, const vec3& point, const glm::vec2& marginScreenSpace) {
|
||||
|
||||
// just for readability
|
||||
mat4 modelTransform = translate(mat4(1), data.position.vec3());
|
||||
mat4 viewTransform = data.camera.combinedViewMatrix();
|
||||
mat4 modelViewProjectionTransform = data.camera.projectionMatrix()
|
||||
* viewTransform * modelTransform;
|
||||
|
||||
vec2 pointScreenSpace = transformToScreenSpace(point, modelViewProjectionTransform);
|
||||
return testPoint(pointScreenSpace, marginScreenSpace);
|
||||
}
|
||||
|
||||
|
||||
bool FrustrumCuller::isVisible(const RenderData& data, const LatLonPatch& patch, double radius) {
|
||||
// An axis aligned bounding box based on the patch's minimum boudning sphere is
|
||||
// used for testnig
|
||||
|
||||
// Calculate the MVP matrix
|
||||
mat4 modelTransform = translate(mat4(1), data.position.vec3());
|
||||
mat4 viewTransform = data.camera.combinedViewMatrix();
|
||||
mat4 modelViewProjectionTransform = data.camera.projectionMatrix()
|
||||
* viewTransform * modelTransform;
|
||||
|
||||
// Calculate the patch's center point in screen space
|
||||
vec4 patchCenterModelSpace = vec4(radius * patch.center().asUnitCartesian(), 1);
|
||||
vec4 patchCenterProjectionSpace = modelViewProjectionTransform * patchCenterModelSpace;
|
||||
vec2 pointScreenSpace = (1.0f / patchCenterProjectionSpace.w) * patchCenterProjectionSpace.xy;
|
||||
|
||||
// Calculate the screen space margin that represents an axis aligned bounding
|
||||
// box based on the patch's minimum boudning sphere
|
||||
double boundingRadius = radius * patch.minimalBoundingRadius();
|
||||
vec4 marginProjectionSpace = vec4(vec3(boundingRadius), 0) * data.camera.projectionMatrix();
|
||||
vec2 marginScreenSpace = (1.0f / patchCenterProjectionSpace.w) * marginProjectionSpace.xy;
|
||||
|
||||
// Test the bounding box by testing the center point and the corresponding margin
|
||||
return testPoint(pointScreenSpace, marginScreenSpace);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool FrustrumCuller::testPoint(const glm::vec2& pointScreenSpace,
|
||||
const glm::vec2& marginScreenSpace) const
|
||||
{
|
||||
|
||||
const vec2& p = pointScreenSpace;
|
||||
return ((-_tolerance < p.x && p.x < _tolerance) &
|
||||
(-_tolerance < p.y && p.y < _tolerance));
|
||||
|
||||
|
||||
vec2 cullBounds = vec2(1) + marginScreenSpace;
|
||||
return ((-cullBounds.x < p.x && p.x < cullBounds.x) &&
|
||||
(-cullBounds.y < p.y && p.y < cullBounds.y));
|
||||
}
|
||||
|
||||
bool FrustrumCuller::isVisible(const LatLonPatch& patch, double radius, const mat4x4& modelViewProjection) {
|
||||
return isVisible(radius * patch.northWestCorner().asUnitCartesian(), modelViewProjection)
|
||||
|| isVisible(radius * patch.northEastCorner().asUnitCartesian(), modelViewProjection)
|
||||
|| isVisible(radius * patch.southWestCorner().asUnitCartesian(), modelViewProjection)
|
||||
|| isVisible(radius * patch.southEastCorner().asUnitCartesian(), modelViewProjection);
|
||||
|
||||
glm::vec2 FrustrumCuller::transformToScreenSpace(const vec3& point,
|
||||
const mat4x4& modelViewProjection) const
|
||||
{
|
||||
vec4 pointProjectionSpace = modelViewProjection * vec4(point, 1.0f);
|
||||
vec2 pointScreenSpace = (1.0f / pointProjectionSpace.w) * pointProjectionSpace.xy;
|
||||
return pointScreenSpace;
|
||||
}
|
||||
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
@@ -45,15 +45,38 @@ namespace openspace {
|
||||
class FrustrumCuller {
|
||||
public:
|
||||
|
||||
FrustrumCuller(float tolerance = 1.0f);
|
||||
FrustrumCuller();
|
||||
~FrustrumCuller();
|
||||
|
||||
bool isVisible(const vec3& point, const mat4x4& modelViewProjection);
|
||||
bool isVisible(const LatLonPatch& patch, double radius, const mat4x4& modelViewProjection);
|
||||
/// 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 vec3& point,
|
||||
const vec2& marginScreenSpace = vec2(0));
|
||||
|
||||
|
||||
/// 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 LatLonPatch& patch,
|
||||
double radius);
|
||||
|
||||
|
||||
protected:
|
||||
float _tolerance;
|
||||
private:
|
||||
|
||||
|
||||
/// Returns true if the point in screen space is inside the view frustrum.
|
||||
/// The optional screen space margin vector is used to resize area defining
|
||||
/// what is considered to be inside the view frustrum.
|
||||
bool testPoint(
|
||||
const glm::vec2& pointScreenSpace,
|
||||
const glm::vec2& marginScreenSpace) const;
|
||||
|
||||
glm::vec2 transformToScreenSpace(const vec3& point, const mat4x4& modelViewProjection) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -67,9 +67,7 @@ namespace openspace {
|
||||
}
|
||||
}
|
||||
|
||||
void PatchRenderer::setFrustrumCuller(std::shared_ptr<FrustrumCuller> fc) {
|
||||
_frustrumCuller = fc;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
// LATLON PATCH RENDERER //
|
||||
@@ -104,23 +102,10 @@ namespace openspace {
|
||||
* viewTransform * modelTransform;
|
||||
|
||||
|
||||
// View frustrum culling
|
||||
if (_frustrumCuller != nullptr) {
|
||||
//LatLon center = patch.center;
|
||||
//vec3 centerPoint = radius * center.asUnitCartesian();
|
||||
if (!_frustrumCuller->isVisible(patch, radius, modelViewProjectionTransform)) {
|
||||
// dont render the patch
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
LatLon swCorner = patch.southWestCorner();
|
||||
_programObject->setUniform("modelViewProjectionTransform", modelViewProjectionTransform);
|
||||
_programObject->setUniform("minLatLon", vec2(swCorner.lat, swCorner.lon));
|
||||
_programObject->setUniform("latLonScalingFactor", 2.0f * vec2(patch.halfSize.asVec2()));
|
||||
_programObject->setUniform("latLonScalingFactor", 2.0f * vec2(patch.halfSize().asVec2()));
|
||||
_programObject->setUniform("globeRadius", float(radius));
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
@@ -157,25 +142,26 @@ namespace openspace {
|
||||
_programObject->activate();
|
||||
using namespace glm;
|
||||
|
||||
const mat4& viewTransform = data.camera.combinedViewMatrix();
|
||||
mat4 viewTransform = data.camera.combinedViewMatrix();
|
||||
|
||||
// TODO : Model transform should be fetched as a matrix directly.
|
||||
mat4 modelTransform = translate(mat4(1), data.position.vec3());
|
||||
|
||||
// Snap patch position
|
||||
int segmentsPerPatch = 32;
|
||||
LatLon halfSize = patch.halfSize();
|
||||
LatLon stepSize = LatLon(
|
||||
patch.halfSize.lat * 2 / segmentsPerPatch,
|
||||
patch.halfSize.lon * 2 / segmentsPerPatch);
|
||||
halfSize.lat * 2 / segmentsPerPatch,
|
||||
halfSize.lon * 2 / segmentsPerPatch);
|
||||
ivec2 patchesToCoverGlobe = ivec2(
|
||||
M_PI / (patch.halfSize.lat * 2) + 0.5,
|
||||
M_PI * 2 / (patch.halfSize.lon * 2) + 0.5);
|
||||
M_PI / (halfSize.lat * 2) + 0.5,
|
||||
M_PI * 2 / (halfSize.lon * 2) + 0.5);
|
||||
ivec2 intSnapCoord = ivec2(
|
||||
patch.center.lat / (M_PI * 2) * segmentsPerPatch * patchesToCoverGlobe.y,
|
||||
patch.center.lon / (M_PI) * segmentsPerPatch * patchesToCoverGlobe.x);
|
||||
patch.center().lat / (M_PI * 2) * segmentsPerPatch * patchesToCoverGlobe.y,
|
||||
patch.center().lon / (M_PI) * segmentsPerPatch * patchesToCoverGlobe.x);
|
||||
LatLon swCorner = LatLon(
|
||||
stepSize.lat * intSnapCoord.x - patch.halfSize.lat,
|
||||
stepSize.lon * intSnapCoord.y - patch.halfSize.lon);
|
||||
stepSize.lat * intSnapCoord.x - halfSize.lat,
|
||||
stepSize.lon * intSnapCoord.y - halfSize.lon);
|
||||
|
||||
ivec2 contraction = ivec2(intSnapCoord.y % 2, intSnapCoord.x % 2);
|
||||
|
||||
@@ -185,7 +171,7 @@ namespace openspace {
|
||||
|
||||
_programObject->setUniform("modelViewProjectionTransform", data.camera.projectionMatrix() * viewTransform * modelTransform);
|
||||
_programObject->setUniform("minLatLon", vec2(swCorner.lat, swCorner.lon));
|
||||
_programObject->setUniform("latLonScalingFactor", 2.0f * vec2(patch.halfSize.lat, patch.halfSize.lon));
|
||||
_programObject->setUniform("latLonScalingFactor", 2.0f * vec2(halfSize.lat, halfSize.lon));
|
||||
_programObject->setUniform("globeRadius", float(radius));
|
||||
_programObject->setUniform("contraction", contraction);
|
||||
|
||||
|
||||
@@ -65,7 +65,6 @@ namespace openspace {
|
||||
|
||||
unique_ptr<ProgramObject> _programObject;
|
||||
shared_ptr<Geometry> _geometry;
|
||||
shared_ptr<FrustrumCuller> _frustrumCuller;
|
||||
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user