Validating Object Visibility Using Three.js and Sphere Intersection

I have a spherical globe adorned with pins on its surface, each pin representing an object and labeled accordingly with DOM elements. These labels are calculated based on the positioning of the pin in the 2D world.

The issue arises when the pins move behind the globe either through mouse dragging or animation. In such cases, I need to ensure that the corresponding labels in the DOM are hidden, making them invisible without their associated pins.

To tackle this problem, my approach involves determining whether a pin in the 3D world is positioned behind the globe so I can then hide its label.

For a comprehensive view of the code, visit Codepen.

Here is the function I have been exploring:

function checkPinVisibility() {

    var startPoint = camera.position.clone();

    for (var i = 0; i < pins.length; i++) {

        var direction = pins[i].position.clone();
        var directionVector = direction.sub(startPoint);

        raycaster.set(startPoint, directionVector.clone().normalize());

        var intersects = raycaster.intersectObject(pins[i]);

        if (intersects.length > 0) {
            // ?
        }

    }
}

Despite delving into multiple resources, including:

  • ThreeJS: How to detect if an object is rendered/visible
  • Three.js - How to check if an object is visible to the camera

I have managed to make it work using mouse XY position as a ray, but a sustainable solution for continuous rendering of all pins still eludes me.

Answer №1

Have you ever wondered which points on the surface of a sphere are within view of a camera?

Picture a line extending from the camera that just touches the sphere. Let's call the length of this line L.

The camera has visibility only to those points on the sphere that lie closer to it than L.

To calculate L, use the formula L = sqrt( D^2 - R^2 ), where D represents the distance between the camera and the center of the sphere, and R is the radius of the sphere.

Answer №2

WestLangley's coding solution is presented here. If you find it to be the most suitable answer, consider marking it as accepted.

function checkPinVisibility() {
    var cameraToEarth = earth.position.clone().sub(camera.position);
    var L = Math.sqrt(Math.pow(cameraToEarth.length(), 2) - Math.pow(earthGeometry.parameters.radius, 2));

    for (var i = 0; i < pins.length; i++) { 

        var cameraToPin = pins[i].position.clone().sub(camera.position);

        if(cameraToPin.length() > L) { 
            pins[i].domlabel.style.visibility = "hidden";
        } else { 
            pins[i].domlabel.style.visibility = "visible";
        }
    }
}

Surprisingly, this method still exhibits susceptibility to a camera pan error. The situation is peculiar, but it remains superior to my Projection-onto-LOOKAT approach.

MY PREVIOUS ANSWER:

I initially thought it might look something like this, yet its functionality didn't meet expectations.

if (intersects.length > 0) {
       pins[i].domlabel.style.visibility = "visible";
   } else {
       pins[i].domlabel.style.visibility = "hidden";
   }

This solution brought me closer to the desired outcome, albeit not flawlessly. The code snippet below calculates the distance along the LOOKAT direction from the camera to a pin (cameraToPinProjection), comparing it against the distance along said direction to the earth (cameraToEarthProjection). If cameraToPinProjection > cameraToEarthProjection, it indicates that the pin lies beyond the Earth's center along the LOOKAT line, prompting me to hide it.

Notice the factor "0.8" used in multiplying the cameraToEarth projection - it adjusts its length slightly. Experiment with it for variations.

The imperfection arises when rotating the Earth around, causing labels to behave unexpectedly. I am uncertain about resolving this issue.

I trust this information proves helpful.

