Skip to content

Fix near clipping distance calculation considering terrain elevation #248

Open
@EMaksymenko

Description

@EMaksymenko

To avoid camera falling to the ground it is required to correct GL near clip distance calculation taking elevation in camera position into account.

First of all, calculate near clip distance taking terrain elevation at camera point into account in WorldWindow:

 @Override
    protected void computeViewingTransform(Matrix4 projection, Matrix4 modelview) {
        double eyeHorizon = this.globe.horizonDistance(this.navigator.getAltitude());
        double atmosphereHorizon = this.globe.horizonDistance(160000);

        // The far distance is set to the smallest value that does not clip the atmosphere.
        double far = eyeHorizon + atmosphereHorizon;
        if (far < 1e3) far = 1e3;

        //The near distance is set to a large value that does not clip the globe's surface.
        double maxDepthValue = (1L << this.depthBits) - 1L;
        double farResolution = 10.0;
        double near = far / (maxDepthValue / (1 - farResolution / far) - maxDepthValue + 1);

        // Prevent the near clip plane from intersecting the terrain.
        double distanceToSurface = this.navigator.getAltitude() - this.globe.getElevationAtLocation(this.navigator.getLatitude(), this.navigator.getLongitude()) * this.getVerticalExaggeration();
        if (distanceToSurface > 0) {
            double tanHalfFov = Math.tan(0.5 * Math.toRadians(this.fieldOfView));
            double maxNearDistance = distanceToSurface / (2 * Math.sqrt(2 * tanHalfFov * tanHalfFov + 1));
            if (near > maxNearDistance) near = maxNearDistance;
        }

        if (near < 1) near = 1;

        // Compute a perspective projection matrix given the WorldWindow's viewport, field of view, and clip distances.
        projection.setToPerspectiveProjection(this.viewport.width, this.viewport.height, this.fieldOfView, near, far);

        // Compute a Cartesian transform matrix from the Navigator.
        this.navigator.getAsViewingMatrix(this.globe, modelview);
    }

Terrain elevation determination in Globe should be also added:

    public double getElevationAtLocation(double latitude, double longitude) {
        Sector sector = Sector.fromDegrees(latitude, longitude, 1E-15, 1E-15); // 1E-15 is used because sector can not have 0 with and height
        float[] heights = new float[1];
        this.getElevationModel().getHeightGrid(sector, 1, 1, heights);
        return heights[0];
    }

Then limit navigator movements to avoid passing below the ground:

    @Override
    protected Camera lookAtToCamera(Globe globe, LookAt lookAt, Camera result) {
        Camera camera = super.lookAtToCamera(globe, lookAt, result);
        // Check if camera altitude is not under the surface
        double elevation = wwd.getElevation(camera.latitude, camera.longitude) * wwd.getVerticalExaggeration() + COLLISION_THRESHOLD;
        if(elevation > camera.altitude) {
            // Set camera altitude above the surface
            camera.altitude = elevation;
            // Compute new camera point
            globe.geographicToCartesian(camera.latitude, camera.longitude, camera.altitude, originPoint);
            // Compute look at point
            globe.geographicToCartesian(lookAt.latitude, lookAt.longitude, lookAt.altitude, forwardRay.origin);
            // Compute normal to globe in look at point
            globe.geographicToCartesianNormal(lookAt.latitude, lookAt.longitude, forwardRay.direction);
            // Calculate tilt angle between new camera point and look at point
            originPoint.subtract(forwardRay.origin).normalize();
            double dot = forwardRay.direction.dot(originPoint);
            if (dot >= -1 || dot <= 1) {
                camera.tilt = Math.toDegrees(Math.acos(dot));
            }
        }
        return camera;
    }

private final static double COLLISION_THRESHOLD = 10.0; // 10m above surface

WorldWindEarth#10

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions