具有不同点形状的两层

Two layers with different point shapes

我正在地图上绘制 webgl 点,目前它工作正常。现在我想在地图上添加另一个图层。我正在努力找出最好的方法来做到这一点。由于我的代码编写方式,我向 gl draw 函数发送了一个长数组,格式如下:

[lat, lng, r, g, b, a, id, lat, lng, r, g, b, a, id, etc...] //where id is used for selecting the marker.

使用以下方法绘制点:

this.delegate.gl.drawArrays(this.delegate.gl.POINTS, 0, numPoints);

添加额外层时,我希望一层显示为圆形,另一层显示为正方形。我的想法是向数组添加另一个元素,该元素编码是绘制圆形还是方形,即 0 或 1,因此数组步幅现在为 8:

[lat, lng, r, g, b, a, id, code, lat, lng, r, g, b, a, id, code etc...]

着色器代码然后决定是绘制圆形还是方形。这可能吗?我不确定如何将形状代码属性传递给着色器以确定要绘制的形状。 下面是shader代码,目前有两个fragment shader程序。一个画圆,一个画方

 <script id="vshader" type="x-shader/x-vertex">
      uniform mat4 u_matrix;
      attribute vec4 a_vertex;
      attribute float a_pointSize;
      attribute vec4 a_color;
      varying vec4 v_color;

      void main() {
        gl_PointSize =  a_pointSize;
        gl_Position = u_matrix * a_vertex;
        v_color = a_color;
      }
    </script>

    <script id="fshader" type="x-shader/x-fragment">
      precision mediump float;
      varying vec4 v_color;

      void main() {
        float border = 0.05;
        float radius = 0.5;

        vec2 m = gl_PointCoord.xy - vec2(0.5, 0.5);
        float dist = radius - sqrt(m.x * m.x + m.y * m.y);

        float t = 0.0;
        if (dist > border)
        t = 1.0;
        else if (dist > 0.0)
        t = dist / border;
        gl_FragColor = mix(vec4(0), v_color, t);

      }
    </script>
      <script id="fshader-square" type="x-shader/x-fragment">
        precision mediump float;
        varying vec4 v_color;
        void main() {
          gl_FragColor = v_color;  
        }

    </script>

我的属性指针是这样设置的:

this.gl.vertexAttribPointer(vertLoc, 2, this.gl.FLOAT, false, fsize*7, 0); //顶点

this.gl.vertexAttribPointer(colorLoc, 4, this.gl.FLOAT, true, fsize*7, fsize*2); //颜色

绘制不同形状的点最常用的方法是使用纹理,这样您的设计师可以制作 markers 等等。

不绘制 POINTS 而是绘制由 TRIANGLES 构成的四边形也很常见。 Google 地图和 Mapbox 都不使用 POINTS(你可以 verify yourself

POINTS 有 2 个问题

  1. 规范说你可以画一个点的最大尺寸取决于实现,可以只有 1 个像素

  2. 当点的中心超出屏幕时是否立即消失取决于实现(这不是规范的一部分,但不幸的是这是真的)

  3. POINTS 只能对齐正方形。

    如果你想画的形状又高又瘦,你需要浪费一堆纹理 space 或者透支绘制一个足够大的正方形来容纳你想画的又高又细的矩形。同样,如果您想旋转图像,使用三角形比使用点更容易。

至于如何实现,就看你的了。一些随意的想法

  • 使用POINTS,每点加一个imageId。使用 imageIdgl_PointCoord 从纹理图集中选择图像

    假设所有图像大小相同

    uniform vec2 textureAtlasSize;  // eg 64x32
    uniform vec2 imageSize;         // eg 16x16
    
    float imagesAcross = floor(textureAtlasSize.x / imageSize.x);
    vec2 imageCoord = vec2(mod(imageId, imagesAcross), floor(imageId / imagesAcross));
    vec2 uv = (imageCoord + imageSize * gl_PointCoord) / textureAtlasSize;
    
    gl_FragColor = texture2D(textureAtlas, uv);
    

请注意,如果您将 imageId 设置为 vec2 而不是 float,并且仅将 id 作为 imageCoord 传递,那么您就不需要在着色器中使用 imageCoord 数学运算。

  • 使用POINTS,一个纹理图集,和vec2偏移,每个点的vec2范围

    现在图像不需要相同大小,但您需要为每个点适当设置偏移和范围

    gl_FragColor = texture2D(textureAtlas, offset + range * gl_PointCoord);
    
  • 使用TRIANGLESinstanced drawing

    除了创建一个 2 三角形四边形并使用 drawArrayInstanceddrawElementsInstanced 之外,这实际上与上面没有什么不同。您需要使用自己的纹理坐标更改对 gl_PointCoord 的引用,并且需要计算顶点着色器中的点

    attribute vec2 reusedPosition;  // the 6 points (1, -1)
    
    ... all the attributes you had before ...
    
    uniform vec2 outputResolution;  // gl.canvas.width, gl.canvas.height
    
    varying vec2 ourPointCoord;
    
    void main() {
       ... -- insert code that you had before above this line -- ...
    
       // now take gl_Position and convert to point
       float ourPointSize = ???
       gl_Position.xy += reusedPosition * ourPointSize / outputResolution * gl_Position.w;
    
       ourPointCoord = reusedPosition * 0.5 + 0.5;
    
  • TRIANGLES 与合并的几何图形一起使用。

    这只是意味着您需要 4 个(如果有索引)或 6 个而不是每个点一个顶点。

  • 使用TRIANGLES只有一个id,将数据放入纹理中。

    如果更新 4 到 6 个顶点以移动一个点是太多的工作(提示:可能不是)。然后你可以把你的数据放在一个纹理中,并根据 id 查找每个点的数据。因此,您将每个点的 4 个 ID 加上某个缓冲区中的一些顶点 ID(即 ID 0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3, 4,4,4,4, vertex ids 0,1,2,3,0,1,2,3,0,1,2,3,0,1,2,3) 然后你可以用它们来计算四边形坐标、纹理坐标和 uv 以查找纹理中每个点的数据。优点,如果你想移动一个点,你只需要每个点更新一个值而不是每个点更新 4 到 6 个值。

注意:以上所有内容均假定您希望在单个绘制调用中绘制 1000 个点。如果您要绘制 250 个或更少的点,甚至可能是 1000-2000 个点,则以正常方式在每个绘制调用中绘制一个点可能就可以了。例如

for each point
  setup uniforms
  gl.drawXXX

不是点,只是举个例子 WebGL Aquarium is using that loop. It is not using instancing or merging geometry in any way. Here's another example 每次绘制调用只绘制 1 个四边形