Tips for arranging particles in three.js based on their depth?

I am still relatively new to the world of three.js, WebGL, and 3D graphics in general. My current project involves using three.js to visualize GPS track points in a 3D environment. The datasets I am working with can be quite large, containing hundreds of thousands of points, so optimizing performance is crucial.

To display these points, I utilize a `Points` object that is populated with a `BufferGeometry` containing all the necessary data. These points are added in chronological order, following the track path.

The visual representation of each point is achieved through a `PointsMaterial` with a 2D texture (sprite) depicting the point as a circle, while areas outside the circle remain transparent. Since the color of these points is dynamic, the 2D texture is dynamically drawn on a canvas.

However, a challenge arises when viewing the points along the direction of the track, where overlapping points cause artifacts. This occurs because the transparent parts of closer points are rendered above farther points:

https://i.sstatic.net/apR0c.png

Interestingly, when viewing the track from the opposite direction, rendering the points back to front resolves this issue:

https://i.sstatic.net/DEtQ1.png

I have experimented with two potential solutions to address this problem:

  • Using `alphaTest` with a value between 0 and 1 somewhat mitigates the issue. However, since some points have partial transparency based on customer requirements, there is a risk of clipping valid portions of the points. Additionally, it creates jagged edges where points overlap.

https://i.sstatic.net/TllVb.png

  • Setting `depthWrite: false` for the points material results in visually appealing renderings. Unfortunately, newer points always obstruct older ones regardless of camera orientation, leading to incorrect visuals.

https://i.sstatic.net/P2gdK.png

What would be an effective solution to render the points in correct depth order, starting with the farthest and ending with the closest?

Below are key snippets of the code implementation:

Building geometry using coordinates fetched from an XHR request:

  const particlesGeometry = new BufferGeometry();
  const vertices = [];

  for (let i = 0; i < timeline3d.points.length; i++) {
    const coordinates = timeline3d.points[i].sceneCoordinates;
    vertices.push(coordinates[0], coordinates[1], coordinates[2]);
  }

  particlesGeometry.addAttribute('position', new Float32BufferAttribute(vertices, 3));
  return new Points(particlesGeometry, buildTimelinePointsMaterial(timeline3d));

Defining the material:

function buildTimelinePointsMaterial (timeline) {
  const pointTexture = new CanvasTexture(drawPointSprite(POINT_SPRITE_RADIUS, timeline.color));

  return new PointsMaterial({
    size: POINT_SIZE,
    sizeAttenuation: true,
    map: pointTexture,
    transparent: true,
    alphaTest: 0.4
  });
}

Answer №1

Alternative Approach:

When it comes to rendering points in WebGL, there is a known limitation that can cause overlapping issues. To address this, I have found a workaround similar to what @ScieCode suggested by adjusting the material's blending mode. By changing the blending mode, each point can blend over the other like layers in Photoshop, eliminating the overlap effect. Depending on your background color, you may opt for THREE.MultiplyBlending for light backgrounds or THREE.AdditiveBlending for dark backgrounds.

Potential Solution:

For a more intricate solution, there is an example that tackles this issue by sorting all vertices by depth on a per frame basis. This approach is detailed in the example here. Examining the source code, you will notice the use of sortPoints() within the render loop. This function combines camera matrices with the geometry's world matrix to assess vertex depth and arrange them accordingly.

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

How come the useState function remains undefined, even when initialized with a default value?

I attempted to set an empty array as the default value for a state using the useState hook in React. However, when I try to check the type of testing with console.log(Array.isArray(testing)); or simply log testing, it always displays undefined regardless o ...

Rails 7: It appears that the browser is disregarding all importmap JavaScript packages

Last week I worked through two different tutorials, successfully achieving most of the tasks. However, three crucial issues remain unresolved. Upon closer inspection, it became evident that all three problems revolve around the browser seemingly disregardi ...

Azure webhosting blocks the use of AJAX calls

There is a PHP script hosted on my Azure server that returns JSON when accessed through the browser. However, I am having trouble making an AJAX call to this script as none of my requests seem to go through. The issue remains unclear. You can see a failed ...

coding with javascript and css

