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的问题,但是带来了严重的性能开销,与同事应用局部更新提升性能的结果相反。这个问题还要继续跟踪。

Three.js使用局部纹理更新的更多相关文章

  1. 【转】【Asp.Net】了解使用 ASP.NET AJAX 进行局部页面更新

    简介Microsoft的 ASP.NET 技术提供了一个面向对象.事件驱动的编程模型,并将其与已编译代码的优势结合起来.但其服务器端的处理模型仍存在技术本身所固有的几点不足: 进行页面更新需要往返服务 ...

  2. nw.js桌面程序自动更新(node.js表白记)

    Hello Google Node.js 一个基于Google V8 的JavaScript引擎. 一个伟大的端至端语言,或许我对你的热爱源自于web这门极富情感的技术吧! 注: 光阴似水,人生若梦, ...

  3. WebGL three.js学习笔记 纹理贴图模拟太阳系运转

    纹理贴图的应用以及实现一个太阳系的自转公转 点击查看demo演示 demo地址:https://nsytsqdtn.github.io/demo/solar/solar three.js中的纹理 纹理 ...

  4. React.js 小书 Lesson20 - 更新阶段的组件生命周期

    作者:胡子大哈 原文链接:http://huziketang.com/books/react/lesson20 转载请注明出处,保留原文链接和作者信息. 从之前的章节我们了解到,组件的挂载指的是将组件 ...

  5. three.js学习:纹理Texture之平面纹理

    index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset=&quo ...

  6. cocos2dx js 3.2 热更新

    COCOS IDE用手机调试更新是正常的,是预想的结果,但用COCOS IDE打包发布APK,安装到手机上,热更新下载图片.JSON UI什么的都能正常更新替换,但JS脚本没有替换,这是为毛.更新文件 ...

  7. JS小福利 —— 实时更新的页面小时钟

    今天小女刚学会了一个好玩的小玩意儿~~特来跟大家分享一下~~~ 这是一个有趣的时钟显示程序,可以进行实时的年月日.星期.时分秒更新,有了这组小代码,以后可以作为日期插件应用在大型的JS代码中哦~~ 积 ...

  8. node webkit(nw.js) 设置自动更新

    原理:把更新的文件放在服务器上,设置一个客户端版本号,每次打开客户端的时候,通过接口获取服务器上的版本,如果高于本地的版本就下载服务器上的代码,低于或等于就不更新 <script> var ...

  9. FastAdmin 浏览器 JS CSS 缓存如何更新?

    由于代码修改,但文件名没有修改,因为浏览器对 JS 和 CSS 是缓存的,而且由于服务器无法控制客户端的缓存. 但是可以对 JS 和 CSS 的请求加上版本号,达到更新缓存的效果.

随机推荐

  1. 适合初学者的一个分布式环境搭建过程(spring boot + zookeeper + dubbo + mybatis + mysql)

    本人也是才开始接触 阿里巴巴的开源分布式框架 dubbo,因为现在微服务框架 spring boot也非常的火,然后结合dubbo的官网搭建这个开发环境. 一.首先 zookeeper作为集群管理服务 ...

  2. gradle的安装,配置,构建,研究,初体验......(入职一周研究的第一个大知识点)

    (1)Gradle是一个基于Apache Ant和Apache Maven概念的项目自动化构建工具.它使用一种基于Groovy的特定领域语言(DSL)来声明项目设置,抛弃了基于XML的各种繁琐配置.更 ...

  3. Bootstrap提示框

    前面的话 提示框是一个比较常见的功能,一般来说是鼠标移动到特定元素上时,显示相关的提示语.本文将详细介绍Bootstrap提示框 基本用法 Bootstrap框架中的提示框,结构非常简单,常常使用的是 ...

  4. python多线程爬虫设计及实现示例

    爬虫的基本步骤分为:获取,解析,存储.假设这里获取和存储为io密集型(访问网络和数据存储),解析为cpu密集型.那么在设计多线程爬虫时主要有两种方案:第一种方案是一个线程完成三个步骤,然后运行多个线程 ...

  5. es6知识总结--3

    es6知识总结--3 es6对咱们es3,es5的数据类型进行了升级下边说新APIs! js数据类型有Number.String .oject.Boolean.Null.Undefined六种数据类型 ...

  6. 第一章:eclipse 中修改字体大小和编码格式

    eclipse 中修改字体大小的步骤: 1. 在 eclipse 的工具栏中,找到 weindows 下面的 preferences 2. 在 preferences 的 输出 font ,在 Bas ...

  7. 如何删除svn标签

    SVN是Subversion的简称,是一个开放源代码的版本控制系统,对于多人共同开发同一个项目非常有用,但有一点比较让人头疼,那就是项目移植的时候,原来的文件夹带有很多可恶的svn标签,使其不能add ...

  8. iOS图解多线程

    前言 多线程一直是iOS开发中重中之重的话题,无论是面试还是真正在公司中进行业务开发,都会经常使用到多线程来开发.笔者在简书上看到一张图,记录的是多线程的相关知识,笔者认为这是非常好的,推荐给大家: ...

  9. tensorflow tanh应用

    1.tanh()函数 tanh是双曲函数中的一个,tanh()为双曲正切. 双曲正切函数的导数公式:   2.tensorflow tanh()例子 import tensorflow as tf i ...

  10. FillConsoleOutputAttribute 函数--指定区域填充控制台输出属性

    FillConsoleOutputAttribute函数 来源:https://msdn.microsoft.com/en-us/library/windows/desktop/ms682663(v= ...