Skip to content

Commit b053e7e

Browse files
committed
added per tile shrunk-ellipsoid horizon culling
1 parent 63e97a5 commit b053e7e

8 files changed

+174
-31
lines changed

Source/Core/EllipsoidalOccluder.js

+144-10
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import Check from './Check.js';
44
import defaultValue from './defaultValue.js';
55
import defined from './defined.js';
66
import defineProperties from './defineProperties.js';
7+
import Ellipsoid from './Ellipsoid.js';
78
import Rectangle from './Rectangle.js';
89

910
/**
@@ -116,16 +117,40 @@ import Rectangle from './Rectangle.js';
116117
* occluder.isScaledSpacePointVisible(scaledSpacePoint); //returns true
117118
*/
118119
EllipsoidalOccluder.prototype.isScaledSpacePointVisible = function(occludeeScaledSpacePosition) {
119-
// See https://cesium.com/blog/2013/04/25/Horizon-culling/
120-
var cv = this._cameraPositionInScaledSpace;
121-
var vhMagnitudeSquared = this._distanceToLimbInScaledSpaceSquared;
122-
var vt = Cartesian3.subtract(occludeeScaledSpacePosition, cv, scratchCartesian);
123-
var vtDotVc = -Cartesian3.dot(vt, cv);
124-
// If vhMagnitudeSquared < 0 then we are below the surface of the ellipsoid and
125-
// in this case, set the culling plane to be on V.
126-
var isOccluded = vhMagnitudeSquared < 0 ? vtDotVc > 0 : (vtDotVc > vhMagnitudeSquared &&
127-
vtDotVc * vtDotVc / Cartesian3.magnitudeSquared(vt) > vhMagnitudeSquared);
128-
return !isOccluded;
120+
return isScaledSpacePointVisible(occludeeScaledSpacePosition, this._cameraPositionInScaledSpace, this._distanceToLimbInScaledSpaceSquared);
121+
};
122+
123+
var scratchEllipsoidShrunkRadii = new Cartesian3();
124+
var scratchEllipsoidShrunk = Ellipsoid.clone(Ellipsoid.UNIT_SPHERE);
125+
var scratchCameraPositionInScaledSpaceShrunk = new Cartesian3();
126+
127+
/**
128+
* Similar to {@link EllipsoidalOccluder#isScaledSpacePointVisible} except tests against an
129+
* ellipsoid that has been shrunk by the minimum height when the minimum height is below
130+
* the ellipsoid. This is intended to be used with points generated by
131+
* {@link EllipsoidalOccluder#computeHorizonCullingPointPossiblyUnderEllipsoid},
132+
* {@link EllipsoidalOccluder#computeHorizonCullingPointFromVerticesPossiblyUnderEllipsoid}, or
133+
* {@link EllipsoidalOccluder#computeHorizonCullingPointFromRectanglePossiblyUnderEllipsoid}.
134+
*
135+
* @param {Cartesian3} occludeeScaledSpacePosition The point to test for visibility, represented in the scaled space of the possibly-shrunk ellipsoid.
136+
* @returns {Boolean} <code>true</code> if the occludee is visible; otherwise <code>false</code>.
137+
*/
138+
EllipsoidalOccluder.prototype.isScaledSpacePointVisiblePossiblyUnderEllipsoid = function(occludeeScaledSpacePosition, minimumHeight) {
139+
var ellipsoid = this._ellipsoid;
140+
if (defined(minimumHeight) && minimumHeight < 0.0 && ellipsoid.minimumRadius > -minimumHeight) {
141+
var ellipsoidShrunkRadii = Cartesian3.fromElements(
142+
ellipsoid.radii.x + minimumHeight,
143+
ellipsoid.radii.y + minimumHeight,
144+
ellipsoid.radii.z + minimumHeight,
145+
scratchEllipsoidShrunkRadii
146+
);
147+
var ellipsoidShrunk = Ellipsoid.fromCartesian3(ellipsoidShrunkRadii, scratchEllipsoidShrunk);
148+
var cameraPositionInScaledSpaceShrunk = ellipsoidShrunk.transformPositionToScaledSpace(this._cameraPosition, scratchCameraPositionInScaledSpaceShrunk);
149+
var distanceToLimbinScaledSpaceSquaredShrunk = Cartesian3.magnitudeSquared(cameraPositionInScaledSpaceShrunk) - 1.0;
150+
return isScaledSpacePointVisible(occludeeScaledSpacePosition, cameraPositionInScaledSpaceShrunk, distanceToLimbinScaledSpaceSquaredShrunk);
151+
}
152+
153+
return isScaledSpacePointVisible(occludeeScaledSpacePosition, this._cameraPositionInScaledSpace, this._distanceToLimbInScaledSpaceSquared);
129154
};
130155

