7
votes

THREE.js Raycasting très lent contre un seul objet poly (faces)> 500k, intersection de ligne avec globe

dans mon projet, un joueur parcourt un globe. Le globe n'est pas seulement une sphère, il a des montagnes et des vallées, donc j'ai besoin que la position des joueurs change. Pour cela, je diffuse un rayon de la position du joueur contre un seul objet (le globe) et j'obtiens le point où ils se croisent et change la position des joueurs en conséquence. Je ne fais du raycast que lorsque le joueur bouge, pas sur chaque image.

Pour un objet complexe, cela prend une éternité. Cela prend ~ 200ms pour un objet avec ~ 1m polys (faces) (sphère de 1024x512 segments). Est-ce que le raycasting est lancé contre chaque visage?

Existe-t-il un moyen traditionnel rapide pour y parvenir en TROIS, comme une structure d'accélération (octree? Bvh? - tbh de mes recherches Google que je ne semble pas trouver une telle chose incluse dans THREE) ou une autre méthode de réflexion (pas de diffusion de rayon)?

        var dir = g_Game.earthPosition.clone();
        var startPoint = g_Game.cubePlayer.position.clone();
        var directionVector = dir.sub(startPoint.multiplyScalar(10));
        g_Game.raycaster.set(startPoint, directionVector.clone().normalize());
        var t1 = new Date().getTime();
        var rayIntersects = g_Game.raycaster.intersectObject(g_Game.earth, true);
        if (rayIntersects[0]) {
            var dist = rayIntersects[0].point.distanceTo(g_Game.earthPosition);
            dist = Math.round(dist * 100 + Number.EPSILON) / 100;
            g_Player.DistanceFromCenter = dist + 5;
        }
        var t2 = new Date().getTime();
        console.log(t2-t1);

Merci d'avance


0 commentaires

3 Réponses :


8
votes

Pour un objet complexe, cela prend une éternité. Cela prend ~ 200ms pour un objet avec ~ 1m polys (faces) (sphère de 1024x512 segments). Le raycasting se joue-t-il sur chaque visage?

Hors de la boîte THREE.js vérifie chaque triangle lors de l'exécution d'un raycast contre un maillage et il n'y a pas de structures d'accélération intégrées dans THREE.

J'ai travaillé avec d'autres sur le package three-mesh-bvh ( github , npm ) pour aider à résoudre ce problème, ce qui peut vous aider à atteindre les vitesses que vous recherchez. Voici comment vous pouvez l'utiliser:

import * as THREE from 'three';
import { MeshBVH, acceleratedRaycast } from 'three-mesh-bvh';

THREE.Mesh.prototype.raycast = acceleratedRaycast;

// ... initialize the scene...

globeMesh.geometry.boundsTree = new MeshBVH(globeMesh.geometry);

// ... initialize raycaster...

// Optional. Improves the performance of the raycast
// if you only need the first collision
raycaster.firstHitOnly = true;
const intersects = raycaster.intersectObject(globeMesh, true);

// do something with the intersections

Il y a quelques mises en garde mentionnées dans le README, alors gardez-les à l'esprit (l'index de maillage est modifié, seule la BufferGeometry non animée est prise en charge, etc.) . Et il y a encore une optimisation de la mémoire qui pourrait être faite, mais il existe des options modifiables pour vous aider à régler cela.

Je serai intéressé de savoir comment cela fonctionne pour vous! N'hésitez pas à laisser des commentaires sur les problèmes sur la façon d'améliorer le package. J'espère que cela vous aidera!


0 commentaires

7
votes

Je pense que vous devriez pré-rendre la carte de hauteur de votre globe en une texture, en supposant que votre terrain n’est pas dynamique. Lisez tout cela dans un tableau typé, puis chaque fois que votre joueur se déplace, il vous suffit de projeter en arrière ses coordonnées dans cette texture, de l'interroger, de la décaler et de se multiplier et vous devriez obtenir ce dont vous avez besoin dans le temps O (1). < / p>

C'est à vous de décider comment générer cette carte de hauteur. En fait, si vous avez un globe cahoteux, vous devriez probablement commencer par la carte de hauteur en premier lieu et l'utiliser dans votre shader de vertex pour rendre le globe (la sphère d'entrée étant parfaitement lisse). Ensuite, vous pouvez utiliser la même carte de hauteur pour interroger le Z du joueur.


1 commentaires

la carte de hauteur sera soit inexacte, soit énorme (ou les deux)



9
votes

N'utilisez pas three.js Raycaster.

Pensez à Ray.js qui offre function intersectTriangle (a, b, c, backfaceCulling, cible)

Optimisations suggérées:

  1. Si le joueur commence à partir de certaines positions connues ⇒ vous devez connaître sa hauteur initiale, - pas besoin de lancer de rayons (ou simplement de faire une intersection lente à maillage complet)

  2. si le joueur se déplace avec de petits pas ⇒ le prochain raycast va très probablement croiser la même face que précédemment.

Optimisation n ° 1 - souvenez-vous du visage précédent et diffusez-le en premier.

  1. si le joueur ne saute pas ⇒ le prochain raycast va très probablement croiser la face adjacente au visage où se trouvait le joueur auparavant.

Optimisation n ° 2 - créez un cache, de sorte que, étant donné un identifiant de visage, vous puissiez récupérer les faces adjacentes en un temps O (1).

Ce cache peut être chargé à partir du fichier, si votre planète n'est pas générée en temps réel.


Donc, avec mon approche à chaque mouvement, vous faites une opération de lecture O (1) depuis le cache et raycast 1-6 faces.

Gagnez!


0 commentaires