为了让广大的开发者
更深入地了解
百度地图开放平台的技术能力
轻松掌握满满的技术干货
更加简单地接入
开放平台的服务
我们特别推出了
“位置服务(LBS)开发微课堂”
系列技术案例
第一期的主题是
《Polyline 绘制优化升级》
你还想了解哪些技术内容?
快来评论区留言告诉我们吧!
Polyline(线段)绘制,作为地图绘制的基础,凝聚着工程师们的巧思与智慧。百度地图开放平台持续对地图 SDK 的 Polyline 绘制技术进行优化,努力为开发者提供更好的开发体验。
百度地图最新版本的地图 SDK 在基础性能、渲染效果以及场景化应用等方面都有了明显的突破,让地图绘制更流畅,绘制效果更丰富。
(向上发光效果呈现出的围栏视觉效果)
(司乘同显:乘客端小车平滑移动效果)
今天,让我们一起来看看这些技术提升背后的秘密,体验一下百度地图在 Polyline 绘制方面的过人之处吧!
1 Polyline渲染优化
新版地图 SDK 的 Polyline 绘制功能采用了记录法向量的方式,有效解决了线宽变化导致的顶点数据重复计算的问题,使得开发者在调用相关功能进行开发时,能够显著降低CPU的占用率。
1.1 顶点数据优化方案
旧版 SDK:在线的每一段的顶点处,垂直当前线段做垂线,再沿垂线各取二分为一线宽长度计算出上下两个顶点的位置坐标。
新版 SDK:在线的每一段的顶点处,垂直当前线段做两个相反方向的射线,记录顶点位置点及射线的方向向量,绘制时传入线宽。
以两个点的线段为例:
旧版本(图 1)在 CPU 阶段根据线宽计算顶点位置 a1、a2、b1、b2 和纹理坐标 st 。
新版本(图 2)中直接记录原始 A、B 和 an1、an2、bn1、bn2 方向信息。
顶点偏移量计算 delta= an1*lineWidth*线宽缩放比例,等同于 shader 中的 vec4 delta= vec4(a_normal.xy * u_line_width * v_direction, 0, 0) 。
线宽缩放比例在正常直线时为 1,拐角连接处根据拐角类型(jointype)计算需要的缩放比例。比如:圆角为 1,尖角为向量 AB 的法向量与 AB、BC 法向量相加的向量的点乘结果(有最大值限制)。
1.2 顶点数据差异对比
图 3(旧版本)
图 4(新版本)
旧版中(图 3):a_position 中 xyz 代表位置,a_texCoord 中 st 代表纹理坐标。
新版中(图 4):a_position 中 xyz 代表位置,w 代表累计长度(纹理坐标及 Track 动画时使用);射线方向向量在 a_nomal 中,xy 代表法向量,z 的正负代表法向量的正逆,用于纹理计算;z 的绝对值代表线宽缩放比例,用于顶点偏移量计算。
1.3 Shader数据处理实现
图 5( 旧版本)
图 6( 新版本)
旧版中(图 5):gl_Position 数据由传入的顶点数据经过 MVP 矩阵转化后得到;纹理坐标直接传递给片段着色器。
新版中(图 6):gl_Position 数据由传入的顶点数据+线宽对应的顶点偏移量计算后再经过 MVP 矩阵转化后得到;纹理坐标也在顶点着色器 Shader 中计算后传递给片段着色器。
gl_Position 根据线宽在顶点着色器 Shader 中计算:
// 线宽缩放比例,直线时为1,拐角处根据拐角类型计算得出
float v_direction = abs(a_normal.z);
// 线宽对应的顶点偏移
vec4 delta= vec4(a_normal.xy * u_line_width * v_direction, 0, 0);
gl_Position = u_MVPMatrix * vec4(a_position.xyz, 1) + u_MVPMatrix * delta;
纹理坐标在顶点着色器 Shader 中计算(以repeat方式为例):
float s = 0.5 + a_normal.z * 0.5;
// u_gl_to_pixel不同地图缩放层级的坐标像素比例
// u_tex_height0为纹理高度
float t = a_position.w * u_gl_to_pixel / u_tex_height0;
v_tex_coord0 = vec2(s, t);
另外还内置其他纹理坐标计算方式:拉伸中间部分,纹理整数倍平铺、0~0.5 部分拉伸、单纹理拉伸、纹理等比缩放等。
路线走过擦除、置灰场景,走过和未走过路线样式的处理,是根据顶点着色器 Shader 中传递的累计长度(v_acculength)在片段着色器 Shader 中控制并渲染。
v_accuLength = a_position.w;
if (v_accuLength >= u_progressLength) { // progress forward
gl_FragColor = u_color1;
} else { // progress backward
gl_FragColor = u_color0;
}
1.4 性能对比
同等 26,050 个点数据路线绘制(乌鲁木齐-深圳),新旧版本性能对比,可以看到 CPU 占用率下降超 50%,GPU 性能没有明显改变。
图 7(旧版本)
图 8(新版本)
2 发光效果优化
2.1 性能优化
得益于顶点数据优化方案,新版的Polyline绘制实现了发光与非发光效果的顶点数据共享同一份数据Buffer,开发者在调用相关功能的过程中,可以显著降低内存的占用量。
2.2 模糊发光
旧版的发光效果逻辑中,需要用到离屏渲染,绘制比原polyline宽度更宽的线,然后进行多次模糊处理,生成一张模糊图片绘制到原来的Polyline下方,实现发光效果。所以旧版SDK的发光效果就需要两倍的时间、空间去计算和储存点信息,还要离屏渲染,不仅要申请额外的帧缓冲空间,多次模糊的处理还增加了GPU的耗时时间。
针对模糊效果的离屏渲染问题,我们在新版SDK的Shader中采用了模糊函数进行替代,避免了相关问题。模糊程度可通过系数灵活控制。
代码片段:
if (uniforms.u_bloom == 1) {
// 透明度渐变发光
// uniforms.u_speed衰减速度
weight = pow(weight, uniforms.u_speed);
} else if (uniforms.u_bloom == 2) {
// 模糊发光
// uniforms.u_times代表模糊程度
// r = 根号2 = 1.414,σ为 r/3 = 0.471
// 2.0*σ方 = 2.0 * 0.471 * 0.471 = 0.443682
weight = 1.0 - exp( - (weight * weight ) / float(uniforms.u_times) * 0.443682);
}
新、旧版模糊发光效果对比:
2.3 发光方向
通过记录方向向量的方案,发光效果拥有了更多的自由度:新版SDK的发光效果不仅可以实现正常的向两侧发光,还支持单侧发光和向上发光。Polygon边线、Circle的边线、弧线、大地曲线也采用了类似Polyline的处理技术。
多方向发光效果:
图 11 线段
图 12 圆边线
图 13 Polygon 边线
2.4 参数接口
Polyline的发光效果功能为开发者提供了高度灵活且可配置的参数接口,从而可以实现更加丰富和吸引人的用户体验,助力开发者打造出更具竞争力的应用。
渐变线多方向不同发光参数动态更新效果:
3 Track 动画
3.1 场景分析
在司乘同显场景中,旧版 SDK 在司乘同显组件中对路线和小车数据进行绑路计算,每次动画结束需要更新 Polyline 的点数据集,每次更新变化就会产生大量的计算。然而大部分的情况下整体路线信息是没有变化的,只是随着行程进度进行路线擦除或置灰。类似的还有轨迹回放,小车平滑移动等场景。为了解决以上问题,Polyline绘制引入了Track 动画。
3.2 Track 动画
在新版 SDK 中,Polyline 新增的 Track 动画能力,可以根据行程进度对路线进行动画处理;除了按进度进行动画,还内置了绑路逻辑,开发者可以直接传入小车行进的位置和方向信息,由 SDK 自动绑路后进行动画;另外除了正常(Nomal)样式,还新增了走过和未走过路线样式,实现 Polyline 走过和未走过路线的自定义绘制。对比旧版的 BMKTraceOverlay,在性能、效果、易用性和灵活性方面都有较大提升。
历史轨迹回放:
图 15
走过路线擦除:
图 16
新旧版本特性对照:
新版效果将于 9 月份在官网发布的 Android 地图 SDK V7.6.3 和 iOS 地图 SDK V6.6.3 版本中呈现,敬请期待!