131156
/**
@@ -167,6 +192,30 @@ import Rectangle from './Rectangle.js';
167192
return magnitudeToPoint(scaledSpaceDirectionToPoint, resultMagnitude, result);
168193
};
169194

195+
/**
196+
* Similar to {@link EllipsoidalOccluder#computeHorizonCullingPoint} except computes the culling
197+
* point relative to an ellipsoid that has been shrunk by the minimum height when the minimum height is below
198+
* the ellipsoid. The returned point is expressed in the possibly-shrunk ellipsoid-scaled space and is suitable
199+
* for use with {@link EllipsoidalOccluder#isScaledSpacePointVisiblePossiblyUnderEllipsoid}.
200+
*
201+
* @param {Cartesian3} directionToPoint The direction that the computed point will lie along.
202+
* A reasonable direction to use is the direction from the center of the ellipsoid to
203+
* the center of the bounding sphere computed from the positions. The direction need not
204+
* be normalized.
205+
* @param {Cartesian3[]} positions The positions from which to compute the horizon culling point. The positions
206+
* must be expressed in a reference frame centered at the ellipsoid and aligned with the
207+
* ellipsoid's axes.
208+
* @param {Number} [minimumHeight] The minimum height of all positions. If this value is undefined, all positions are assumed to be above the ellipsoid.
209+
* @param {Cartesian3} [result] The instance on which to store the result instead of allocating a new instance.
210+
* @returns {Cartesian3} The computed horizon culling point, expressed in the possibly-shrunk ellipsoid-scaled space.
211+
*/
212+
EllipsoidalOccluder.prototype.computeHorizonCullingPointPossiblyUnderEllipsoid = function(directionToPoint, positions, minimumHeight, result) {
213+
var that = this;
214+
return computeHorizonCullingPointPossiblyUnderEllipsoid(that, minimumHeight, function() {
215+
return that.computeHorizonCullingPoint(directionToPoint, positions, result);
216+
});
217+
};
218+
170219
var positionScratch = new Cartesian3();
171220

172221
/**
@@ -215,6 +264,32 @@ import Rectangle from './Rectangle.js';
215264
return magnitudeToPoint(scaledSpaceDirectionToPoint, resultMagnitude, result);
216265
};
217266

267+
/**
268+
* Similar to {@link EllipsoidalOccluder#computeHorizonCullingPointFromVertices} except computes the culling
269+
* point relative to an ellipsoid that has been shrunk by the minimum height when the minimum height is below
270+
* the ellipsoid. The returned point is expressed in the possibly-shrunk ellipsoid-scaled space and is suitable
271+
* for use with {@link EllipsoidalOccluder#isScaledSpacePointVisiblePossiblyUnderEllipsoid}.
272+
*
273+
* @param {Cartesian3} directionToPoint The direction that the computed point will lie along.
274+
* A reasonable direction to use is the direction from the center of the ellipsoid to
275+
* the center of the bounding sphere computed from the positions. The direction need not
276+
* be normalized.
277+
* @param {Number[]} vertices The vertices from which to compute the horizon culling point. The positions
278+
* must be expressed in a reference frame centered at the ellipsoid and aligned with the
279+
* ellipsoid's axes.
280+
* @param {Number} [stride=3]
281+
* @param {Cartesian3} [center=Cartesian3.ZERO]
282+
* @param {Number} [minimumHeight] The minimum height of all vertices. If this value is undefined, all vertices are assumed to be above the ellipsoid.
283+
* @param {Cartesian3} [result] The instance on which to store the result instead of allocating a new instance.
284+
* @returns {Cartesian3} The computed horizon culling point, expressed in the possibly-shrunk ellipsoid-scaled space.
285+
*/
286+
EllipsoidalOccluder.prototype.computeHorizonCullingPointFromVerticesPossiblyUnderEllipsoid = function(directionToPoint, vertices, stride, center, minimumHeight, result) {
287+
var that = this;
288+
return computeHorizonCullingPointPossiblyUnderEllipsoid(that, minimumHeight, function() {
289+
return that.computeHorizonCullingPointFromVertices(directionToPoint, vertices, stride, center, result);
290+
});
291+
};
292+
218293
var subsampleScratch = [];
219294

