I’m working on a Three.js project where I need to render a PLY point cloud consisting of approximately 17 million points. I’m experiencing significant lag and performance issues when attempting to load and display this large dataset. While I’m aware of Potree, integrating it into my project would require substantial changes, so I’m looking for an alternative solution.
One approach I’m exploring is to use a downsampled version of the point cloud for interactions and load the full point cloud once the user stops interacting, similar to this demo I made:
https://miquelrecloud.github.io/threejs-large-ply-demo/
You can check the repo here: GitHub - MiquelRecloud/threejs-large-ply-demo
However, I’m facing two challenges that I need help with:
- Rendering Downsampled Point Cloud During Zoom: I need the downsampled point cloud to render during zoom interactions. I’m currently listening to the controls variable with start and end events, but this approach does not seem to work effectively, I’ve also tried with change. How can I ensure the downsampled point cloud is rendered properly during zoom interactions?
- Loading Full Point Cloud in the Background: The screen freezes for 3 seconds when attempting to load the full, non-downsampled version of the point cloud. Is there a way to load it in the background so that the user can continue interacting with the downsampled version? Additionally, can the full point cloud be loaded in chunks to improve performance and avoid freezing?
For context, the demo has less lag because the point cloud used there has only 4 million points due to GitHub’s file size limitations. If you want to test it with the 17 million points, you can download it here: K24AC201_17M.ply - Google Drive
Any suggestions or best practices to address these issues would be greatly appreciated. Thank you!
You could try sticking with 4M points model and try to manipulate the point material size slightly.
I have manual controls, +
and -
buttons, implemented in my PCD+XYZ Viewer and you can test it with 1 of the examples from three.js repository, like this one, just so you can see the effect of sizing.
Hey!
Thank you for your response! I’m glad to share that I managed to resolve the zoom problem by using a timer when a change in the controls is detected. For anyone facing a similar issue, here’s the code I used:
let isInteracting
const handleInteraction = () => {
if (!isInteracting) {
showDownsampled()
isInteracting = true
}
clearTimeout(isInteracting)
isInteracting = setTimeout(() => {
showFullRes()
isInteracting = false
}, 100)
}
controls.addEventListener('change', handleInteraction)
Now, the challenge I’m facing is loading the full-resolution point cloud in the background to prevent lag during the transition. Unfortunately, simply increasing the dot size won’t be sufficient due to precision requirements.
Do you have any suggestions on how to load the full point cloud in chunks or in the background to avoid freezing the interface? Any advice or techniques for this would be highly appreciated!
Thanks again for your help!
I don’t really have any specific suggestion since this is done in React (which I am not familiar with).
Maybe a use of Web Workers might help or if any other React savvy member might pitch in some other suggestion.
You would kind of need to move that second PLY load outside the main thread.
Hey!
Thank you so much for the suggestion to use Web Workers! I wasn’t aware of them before, but they turned out to be exactly what I needed. I was able to resolve the issue with loading the full-resolution point cloud in the background using Web Workers.
For anyone else facing a similar issue, here’s the solution that worked for me:
In app.py
(or your main JavaScript file):
async function loadFullResPointCloud() {
const worker = new Worker(new URL('./plyWorker.js', import.meta.url), { type: 'module' });
worker.postMessage({ fileUrl: process.env.PUBLIC_URL + '/diff_4M.ply' });
worker.onmessage = function (e) {
const { vertices, colors, normals } = e.data;
let bufferGeometry = new THREE.BufferGeometry();
bufferGeometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));
bufferGeometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3));
bufferGeometry.setAttribute('normal', new THREE.Float32BufferAttribute(normals, 3));
And here’s the code for plyWorker.js
:
// src/plyWorker.js
import * as THREE from "three";
import { PLYLoader } from 'three/examples/jsm/loaders/PLYLoader';
onmessage = async function (e) {
const { fileUrl } = e.data;
// Fetch the PLY file as a Blob
const response = await fetch(fileUrl);
const blob = await response.blob();
// Read the Blob as an ArrayBuffer using FileReader
const arrayBuffer = await new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsArrayBuffer(blob);
reader.onloadend = () => resolve(reader.result);
});
// Parse the ArrayBuffer using PLYLoader
const loader = new PLYLoader();
const bufferGeometry = loader.parse(arrayBuffer);
// Send the parsed geometry back to the main thread
const vertices = bufferGeometry.getAttribute('position').array.buffer;
const colors = bufferGeometry.getAttribute('color').array.buffer;
const normals = bufferGeometry.getAttribute('normal').array.buffer;
postMessage({ vertices, colors, normals }, [vertices, colors, normals]);
}
1 Like