Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix near clipping distance calculation considering terrain elevation #248

Open
ComBatVision opened this issue Oct 28, 2021 · 0 comments
Open

Comments

@ComBatVision
Copy link

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

1 participant