两层具有不同的点形状

时间:2020-05-04 22:28:17

标签: javascript glsl webgl

我正在地图上绘制webgl点,目前可以正常工作。现在,我想在地图上添加另一个图层。我正在尝试找出最佳方法。由于我的代码编写方式不同,因此我将向gl gl函数发送一个长数组,其格式如下:

[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...]

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

 <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); //颜色

1 个答案:

答案 0 :(得分:1)

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

通常不绘制POINTS,而是绘制由TRIANGLES制成的四边形。 Google Maps和Mapbox都不使用POINTS(您可以verify yourself

POINTS有2个问题

  1. 规范说,您可以画一个POINT的最大尺寸取决于实现,并且只能是1像素

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

  3. POINTS只能对齐正方形。

    如果要绘制的形状又高又薄,则需要浪费一堆纹理空间,或者透支绘制一个足够大的正方形以容纳要绘制的细长的矩形。同样,如果要旋转图像,使用三角形比点旋转要容易得多。

对于具体实现,完全取决于您。一些随机的想法

  • 使用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);
    

请注意,如果将imageIds设置为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,2,3,3,3,3, 4,4,4,4,顶点ID 0,1,2,3,0,1,2,3,0,1,2,3,0,1,2,3)然后可以使用它们来计算四边形坐标,纹理坐标和uvs来查找纹理中的每个点数据。优点是,如果要移动一个点,则只需更新每个点一个值,而不必更新每个点4至6个值。

注意:以上所有假设均假设您要在一个绘制调用中绘制1000个点。如果您要绘制250个或更少的点,甚至可能是1000-2000个点,则在每次平局调用时将它们绘制一个点可能是正常的。例如

for each point
  setup uniforms
  gl.drawXXX

没有指出,只是作为示例,WebGL Aquarium正在使用该循环。它不以任何方式使用实例化或合并几何。 Here's another example每次抽奖仅抽出1张四合彩