Essentially, I am trying to add padding specifically to the left side of this code snippet: if (xmlhttp.readyState==4 && xmlhttp.status==200) { document.getElementById("livesearch").innerHTML=xmlhttp.responseText; document.getElementById("live ...

Execute PHP script via hyperlink

Is there a way for me to have a small PHP script that only gets the current page URL and echoes it when a user clicks on a submit button? I'm struggling because there are no other form items on the page, making it difficult to trigger the script. I th ...

Creating navigation with routed URL in ASP.NET MVC

I'm having trouble with the navigation in asp.net mvc and handling URLs. For example, when you visit your profile on Facebook, the URL is facebook.com/yourusername. On your profile page, there is a menu with links such as Timeline, About, Friends, et ...

Experimenting with a function invoked from a jQuery AJAX callback using Jasmine testing framework

Currently, I'm working on a function that utilizes an AJAX call to interact with a service. My main goal is to ensure the displayError function is triggered in case of a failure. The ajaxCall function is set up to accept a URL parameter. When the req ...

Using jQuery to store the name of a Div in a variable and subsequently invoking it in a function

Recently, I've been grappling with a task involving storing a div name in a variable for easier editing and incorporating it into standard actions like show/hide. My code functions perfectly without the variables, but introducing them causes the div ...

Where to find a threejs collada model position

Recently, I have been working with the THREEJS framework to render *.dae models. As I attempt to render multiple dae models, I am encountering an issue with their location coordinates. Despite setting the location of the models using dae.position.set(0,0,0 ...

Is there a way to make sure that ngbpopovers stay open even when hovering over the popover content?

I have implemented a ngbpopover to display user information on an element. Currently, the popover is triggered on both hover and click events, but I would like it to remain open when hovered over, instead of closing as soon as the mouse moves away. How c ...

Dark opaque background image with Material-UI styling

I'm enclosing all the widgets within a CardMedia component, and adding an image. return ( <CardMedia image={bg} className={classes.bg}> <main className={classes.content}> <div className={classes.toolbar} /> <Grid contai ...

Is it possible to leverage Create-React-App's npm start in conjunction with Node.js?

I've recently started diving into React and node.js. My goal is to have a node.js server that can serve up React.js pages/views. After running 'create-react-app' and then using 'npm start', I'm not sure if I also need to man ...

Issue with Facebook request dialog continuously loading on live server

Currently working on a Facebook application that includes a feature to send multi-friend app requests. Everything functions properly on my local server, where users can tap on the multi-friend link, open a pop-up window, select friends, and send invites. H ...

The passing of React parent component props to the child component is not functioning as expected

Retrieving data through an ajax call and saving it to the state of the component _fetchDataFromServer(){ reqwest({ url: 'http://127.0.0.1:8000/api/example/' , type: 'json' , method: 'get' , contentType: &ap ...

Refresh the React state at regular intervals

constructor(){ super(); this.state={ numbers : [1,2,3,4,1,2,3,4,1,3,1,4,12,2,3,2] }; } componentDidMount(){setInterval(this.updateNumbers(),5000);} updateNumbers() { console.log(this.props.newData); let numbers = this.state.nu ...

Highlighting the menu item in AngularJS based on a randomly generated address

Here is a code snippet that I need help with: Html <li ng-class="{ highlight: isActive('/admin/trackDef/list') || isActive('/admin/trackDef/add')}"> <a href="${createLink(uri: '/#/admin/trackDef/list')}"></ ...

Efficiently accessing and displaying nested models in AngularJS

Currently, I am in the process of developing a website that involves numerous relational links between data. For instance, users have the ability to create bookings, which will include a booker and a bookee, as well as an array of messages that can be asso ...

What causes directive templates to be fetched from the server at times (resulting in a 304 response), while other times they are retrieved from the browser cache without

I've noticed that when I reload the page, Angular directive templates load in two different ways. The first way is when the browser sends a request to the server and receives a 304 response - everything works fine. However, the second way is puzzlin ...

Retrieve the selected checkboxes from the latest .change() trigger

I'm facing an issue with a basic question that I can't seem to find the right terms to research for help. The problem revolves around a .change() listener that monitors checkbox changes within a div (used to toggle Leaflet Map layers). My goal i ...

Ensure that the value in the array is verified using jQuery for errors

HTML <table id="gwForm"> <tbody> <tr data-id="1"></tr> <tr data-id="2"></tr> </tbody> </table> Script var ids = []; $("#gwForm tbody tr").each(function(index, el){ ids.push($(el).data ...