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

时间:2019-06-25 17:39:20

标签: javascript three.js 3d depth-testing

免责声明:我相对来说对three.js,WegGL和3D图形还比较陌生。

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

我使用一个Points对象,并用一个包含所有点的BufferGeometry填充。这些点是按照轨迹的顺序添加的,因此是按时间顺序排列的。

然后,我们使用带有二维纹理(精灵)的PointsMaterial,该纹理将点表示为一个圆,圆外的区域是透明的。由于颜色是动态的,因此将2D纹理动态绘制到画布上。

问题是,如果我们观察轨迹方向上的点,即距摄像机较远的点是在距离更近的点之后渲染的点,则存在点重叠的伪像,其中点的透明部分在较远的点上绘制的较近的点:

points in 3d

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

enter image description here

我尝试了以下两种方法来解决该问题:

  • 使用alphaTest的值介于0和1之间,种类有效
    • 但是我们的点也可以是部分透明的(这是客户的要求),因此存在的风险是alphaTest将剪切应实际渲染的点的一部分
    • 它会创建锯齿状的边缘,使点重叠,看起来不太好

points in 3d

  • 使用depthWrite: false作为点素材,可以很好地渲染,但是无论相机的方向如何,最近的点总是在较旧的点上绘制,而且看起来很奇怪而且是错误的

points in 3d

  

实际渲染深度中的点将是什么解决方案   从最远的位置开始到最接近的顺序?

这是代码的相关部分。

几何的构建。 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));

材料:

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

1 个答案:

答案 0 :(得分:4)

解决方法:

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

解决方案:

有一个 更复杂的解决方案,已实现in this example,该解决方案每帧对所有顶点按深度排序一次。如果查看its source code,您会看到sortPoints()在渲染循环中被调用,它将相机矩阵与几何图形的世界矩阵相乘以查看每个顶点深度并按顺序排序。 / p>