具有不同点形状的两层
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 个像素
当点的中心超出屏幕时是否立即消失取决于实现(这不是规范的一部分,但不幸的是这是真的)
POINTS 只能对齐正方形。
如果你想画的形状又高又瘦,你需要浪费一堆纹理 space 或者透支绘制一个足够大的正方形来容纳你想画的又高又细的矩形。同样,如果您想旋转图像,使用三角形比使用点更容易。
至于如何实现,就看你的了。一些随意的想法
使用POINTS
,每点加一个imageId
。使用 imageId
和 gl_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);
使用TRIANGLES
和instanced drawing
除了创建一个 2 三角形四边形并使用 drawArrayInstanced
或 drawElementsInstanced
之外,这实际上与上面没有什么不同。您需要使用自己的纹理坐标更改对 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 个四边形
我正在地图上绘制 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 个像素
当点的中心超出屏幕时是否立即消失取决于实现(这不是规范的一部分,但不幸的是这是真的)
POINTS 只能对齐正方形。
如果你想画的形状又高又瘦,你需要浪费一堆纹理 space 或者透支绘制一个足够大的正方形来容纳你想画的又高又细的矩形。同样,如果您想旋转图像,使用三角形比使用点更容易。
至于如何实现,就看你的了。一些随意的想法
使用
POINTS
,每点加一个imageId
。使用imageId
和gl_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);
使用
TRIANGLES
和instanced drawing除了创建一个 2 三角形四边形并使用
drawArrayInstanced
或drawElementsInstanced
之外,这实际上与上面没有什么不同。您需要使用自己的纹理坐标更改对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 个四边形