220295
/**
@@ -246,6 +321,65 @@ import Rectangle from './Rectangle.js';
246321
return this.computeHorizonCullingPoint(bs.center, positions, result);
247322
};
248323

324+
/**
325+
* Similar to {@link EllipsoidalOccluder#computeHorizonCullingPointFromRectangle} except computes the culling
326+
* point relative to an ellipsoid that has been shrunk by the height when the height is below
327+
* the ellipsoid. The returned point is expressed in the possibly-shrunk ellipsoid-scaled space and is suitable
328+
* for use with {@link EllipsoidalOccluder#isScaledSpacePointVisiblePossiblyUnderEllipsoid}.
329+
*
330+
* @param {Rectangle} rectangle The rectangle for which to compute the horizon culling point.
331+
* @param {Ellipsoid} ellipsoid The ellipsoid on which the rectangle is defined. This may be different from
332+
* the ellipsoid used by this instance for occlusion testing.
333+
* @param {Number} [height] The height of the rectangle. If this value is undefined, the rectangle is assumed to be on the occluder's ellipsoid.
334+
* @param {Cartesian3} [result] The instance on which to store the result instead of allocating a new instance.
335+
* @returns {Cartesian3} The computed horizon culling point, expressed in the possibly-shrunk ellipsoid-scaled space.
336+
*/
337+
EllipsoidalOccluder.prototype.computeHorizonCullingPointFromRectanglePossiblyUnderEllipsoid = function(rectangle, ellipsoid, height, result) {
338+
var that = this;
339+
return computeHorizonCullingPointPossiblyUnderEllipsoid(that, height, function() {
340+
return that.computeHorizonCullingPointFromRectangle(rectangle, ellipsoid, result);
341+
});
342+
};
343+
344+
var scratchEllipsoidShrunkRadii2 = new Cartesian3();
345+
var scratchEllipsoidShrunk2 = Ellipsoid.clone(Ellipsoid.UNIT_SPHERE);
346+
347+
function computeHorizonCullingPointPossiblyUnderEllipsoid(ellipsoidalOccluder, minimumHeight, func) {
348+
var ellipsoid = ellipsoidalOccluder._ellipsoid;
349+
350+
if (defined(minimumHeight) && minimumHeight < 0.0 && ellipsoid.minimumRadius > -minimumHeight) {
351+
var ellipsoidShrunkRadii = Cartesian3.fromElements(
352+
ellipsoid.radii.x + minimumHeight,
353+
ellipsoid.radii.y + minimumHeight,
354+
ellipsoid.radii.z + minimumHeight,
355+
scratchEllipsoidShrunkRadii2
356+
);
357+
var ellipsoidShrunk = Ellipsoid.fromCartesian3(ellipsoidShrunkRadii, scratchEllipsoidShrunk2);
358+
359+
// The horizon culling point is calculated with respect to the shrunk ellipsoid, so
360+
// temporarily change change the occluder's ellipsoid to the shrunk ellipsoid.
361+
ellipsoidalOccluder._ellipsoid = ellipsoidShrunk;
362+
var result = func();
363+
ellipsoidalOccluder._ellipsoid = ellipsoid;
364+
return result;
365+
}
366+
367+
return func();
368+
}
369+
370+
function isScaledSpacePointVisible(occludeeScaledSpacePosition, cameraPositionInScaledSpace, distanceToLimbInScaledSpaceSquared) {
371+
// See https://cesium.com/blog/2013/04/25/Horizon-culling/
372+
var cv = cameraPositionInScaledSpace;
373+
var vhMagnitudeSquared = distanceToLimbInScaledSpaceSquared;
374+
var vt = Cartesian3.subtract(occludeeScaledSpacePosition, cv, scratchCartesian);
375+
var vtDotVc = -Cartesian3.dot(vt, cv);
376+
// If vhMagnitudeSquared < 0 then we are below the surface of the ellipsoid and
377+
// in this case, set the culling plane to be on V.
378+
var isOccluded = vhMagnitudeSquared < 0 ? vtDotVc > 0 : (vtDotVc > vhMagnitudeSquared &&
379+
vtDotVc * vtDotVc / Cartesian3.magnitudeSquared(vt) > vhMagnitudeSquared);
380+
return !isOccluded;
381+
}
382+
249383
var scaledSpaceScratch = new Cartesian3();
250384
var directionScratch = new Cartesian3();
251385