function checkPinVisibility() {
    var LOOKAT = new THREE.Vector3( 0, 0, -1 );
    LOOKAT.applyQuaternion( camera.quaternion );

    var cameraToEarth = earth.position.clone().sub(camera.position);
    var angleToEarth = LOOKAT.angleTo(cameraToEarth);

    var cameraToEarthProjection = LOOKAT.clone().normalize().multiplyScalar(0.8 * cameraToEarth.length() * Math.cos(angleToEarth));

    var startPoint = camera.position.clone();

    for (var i = 0; i < pins.length; i++) {

        var cameraToPin = pins[i].position.clone().sub(camera.position);
        var angleToPin = LOOKAT.angleTo(cameraToPin);

        var cameraToPinProjection = LOOKAT.clone().normalize().multiplyScalar(cameraToPin.length() * Math.cos(angleToPin));

        if(cameraToPinProjection.length() > cameraToEarthProjection.length()) {
            pins[i].domlabel.style.visibility = "hidden";
        } else { 
            pins[i].domlabel.style.visibility = "visible";
        }

    }
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the best way to manage the loading and unloading of JavaScript within dynamically loaded partial pages?

Utilizing jQuery and history.js, I manage seamless transitions between partial pages to prevent entire document reloads. Some of these partial pages include unique javascript files. Despite successful page transitions, remnants of executed javascript linge ...

How can I adjust the vertical position of Material-UI Popper element using the popper.js library?

https://i.stack.imgur.com/ZUYa4.png Utilizing a material-ui (v 4.9.5) Popper for a pop-out menu similar to the one shown above has been my recent project. The anchorElement is set as the chosen ListItem on the left side. My goal is to have the Popper alig ...

Getting the string value from a table row using JavaScript

I need to capture the value of result_status from the row labeled status. If all values in the row labeled status are 'pass', then the result_status will also be 'pass'. However, if any one of the values in the row labeled status is &a ...

Model is updated by checkbox only on the second click

Take a look at this Plunkr example here: http://plnkr.co/edit/hwVL3xtnD9hGJL?p=preview After clicking the checkbox for the first time, the model fails to update. Can you explain why? var app = angular.module('main', ['ngMaterial']) ...

Retrieving a compilation of items found within text selected by the user on a website

Imagine a scenario in which a webpage contains the following structure: <p> <span class="1">Here's some text</span> <span class="2">that the user</span> <span class="3">could select.</span> </p> I ...

PHP: AJAX request triggers 500 Internal Server Error

I currently have a dynamic MySQL database that receives regular updates from an AI-generated text. My goal is to display this continuously updating text on a web platform. In order to achieve this, we conducted a simple test as shown below: <head> ...

Leveraging AJAX responses to reduce additional requests in single-page applications

I am currently developing an ecommerce app using the MEAN stack and have encountered a recurring question regarding AJAX http requests. The single-page architecture integral to MEAN makes this question particularly significant. I have come across advice s ...

Issues encountered with anchor scrolling upon clicking

Hey there, I'm currently working on setting up a contact page that features two forms with a parallax effect. One thing I want to incorporate is an anchor that smoothly scrolls down to the next form when clicked. Despite trying out numerous code snipp ...

Tips for implementing FontAwesome in Nuxt3

I'm facing some issues trying to implement FontAwesome in my NuxtJS project, and for some unknown reasons, it's not working as expected. Let's take a look at my package.json: { "private": true, "scripts": { " ...

Aggregating and organizing all TypeScript files within the project while preserving the file hierarchy

Looking to utilize a task runner such as Grunt or Gulp to compile TS to JS files in various locations within the project folder. The goal is to ensure that the outputted JS files are placed in the same directory where the project will look for them alongsi ...

Combining two geometries with indexes into a BufferGeometry

Currently, I am utilizing a fixed set of data that contains indices, vertices, and colors, along with multiple instances of THREE.Geometry to populate a scene with objects. This process is quite slow, as it involves adding and removing numerous objects at ...

Hold on for the querySelectorAll function

I've been working on an app using the Ionic Framework. Recently, I made the switch from Ionic 3 to Ionic 4 and encountered an issue with hyperlinks not working properly anymore. The dynamic loading of HTML content based on the selected page is causing ...

When I request the value of a JavaScript object, I am met with 'undefined'

I have been working on creating a Google map application that involves loading markers from a database and displaying them on a map. To accomplish this, I decided to create an array and define an object as shown below: function shop_info(name, latitude, l ...

The process of matching the full names of the source and destination Strings in Node.js

Need assistance comparing two strings with a third string in a JSON Array for full names var source = intentObj.slots.toPlazaName.value.toString(); // Jaipur var destination = intentObj.slots.fromPlazaName.value.toString(); // Kishangarh Compare with t ...

Is there a way to successfully integrate a JavaScript file that has been downloaded from `npm` or `yarn` into a web client or

Currently, I am following a guide titled "Headless Drupal with React" on Medium. The tutorial itself does not address my specific questions. In the tutorial, it demonstrates importing React and ReactDOM directly from CDN in the .html file. My query revolv ...

How can I optimize the performance of JS-heavy pages on mobile devices?

As a website owner, I strive to optimize the performance of my site on mobile devices without the need for a separate "mobile-friendly" version or replacing large sections of code. With around 100K of JS code, including jQuery, I am determined to enhance b ...

Fixing Firebase and React errors in JavaScript functions

Thank you for your understanding. I am currently integrating Firebase into my website. However, when I invoke the signup function in FormUp.js (which is declared in AuthContext.js), it does not reference the function definition. As a result, the function c ...

creating divs inside a parent div with varying heights and using auto margins

Can anyone help me with this code snippet? <div style="position: relative;"> /***main***/ <div style="top:0"> /*****Content1****/ </div> <div> /*****Content2****/ </div> <div> ...

What is the best way to update the video source upon clicking a link with JQuery?

Is there a way to change the video src using jQuery when a link is clicked? For example, if the user clicks on "#staff", the video src will be updated. <ul class="nav nav-pills" > <li role="presentation" class="loginPills"><a ...

Making sure the checkbox stays selected in an angular environment

After experimenting with Angular 9 and a custom input, I achieved the following result => https://stackblitz.com/edit/angular-ivy-rgsatp My goal was to prevent users from disabling a radio button that is currently checked. Therefore, I made changes in ...