在三维 gis 中经常需要在指定的多边形地理范围内做一些操作,比如地形的多边形裁剪、压平多边形区域内的倾斜摄影模型、在指定地理范围内绘制等间距的点等。这都涉及到限定多边形区域的问题。
所谓的限定多边形地理区域,核心问题在于判断某个片元是否处于多边形区域内。虽然说在CPU中通过几何算法可以判断某个点是否处于多边形区域内,但是并不适用于实时计算机图形渲染,因为空间是连续的,而点位是离散的,不可能把一个区域内的所有的地理坐标点都取完,然后依次在CPU中判断;而且在CPU中判断多边形和点的包含关系需要对凸多边形和凹多边形加以区分,较为繁琐而且性能不高。相对来说用纹理限定多边形区域更加通用,该方式不用区分凸多边形和凹多边形、纹理可以在着色器里使用,性能也更高。
在自定义多边形区域内做某些操作
多边形角点的原始坐标为世界坐标系下的位置,为了把多边形绘制在一张纹理上并在后续用于判断包含关系。我们要把多边形角点的世界坐标在局部坐标系(比如ENU坐标系)下转换为相对坐标;同时获得相对坐标X和Y的最大、最小值表示一个范围 u_regionBounds,传入到顶点着色器中用于计算纹理坐标。
世界坐标转换为相对坐标
相对坐标的范围
绘制表示多边形区域的纹理时,在顶点着色阶段:根据角点坐标 position 在范围 u_regionBounds 中的相对位置得到 [ 0, 1 ] 的纹理坐标,然后转到 [ -1, 1 ] 区间表示裁剪坐标。片元着色器直接给一个红色即可,使用时通过坐标值的R分量是否为1判断片元是否位于多边形区域内。
attribute vec3 position;
uniform vec4 u_regionBounds;
void main()
{
vec2 regionBoundsDimension = u_regionBounds.zw - u_regionBounds.xy;
vec2 xy = (position.xy - u_regionBounds.xy) / regionBoundsDimension * 2.0 - 1.0;
gl_Position = vec4(xy.x, xy.y, 0.5, 1.0);
}
绘制多边形区域的顶点着色器
void main()
{
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
绘制多边形区域的片元着色器
在调用draw call渲染多边形到一张纹理时,设置渲染状态( render state ) 的视口大小( viewport )为图片的纹理的尺寸。这次绘制得到如下图所示的红色多边形纹理。
多边形区域纹理
上面的 “多边形区域纹理” 只是记录了多边形区域的形状,把它作为 uniform 变量输入到另一次 draw call 的着色器中,用于判断多边形区域和片元的包含关系。这次在顶点着色器中也是用相对坐标和对应范围计算出纹理坐标插值给片元着色器;在片元着色阶段,从多边形区域纹理上取颜色值,如果 R 分量不为1就判定为不在多边形区域内,被剔除或者以别的方式处理该片元。
vec2 boundsDimension = u_regionBounds.zw - u_regionBounds.xy;
v_texCoord = (a_position.xy - u_bounds.xy) / boundsDimension;
使用多边形纹理 - 顶点阶段 - 计算某个片元在多边形区域纹理上的纹理坐标
float r = texture2D(u_regionTexture, v_texCoord).r;
if (r < 0.9) {
discard;
}
使用多边形纹理 - 片元阶段 - 判断片元是否位于多边形区域内