- 小程序AR全面升级,实时跟踪Get!
[图片] 自七月微信宣布全面支持小程序接入AR后,小程序AR的热度日益高涨。 而作为国内领先的AR整体解决方案提供商,弥知科技小程序AR都有哪些优势: 告别APP,直接使用微信小程序完成AR互动 传统小程序AR仅具备识别能力, 而升级的弥知小程序AR支持摄像头画面下实时的跟踪互动 通过视觉化AR互动可以大幅度提升传统产品销售的转换率,所见即所得,差异化营销必备工具! 弥知科技近日正式推出基于微信小程序的图像识别与跟踪,以及面部识别与跟踪技术,助力微信小程序打造更为完整的全新互动场景! 一、AR面部识别与实时跟踪技术 弥知科技自研面部底层算法与AI技术相结合,完美融入微信小程序端,面部识别的精准度与稳定度大幅度提升,无论你上动下动还是东倒西歪,都能准确进行面部跟踪。 [视频] 通过对这一技术的完善,通过微信小程序即可完成AR眼镜试戴与AR美妆,并且还能通过微信商城直接完成购买,为提高销售转化带来更为直接的帮助! [图片] 弥知科技小程序眼镜试戴案例展示 二、AR图像识别与实时跟踪技术 弥知科技自研AR底层与渲染引擎全面接入微信小程序端,轻松实现小程序端的图像识别与跟踪。 [视频] 特别对于需要AR图像识别与跟踪技术行业,在体验上有了质的飞跃。 对于文创产品,用户打开微信小程序即可体验优质的AR文创内容,还支持一键分享功能,大幅提高产品的传播度! 在展览展示行业的应用来说,使用小程序即可实现AR逛展,了解展品背后的故事。 [图片] 包括美国达利博物馆、英国泰特美术馆等都选择与Facebook、Snapchat合同打造基于流量平台的博物馆AR应用。不难看出使用更为轻便的小程序进行体验是一种趋势。 而对于广告行业来说,通过小程序扫一扫品牌包装即可获得更多动态信息,在竞争日益激烈的广告行业打出一张差异化的王牌! [图片] 三、品牌商如何拥有一个AR小程序 品牌商的小程序要具备定制化AR效果,根据微信官方理论需要“三板斧”来配合: 1. 业务场景,从品牌业务场景出发,确认适合用AR的用户场景,设计体验方案; 2. AR引擎开发,找到有AR引擎开发能力的服务商,支持品牌方落地这个体验; 3. 小程序开发升级,AR引擎服务商和小程序开发团队配合,升级体验。 弥知科技从这“三板斧”出发,提供了正确的解法: 专业的创意团队,从客户的业务场景出发,创造更具趣味性与创意性的AR互动。 超前的技术实力,弥知科技专注于视觉CV算法研发多年,提供专业的AR引擎,保证品牌方完美落地实时跟踪互动的AR小程序。 国内顶级小程序开发团队建立合作,客户更放心! 更多微信小程序AR案例欢迎添加微信👇了解! [图片]
2019-08-22 - 小程序AR实时跟踪的iOS系统bug
自从微信7月初开放AR增强现实接口之后,我们已经成功的将人脸识别与图像识别算法进行了移植,我们的算法在小程序支持实时的跟踪!效果视频如下: 自然图像识别与实时跟踪https://project.kivisense.com/videos/kivisense-wechat-ar.mp4 人脸特征点识别与实时跟踪https://project.kivisense.com/videos/kivisense-facear.mp4 视频是在安卓设备上拍摄的效果非常好,在很低端的设备上都可以跑出很好的效果,但是目前遇到一个问题,这个问题在iOS上面最为明显,希望可以拜托官方人员处理一下,涉及到基础库的一个问题。只要这个问题解决了,我们会将AR接口做成插件,方便所有微信开发者使用!到时就真正的可以为所有需要AR功能的微信小程序服务了!感谢官方人员帮忙处理一下~ 谢谢
2019-08-08 - Three.js 文字渲染那些事
THREE.js开发的应用运行在iphone5下发现有些时候会崩溃,跟了几天发现是因为Sprite太多频繁更新纹理占用显存导致的。通常解决纹理频繁更新问题就要用到one draw all方法,放到纹理上就是把所有纹理图片生成一张大图片的方式 一、阻止纹理重复上传 我们需要一张大纹理,先将所有的内容绘制在大纹理上,需要显示局部纹理的时候通过纹理坐标控制去大纹理上取图像。那么这个时候问题来了,THREE.js内部实现方式是将Texture与图片、纹理坐标绑定,即使为所有的Texture对象设置同一张图片,THREE.js仍然会将每个Texture中的图片上传给GPU。每次上传一张大纹理严重阻塞UI渲染进程。[图片] [图片] 首先要解决的是让这张大纹理值上传一次。 这个问题需要我们对THREE.js源码进行深入了解,可以看到setTexture2D函数中有一个properties变量,这个变量是一个WebGLProperties类型的变量,而该类型存储各种东西:Texture、Material、RenderTarget、Object的buffers等。我们继续深入该类的源码,发现get方法会根据对象的uuid来获取相关WebGL属性,比如gl.createTexture、gl.createBuffer创建的各种缓冲区。 [图片] 对应Texture得到的webgl属性如下,其中__webglTexture就是对应的纹理图片创建的缓冲区对象。 [图片] 那么我们可以来一个取巧的方法,将所有纹理的的uuid都设置唯一,那么THREE.js只会对第一个Texture的纹理进行上传,后面的texture对象取到的都是第一个的properties,这样就能避免纹理重复上传。 [图片] 二、建立纹理索引 我们需要自己维护一套索引关系,通过这套索引关系得到每个贴图在大纹理中纹理坐标。这里要为每一个poi记录它的起始位置和区域范围,其中要用到canvasContext.measureText来测量文本的宽度,文本高度可以直接根据fontSize取得。 [图片] 同时索引建立完毕后,需要计算每个poi区域在全局纹理中的纹理坐标范围: [图片] 要注意的是,这里纹理坐标的原点在左下方,有时候原点在左上方。建立索引代码如下 [图片] 三、局部更新 上述方案虽然能够避免频繁上传纹理,但是需要每次将需要绘制的内容准备好,当有内容需要更新时,还是需要重新上传整个全局纹理,反而使得性能下降巨大。经过查阅资料后发现webgl中有一种局部纹理更新技术,简单来说先在内存中开辟一块的纹理区域,将所有内容绘制在这张全局纹理中,每次有更新时,只需要更新它的一个局部区域即可。 但是这里要解决的问题是THREE.js并没有提供局部纹理更新的方式,也没有相应的自定义接口,那么这时候就需要我们自己来处理了。 这里自定义一个Texture的子类 [图片] 开辟一块内存区域 [图片] 在需要的时候动态更新局部纹理,其中src这里是ImageData对象[图片] 具体代码可以参考这里,我这里也是基于它来定制的。 https://github.com/spite/THREE.UpdatableTexture 原文作者通过更改THREE.js源码的方式实现,而我是直接把下面这个函数拷贝到这个子类中 [图片] 四、高清屏的大坑 现在我们的方案是,先在gpu中开辟一块全局纹理区域,然后绘制时将poi绘制到一张与全局纹理同样大小的canvas上,然后从canvas中调用createImageData来获取像素,将像素局部更新到gpu中。那么在pc上我们得到的结果很完美。 [图片] 然而放到移动端上后,我们得到的结果是:[图片] [图片] TMMD中间那块哪去了!找了大半天发现问题出现在高清屏上,挡在高清屏上绘制canvas上时,我们通常会做一些高清处理,比如四像素绘制一像素。 我们做高清处理的方式是利用radio*radio设备像素绘制一css像素,看起来是css像素的大小,但实际在浏览器内部,看起来css上一像素实际在canvas里的像素是radio * radio(radio代表window.devicePixelRatio) [图片] 但实际上在浏览器内部绘制canvas图像的单位是设备像素。那么如果我们还以上面的rectW、rectH来获取像素的话,我们得到的这部分像素并不是这个poi真正占有的像素数目。 [图片] 所以,问题就来了我们需要在gpu开辟的全局纹理的单位跟canvas中获取像素的单位要保持一致,我们统一使用设备像素。 [图片] 我们对canvas也不用使用style来设置样式宽高了。 [图片] 那么获取poi图像的真正像素范围时: [图片] 所以利用getImageData取像素时候,就要小心取到真正的像素区域,(startX * radio,startY * radio)- (poiRectW * radio, poiRectH * radio);否则某些像素就会被丢弃掉,这部分像素才是浏览器真正使用的设备像素。 现在在移动设备上能够获取正确的高清label啦! [图片] 五、局部更新引起的新问题 当全局纹理被占满时候,在继续绘制poi,这时候新的poi区域需要更新到gpu中,那么也就带来了新的问题,在gpu中的纹理还保持着之前的像素,而新的poi会覆盖这部分区域,但有时候往往会与之前的文字叠加起来,效果如下: [图片] 可以看到新更新的poi,在计算纹理坐标时候,有一部分像素包含了其他poi的像素。这个问题是因为新poi的区域刚好叠在了先前poi的边界上,那么我们只要给新的poi加一点buffer,这个buffer是白素透明区域,buffer会把之前的poi像素覆盖掉,而我们计算纹理坐标时,只取poi的边界,那么就可以解决这个问题。 [图片] 那么首先绘制的时候就要保留buffer [图片] 上传的时候使用buffer [图片] 计算纹理坐标时,排除buffer [图片] 六、局部更新带来的性能问题 根据目前的结果,局部更新能后解决crash的问题,但是带来了严重的性能开销,与同事应用局部更新提升性能的结果相反。这个问题还要继续跟踪。 目前发现问题是因为使用了getImageData来获取数据,然后传递到gpu中,非ios设备用这种方式有时候getImageData的开销特别大,而ios设备相对好一些。 测试发现非ios设备直接上传一张大纹理的效果反而比getImageData这种方式更好。但是依然不如之前上传多个canvas的性能。而在iphone5的测试机和iphone6的机器上性能比之前直接上传多个canvas的方式好一些,且没有崩溃问题。但是在岳阳的iphone6 plus 16g内存的手机上发现用具局部纹理更新性能很差,而且经常崩溃。 后来发现原因是因为,虽然getImageData在IOS上性能好过非IOS设备,但性能开销仍然比较大,所以当场景中POI很多时,仍然会引起主线程卡顿,甚至计算太密集引起浏览器崩溃。其中层尝试使用cesium方式,每个poi创建新的canvas,将canvas进行局部上传,本以为这种方式不需要getImageData会更快一些,然而实践发现每次创建canvas设置参数的过程更耗时。 最终的方案是仍然使用getImageData,但是将getImageData的过程分块处理,每50ms处理一次,分块放到场景中,这样就解决密集计算引起的崩溃问题,虽然增加了控制成本,但是能够有效解决IOS崩溃问题。有趣的是在安卓上getImageData方式开销很大,即使分块也不适合,而且安卓用一张大纹理的方式来处理,会发现很多POI绘制效果不好。 [图片] 最终方案是,IOS使用getImageData局部纹理+分块加载方式绘制POI。安卓使用POI独立创建canvas+全量加载方式。(安卓不适用分块加载,是为了尽快把所有POI呈现给用户) 七、文字黑色描边问题 这个问题自始至终困扰我好久一直没找到黑边的原因; [图片] 将原始的canvas导出后发现这是因为原始的canvas就有一层边界 [图片] 曾经怀疑是minFilter的设置不对在pc端纹理使用NEARESTFilter方式取值发现的确能够消除黑边,然而移动端仍然会出现黑边,最后使用颜色混合公式解决问题。 gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); 在Three.js中需要设置SpriteMaterial的blending为CustomBlending 八、颜色混合新问题 但是使用上述方式同样引来新问题,设计反映poi的icon四周被裁切掉, [图片] 看着没问题是吧,设计同学截了图之后放大了20倍。。。。。 [图片] 刚开始我确实以为这是webgl渲染问题,后来仔细考虑了下这外圈白色的由来(遇到问题还是得静下心分析)。 原因是设置了blendFunc(SrcAlphaFactor,OneMinusSrcAlphaFactor)导致有些icon周围的像素alpha比较低 [图片] 颜色混合后增加了target的颜色分量,导致最终这些区域的颜色范围接近255,所以泛白。从而把原来图片四周有切边的问题充分暴露出 解决方法是设置alphaTest,如果原始纹理的alpha小于这个值则直接discard。最终得到的效果是: [图片][图片][图片] 九、TextureAlta问题 前面因为sprite的旋转中心只能放在sprite纹理区域的中心所以,上面做了很多冗余纹理,有很多空白区域,目前改造了Sprite加了pivot可以动态改变选中中心点,改变后IOS下纹理的使用率提升了60%,安卓下因为是单个纹理上传所以,需要保证纹理的大小是2的n次方,纹理的浪费率降低了50% 上述问题虽然解决了崩溃问题,但是实际使用中每个poi都要getImageData和texSubImage2D这个方法,造成单个poi耗时基本在25ms(iphone5 8.4.4);虽然上面使用setTimeout 50ms分块方式上传,但是如果poi过多比如1000多的停车场,这样会导致停车场数据需要50s才能完全显示出来。这次优化的方案是等待所有poi图片拿到后,绘制所有的poi把画布调用一次getImageData和一次texSubImage2D上传到gpu,同时下次更新时,只会增量一次性上传更新。 [图片] 十、Frstrum增量更新 原来是在每一级别缩放时把所有的poi都生成好,现在的做法是只生成视锥体中能看得到的poi,然后在每次OrbitControl出发change事件时根据视锥体判断poi,做去重后增量更新 [图片] 目前还是有些问题,有时候会碰到视锥体中的poi很少,可能是判断问题,后续会加入空间索引,根据索引和视锥体结合起来做增量更新 后续使用发现在停车场这种大数据的poi全部加载到地图下,使用这种方式每次都要做去重处理,性能开销很大,处理方式是使用{}做hash代替数组includes方法,结果发现性能提示很大,原来3600个节点每次去重处理在iphone 16g 10.3.3上性能基本在28帧每秒,经过优化后数据帧率达到50+(主流iPhone7fps60);iphone5 16g 8.4.1 性能在24左右优化后帧率在44+,安卓华为荣耀9优化前25帧,优化后 40+ [图片]安卓之所以不适用IOS的绘制方式,是因为这种在安卓上的绘制效果不理想,被设计挑战 安卓后面也做了一些优化,之前安卓是每次都会重新创建canvas并上传至gpu纹理中,导致使用视景体增量更新poi时,性能有所下降,后来每一层中的poi都根据icon、文字组成key缓存起来,并且缓存纹理,不但阻止canvas的重复创建,还阻止canvas重复上传至gpu纹理(three中使用同一uuid),使用该方案荣耀9的fps达到50+ 十一、text glyphs该方式还有待尝试 https://webglfundamentals.org/webgl/lessons/webgl-text-glyphs.html 十二、真正解决POI文字黑边问题 由于要做poi渐变出现效果,但是因为之前处理黑边问题用的是颜色混合的方式,所以当动态改变透明度时,受颜色混合影响往往是文字颜色先消失,剩下透明度部分还存在显示先过很差。所以要实现渐变效果,不能使用颜色混合方法,但不适用颜色混合就会有黑边问题,所以要从源头上解决黑边问题。(看到最后会发现有残影) [图片] 那么思考黑边到底是怎么产生的,这与webgl中纹理插值的颜色有关,有的设备像素取纹理时有不同的方案,但一般情况下纹理像素和设备像素都不是一一对应,所以有插值取值问题。 [图片] 这是正常情况下利用canvas绘图时背景颜色不设置,那么可以看到我们绘制出来的canvas的确有一层奇怪的黑边。当设备取到纹理中这些边界时就会产生黑边。那么就要思考怎么不让它取到这层黑边,这个问题想了好久曾经试过用opacity过滤,发现不能解决问题。 [图片] 有一天突然想到如果canvas背景为有颜色,每个设备像素都能取到颜色,那么就不会有这个问题。所以我们能否通过改一下canvas的背景颜色同时有通过透明度过滤掉不合格的像素?最终发现这个问题还真可以。 首先在绘制时将canvas背景设置为白色,但是有很低的透明度 [图片] 这时候canvas绘制出来的效果是 [图片] 可以看到已经没有黑边了,那么这时候设备像素永远不会取到黑色边界,也就彻底解决了黑边问题。 那么就可以利用tween来做动画了
2019-03-14 - webgl 的 canvas.createImage 是否对图片尺寸进行了限制?
小程序真机上使用 webgl 的 canvas.createImage 渲染尺寸较大的图片时,非常大的概率会下载失败,图片尺寸最长的(width或height)大于2000时几乎100%失败,但是调试工具是正常的。 代码片段:https://developers.weixin.qq.com/s/ePTJ67ma78bO 这里使用了修改的THREEJS,加载image使用的是 webgl 的API canvas.createImage
2019-09-04 - 调用微信api报错,Unhandled promise rejection
[图片] 在客户端调用微信api创建授权按钮的时候 let button = wx.createUserInfoButton() 个别手机会出现上面这个错误: Unhandled promise rejection Object { "errMsg": "insertTextVi... errMsg: "insertTextView:fail function cannot run on service" __proto__: Object 我想这应该属于微信api内部的错误吧,之前以后更新了微信版本就不会有问题了,结果都已经更新到7.0.2了,结果还是会报这个错误,手机型号是苹果X,请问这该如何解决,如何打开授权按钮?
2019-01-08