Source/Core/HeightmapTessellator.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -396,7 +396,7 @@ import WebMercatorProjection from './WebMercatorProjection.js';
396396
var occludeePointInScaledSpace;
397397
if (hasRelativeToCenter) {
398398
var occluder = new EllipsoidalOccluder(ellipsoid);
399-
occludeePointInScaledSpace = occluder.computeHorizonCullingPoint(relativeToCenter, positions);
399+
occludeePointInScaledSpace = occluder.computeHorizonCullingPointPossiblyUnderEllipsoid(relativeToCenter, positions, minimumHeight);
400400
}
401401

402402
var aaBox = new AxisAlignedBoundingBox(minimum, maximum, relativeToCenter);

Source/Core/QuantizedMeshTerrainData.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ import TerrainMesh from './TerrainMesh.js';
310310
var maximumHeight = result.maximumHeight;
311311
var boundingSphere = defaultValue(BoundingSphere.clone(result.boundingSphere), that._boundingSphere);
312312
var obb = defaultValue(OrientedBoundingBox.clone(result.orientedBoundingBox), that._orientedBoundingBox);
313-
var occlusionPoint = Cartesian3.clone(that._horizonOcclusionPoint);
313+
var occludeePointInScaledSpace = defaultValue(Cartesian3.clone(result.occludeePointInScaledSpace), that._horizonOcclusionPoint);
314314
var stride = result.vertexStride;
315315
var terrainEncoding = TerrainEncoding.clone(result.encoding);
316316

@@ -326,7 +326,7 @@ import TerrainMesh from './TerrainMesh.js';
326326
minimumHeight,
327327
maximumHeight,
328328
boundingSphere,
329-
occlusionPoint,
329+
occludeePointInScaledSpace,
330330
stride,
331331
obb,
332332
terrainEncoding,

Source/Scene/GlobeSurfaceTileProvider.js

+8-8
Original file line numberDiff line numberDiff line change
@@ -599,7 +599,7 @@ import TileSelectionResult from './TileSelectionResult.js';
599599
return intersection;
600600
}
601601

