THREE.js raycasting очень медленный против одного объекта > 500k poly (faces), пересечение линии с земным шаром

в моем проекте у меня есть игрок, который ходит по земному шару. Земной шар — это не просто сфера, в нем есть горы и долины, поэтому мне нужно, чтобы положение игроков по оси z менялось. Для этого я направляю один луч из положения игрока на один объект (земной шар) и получаю точку, в которой они пересекаются, и соответствующим образом меняю положение игроков. Я делаю рейкастинг только тогда, когда игрок движется, а не на каждом кадре.

Для сложного объекта это занимает вечность. Для объекта с ~1 м полигонов (граней) (сфера сегментов 1024x512) требуется ~200 мс. Использует ли raycasting каждое лицо?

Есть ли традиционный быстрый способ добиться этого в THREE, например, какая-то структура ускорения (octree? bvh? -- tbh из моих поисков в Google я, кажется, не нашел такой вещи, включенной в THREE) или какое-то другое мышление из метод коробки (без луча)?

        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.info(t2-t1);

заранее спасибо

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
7
0
1 450
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

For a complex object it takes forever. It takes ~200ms for an object with ~1m polys (faces) (1024x512 segments sphere). Does raycasting cast against every single face ?

Из коробки THREE.js делает проверяет каждый треугольник при выполнении raycast против сетки, и в THREE нет встроенных структур ускорения.

Я работал с другими над пакетом three-mesh-bvh (гитхаб, нпм), чтобы помочь решить эту проблему, тем не менее, что может помочь вам достичь скорости, которую вы ищете. Вот как вы можете его использовать:

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

В README упоминаются некоторые предостережения, поэтому имейте их в виду (индекс сетки изменен, поддерживается только неанимированная BufferGeometry и т. д.). И есть еще некоторая оптимизация памяти, которую можно выполнить, но есть некоторые настраиваемые параметры, которые помогут это настроить.

Мне будет интересно услышать, как это работает для вас! Не стесняйтесь оставлять отзывы в вопросах о том, как улучшить пакет, а также. Надеюсь, это поможет!

Ответ принят как подходящий

Я думаю, вам следует предварительно отрендерить карта высот вашего земного шара в текстуру, предполагая, что ваш ландшафт не динамичен. Считайте все это в типизированный массив, а затем всякий раз, когда ваш игрок перемещается, вам нужно только обратно спроецировать его координаты в эту текстуру, запросить ее, сместить и умножить, и вы должны получить то, что вам нужно за время O (1).

Вам решать, как создать эту карту высот. На самом деле, если у вас есть неровный земной шар, то вам, вероятно, следует начать с карты высот в первую очередь и использовать ее в своем вершинном шейдере для рендеринга земного шара (с идеально гладкой входной сферой). Затем вы можете использовать ту же карту высот для запроса Z игрока.

карта высот будет либо неточной, либо огромной (или и то, и другое)

Alex Khoroshylov 03.03.2019 08:28

Не используйте three.js Raycaster.

Рассмотрим Ray.js, который предлагает function intersectTriangle(a, b, c, backfaceCulling, target)

Предлагаемые оптимизации:

  1. Если игрок начинает с некоторых известных позиций ⇒ вы должны знать его начальную высоту, − нет необходимости в рейкасте (или просто сделайте один раз медленное пересечение с полной сеткой)

  2. если игрок движется маленькими шагами ⇒ следующий рейкаст будет вероятно пересекать ту же грань, что и раньше.

Оптимизация №1 — запомнить предыдущее лицо и сначала выполнить его рейкаст.

  1. если игрок не прыгает ⇒ следующий луч будет вероятно пересекать смежную грань с той гранью, на которой игрок был до этого.

Оптимизация №2 — создайте кеш, чтобы с учетом идентификатора лица вы могли получить соседние лица за время O (1).

Этот кеш можно загрузить из файла, если ваша планета не генерируется в реальном времени.


Таким образом, с моим подходом на каждом шаге вы выполняете O (1) операцию чтения из кеша и raycast 1-6 лиц.

Выиграть!

Другие вопросы по теме