THREE.js 对单个 > 500k 多边形(面)对象的光线投射非常慢,与地球的线相交
Posted
技术标签:
【中文标题】THREE.js 对单个 > 500k 多边形(面)对象的光线投射非常慢,与地球的线相交【英文标题】:THREE.js raycasting very slow against single > 500k poly (faces) object, line intersection with globe 【发布时间】:2019-07-19 22:52:45 【问题描述】:在我的项目中,我有一个玩家环游地球。地球不仅仅是一个球体,它有山脉和山谷,所以我需要改变玩家的 z 位置。为此,我将来自玩家位置的单条光线投射到单个对象(地球)上,我得到它们相交的点并相应地改变玩家位置。我只在玩家移动时进行光线投射,而不是在每一帧上。
对于一个复杂的对象,它需要很长时间。具有约 1m 个多边形(面)(1024x512 段球体)的对象需要约 200 毫秒。光线投射会投射到每一张脸上吗?
有没有一种传统的快速方法可以在三中实现这一点,比如一些加速结构(八叉树?bvh?-从我的谷歌搜索中我似乎没有找到三中包含这样的东西)或其他一些想法-开箱即用(无光线投射)方法?
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);
提前谢谢你
【问题讨论】:
【参考方案1】:对于一个复杂的对象,它需要很长时间。具有约 1m 个多边形(面)(1024x512 段球体)的对象需要约 200 毫秒。光线投射会投射到每一张脸上吗?
开箱即用的 THREE.js 确实在对网格执行光线投射时检查每个三角形,并且 THREE 中没有内置加速结构。
不过,我与其他人合作开发了三网格 bvh 包(github、npm)来帮助解决这个问题,这可能会帮助您达到您所寻找的速度。以下是您可以如何使用它:
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
自述文件中提到了一些注意事项,因此请牢记(网格索引已修改,仅支持非动画 BufferGeometry 等)。仍然可以进行一些内存优化,但有一些可调整的选项可以帮助调整。
我很想知道这对你有什么作用!也可以随时在有关如何改进软件包的问题中留下反馈。希望对您有所帮助!
【讨论】:
【参考方案2】:我认为您应该将地球的高度图预渲染为纹理,假设您的地形不是动态的。将所有这些读入一个类型化数组,然后每当你的玩家移动时,你只需要将她的坐标反向投影到那个纹理中,查询它,偏移和相乘,你应该在 O(1) 时间内得到你需要的东西。
如何生成高度图由您决定。实际上,如果您有一个凹凸不平的地球仪,那么您可能应该首先从高度图开始,然后在您的顶点着色器中使用它来渲染地球仪(输入球体非常平滑)。然后就可以用同样的高度图查询玩家的Z了。
【讨论】:
高度图将不准确或巨大(或两者兼而有之)【参考方案3】:不要使用three.js Raycaster。
考虑提供function intersectTriangle(a, b, c, backfaceCulling, target)
的Ray.js
建议的优化:
如果玩家从一些已知位置开始⇒ 你必须知道他的初始高度,- 不需要光线投射(或者只做一次全网格慢速交叉)
如果玩家以小步移动 ⇒ 下一个光线投射将很可能与之前一样与同一张脸相交。
优化#1 - 记住前一张脸,并首先对其进行光线投射。
-
如果玩家不跳跃 ⇒ 下一个射线投射将很可能将相邻面与玩家之前所在的面相交。
优化#2 - 建立一个缓存,以便给定一个人脸 idx,您可以在 O(1) 时间内检索相邻的人脸。
如果你的星球不是实时生成的,这个缓存可能会从文件中加载。
因此,使用我在每次移动时的方法,您都需要从缓存中读取 O(1) 操作并进行光线投射 1-6 面。
赢了!
【讨论】:
以上是关于THREE.js 对单个 > 500k 多边形(面)对象的光线投射非常慢,与地球的线相交的主要内容,如果未能解决你的问题,请参考以下文章