如何在 three.js 中按深度顺序渲染粒子?

How to render particles in depth order in three.js?

免责声明:总体而言,我对 three.js、WegGL 和 3D 图形比较陌生。

我正在使用 three.js 以 3D 方式显示来自 GPS 轨迹的点。我们必须能够可视化的数据集可能非常大(数十万个点),因此性能非常重要。

我使用一个 Points 对象,我用一个包含所有点的 BufferGeometry 填充它。积分是按照曲目的顺序加的,所以按时间顺序。

然后,我们使用 PointsMaterial 和 2D 纹理(精灵)将点表示为一个圆,圆外的区域是透明的。 2D 纹理被动态绘制为 canvas 因为颜色是动态的。

问题是,如果我们沿着轨迹的方向看点,即离摄像机较远的点是在距离较近的点之后渲染的点,则在点重叠的地方会出现伪像,透明的部分较近的点被绘制在较远的点上:

当我们从另一个方向看轨道时,即从后到前渲染的点,问题就消失了:

我尝试了以下两个选项来解决该问题:

What would a solution be to actually render the points in the depth order starting with the farthest and ending with the closest?

以下是代码的相关部分。

几何的构建。 timeline3d 对象包含所有点并来自 XHR 请求:

  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));

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
  });
}

解决方法:

这是 WebGL 在渲染点时的一个限制。就像上面提到的@ScieCode 一样,我个人一直在通过更改 material's blending mode 来解决这个问题。有了它,每个点都像透明的 Photoshop 图层一样相互融合,并消除了重叠效果。通常,如果您有浅色背景,您会想使用 THREE.MultiplyBlending,如果您有深色背景,您会使用 THREE.AdditiveBlending,但这完全取决于您的品味。

解决方案:

一个更复杂的解决方案in this example that sorts all vertices by depth once per frame. If you look at its source code,你会看到在渲染循环中调用了sortPoints(),它乘以相机矩阵与几何体的世界矩阵,以查看每个顶点深度并按顺序排序。