602-
if (occluders.ellipsoid.isScaledSpacePointVisible(occludeePointInScaledSpace)) {
602+
if (occluders.ellipsoid.isScaledSpacePointVisiblePossiblyUnderEllipsoid(occludeePointInScaledSpace, tileBoundingRegion.minimumHeight)) {
603603
return intersection;
604604
}
605605

@@ -802,17 +802,17 @@ import TileSelectionResult from './TileSelectionResult.js';
802802

803803
var cornerPositionsScratch = [new Cartesian3(), new Cartesian3(), new Cartesian3(), new Cartesian3()];
804804

805-
function computeOccludeePoint(tileProvider, center, rectangle, height, result) {
805+
function computeOccludeePoint(tileProvider, center, rectangle, minimumHeight, maximumHeight, result) {
806806
var ellipsoidalOccluder = tileProvider.quadtree._occluders.ellipsoid;
807807
var ellipsoid = ellipsoidalOccluder.ellipsoid;
808808

809809
var cornerPositions = cornerPositionsScratch;
810-
Cartesian3.fromRadians(rectangle.west, rectangle.south, height, ellipsoid, cornerPositions[0]);
811-
Cartesian3.fromRadians(rectangle.east, rectangle.south, height, ellipsoid, cornerPositions[1]);
812-
Cartesian3.fromRadians(rectangle.west, rectangle.north, height, ellipsoid, cornerPositions[2]);
813-
Cartesian3.fromRadians(rectangle.east, rectangle.north, height, ellipsoid, cornerPositions[3]);
810+
Cartesian3.fromRadians(rectangle.west, rectangle.south, maximumHeight, ellipsoid, cornerPositions[0]);
811+
Cartesian3.fromRadians(rectangle.east, rectangle.south, maximumHeight, ellipsoid, cornerPositions[1]);
812+
Cartesian3.fromRadians(rectangle.west, rectangle.north, maximumHeight, ellipsoid, cornerPositions[2]);
813+
Cartesian3.fromRadians(rectangle.east, rectangle.north, maximumHeight, ellipsoid, cornerPositions[3]);
814814

815-
return ellipsoidalOccluder.computeHorizonCullingPoint(center, cornerPositions, result);
815+
return ellipsoidalOccluder.computeHorizonCullingPointPossiblyUnderEllipsoid(center, cornerPositions, minimumHeight, result);
816816
}
817817

818818
/**
@@ -860,7 +860,7 @@ import TileSelectionResult from './TileSelectionResult.js';
860860
tile.tilingScheme.ellipsoid,
861861
surfaceTile.orientedBoundingBox);
862862

863-
surfaceTile.occludeePointInScaledSpace = computeOccludeePoint(this, surfaceTile.orientedBoundingBox.center, tile.rectangle, tileBoundingRegion.maximumHeight, surfaceTile.occludeePointInScaledSpace);
863+
surfaceTile.occludeePointInScaledSpace = computeOccludeePoint(this, surfaceTile.orientedBoundingBox.center, tile.rectangle, tileBoundingRegion.minimumHeight, tileBoundingRegion.maximumHeight, surfaceTile.occludeePointInScaledSpace);
864864
}
865865
}
866866

Source/Scene/TerrainFillMesh.js

+7-7
Original file line numberDiff line numberDiff line change
@@ -655,7 +655,7 @@ import TileSelectionResult from './TileSelectionResult.js';
655655
minimumHeight,
656656
maximumHeight,
657657
BoundingSphere.fromOrientedBoundingBox(obb),
658-
computeOccludeePoint(tileProvider, obb.center, rectangle, maximumHeight),
658+
computeOccludeePoint(tileProvider, obb.center, rectangle, minimumHeight, maximumHeight),
659659
encoding.getStride(),
660660
obb,
661661
encoding,
@@ -1183,16 +1183,16 @@ import TileSelectionResult from './TileSelectionResult.js';
11831183

11841184
var cornerPositionsScratch = [new Cartesian3(), new Cartesian3(), new Cartesian3(), new Cartesian3()];
11851185

1186-
function computeOccludeePoint(tileProvider, center, rectangle, height, result) {
1186+
function computeOccludeePoint(tileProvider, center, rectangle, minimumHeight, maximumHeight, result) {
11871187
var ellipsoidalOccluder = tileProvider.quadtree._occluders.ellipsoid;
11881188
var ellipsoid = ellipsoidalOccluder.ellipsoid;
11891189

11901190
var cornerPositions = cornerPositionsScratch;
1191-
Cartesian3.fromRadians(rectangle.west, rectangle.south, height, ellipsoid, cornerPositions[0]);
1192-
Cartesian3.fromRadians(rectangle.east, rectangle.south, height, ellipsoid, cornerPositions[1]);
1193-
Cartesian3.fromRadians(rectangle.west, rectangle.north, height, ellipsoid, cornerPositions[2]);
1194-
Cartesian3.fromRadians(rectangle.east, rectangle.north, height, ellipsoid, cornerPositions[3]);
1191+
Cartesian3.fromRadians(rectangle.west, rectangle.south, maximumHeight, ellipsoid, cornerPositions[0]);
1192+
Cartesian3.fromRadians(rectangle.east, rectangle.south, maximumHeight, ellipsoid, cornerPositions[1]);
1193+
Cartesian3.fromRadians(rectangle.west, rectangle.north, maximumHeight, ellipsoid, cornerPositions[2]);
1194+
Cartesian3.fromRadians(rectangle.east, rectangle.north, maximumHeight, ellipsoid, cornerPositions[3]);
11951195

1196-
return ellipsoidalOccluder.computeHorizonCullingPoint(center, cornerPositions, result);
1196+
return ellipsoidalOccluder.computeHorizonCullingPointPossiblyUnderEllipsoid(center, cornerPositions, minimumHeight, result);
11971197
}
11981198
export default TerrainFillMesh;

Source/WorkersES6/createVerticesFromGoogleEarthEnterpriseBuffer.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,7 @@ import createTaskProcessorWorker from './createTaskProcessorWorker.js';
372372
}
373373

374374
var occluder = new EllipsoidalOccluder(ellipsoid);
375-
var occludeePointInScaledSpace = occluder.computeHorizonCullingPoint(relativeToCenter, positions);
375+
var occludeePointInScaledSpace = occluder.computeHorizonCullingPointPossiblyUnderEllipsoid(relativeToCenter, positions, minHeight);
376376

377377
var aaBox = new AxisAlignedBoundingBox(minimum, maximum, relativeToCenter);
378378
var encoding = new TerrainEncoding(aaBox, skirtOptions.hMin, maxHeight, fromENU, false, includeWebMercatorT);

0 commit comments

Comments
 (0)