背景

之前尝试过利用多个纹理单元,再基于传入给 shader 的 vertexBuffer 信息决定选 1 号纹理单元还是 2 号纹理单元。
虽然理论上,这个方式确实行得通,但是一次 drawcall 绘制多个纹理,本来目的是为了提高绘制性能,而实际上却无法提高性能,甚至还有反作用。
因为有说法是 shader 分支会降低 GPU 性能,详见:https://blog.csdn.net/qq_31788759/article/details/107248224

那么,是否还有其他批量绘制不同纹理的方式呢?

WebGL 2 sampler2DArray

WebGL 2 带来了一个新的数据结构:sampler2DArray。这个东西,还是占用一个纹理单元,但传入的不是一个纹理,而是一个纹理数组。
关键 shader 代码:

#version 300 es
precision mediump float;
precision mediump sampler2DArray;

// our texture
uniform sampler2DArray u_image;
// the texCoords passed in from the vertex shader.
in vec3 v_texCoord;
// we need to declare an output for the fragment shader
out vec4 outColor;

void main() {
    outColor = texture(u_image, v_texCoord); // 这里从一般的 vec2 变成了 vec3,第三个元素是纹理 index
}
</script>

相对于普通的 2d 纹理渲染,最关键的是 JS 如何传递纹理到 GPU。这里关键是类型 gl.TEXTURE_2D 变成 gl.TEXTURE_2D_ARRAY,texImage2D 变成 texImage3D。
有两种方式传递纹理:

  • 可以把多个纹理合并到一个大图,一次性推送;
  • 也可以先预申请空间,再用 texSubImage3D 逐个推送。
    关键 JS 代码:

    var canvas = document.createElement('canvas');
    canvas.width = imgs[0].width;
    canvas.height = imgs[0].height * imgs.length;
    var ctx = canvas.getContext('2d');
    for (let i = 0; i < imgs.length; i++) {
      ctx.drawImage(imgs[i], 0, imgs[0].height * i);
    }
    var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
    var pixels = new Uint8Array(imageData.data.buffer);
    gl.texImage3D(
      gl.TEXTURE_2D_ARRAY,
      0, // level 暂时不知道什么地方会用到
      gl.RGBA, // internalFormat
      imgs[0].width, // width
      imgs[0].height, // height
      imgs.length, // depth 多少个纹理
      0, // border
      gl.RGBA, // format
      gl.UNSIGNED_BYTE, // type
      pixels
    );

    // 利用 texImage3D 设定空间,再用 texSubImage3D 设置图片。texImage3D 创建空间时,宽高设定很重要,后续的尺寸不能超过这个尺寸。如果后续尺寸比这个小,会导致渲染时图片偏小,也很好理解,因为 subImage 传递的只是左上角一小部分的图片,其他面积为空白。
    gl.texImage3D(
      gl.TEXTURE_2D_ARRAY,
      0, // level 暂时不知道什么地方会用到
      gl.RGBA, // internalFormat
      imgs[0].width, // width
      imgs[0].height, // height
      imgs.length, // depth 多少个纹理
      0, // border
      gl.RGBA, // format
      gl.UNSIGNED_BYTE, // type
      null
    );
    /**
     * 用 texStorage3D 搭配 texSubImage3D 也可以。但这里需要时 RGBA8 不是 RGBA。
     * 因为选项里边没有 RGBA https://developer.mozilla.org/en-US/docs/Web/API/WebGL2RenderingContext/texStorage3D
     * 另外,这里不是指输入图片的格式,是指把图片转为什么格式存储。
     * texImage3D 的 gl.RGBA 默认情况下跟 gl.RGBA8 一致
     */
    // gl.texStorage3D(gl.TEXTURE_2D_ARRAY, 1, gl.RGBA8, imgs[0].width, imgs[0].height,3);
    gl.texSubImage3D(
      gl.TEXTURE_2D_ARRAY,
      0, // level 暂时不知道什么地方会用到
      0, // x offset
      0, // y offset
      0, // z offset 不能超过前边预留的 depth
      imgs[0].width, // width
      imgs[0].height, // height
      1, // depth 多少个纹理
      gl.RGBA, // format
      gl.UNSIGNED_BYTE, // type
      imgs[0]
    );
    gl.texSubImage3D(
      gl.TEXTURE_2D_ARRAY,
      0, // level 暂时不知道什么地方会用到
      0,
      0,
      1,
      imgs[0].width, // width
      imgs[0].height, // height
      1, // depth 多少个纹理
      gl.RGBA, // format
      gl.UNSIGNED_BYTE, // type
      imgs[1]
    );
    gl.texSubImage3D(
      gl.TEXTURE_2D_ARRAY,
      0, // level 暂时不知道什么地方会用到
      0,
      0,
      2,
      imgs[0].width, // width
      imgs[0].height, // height
      1, // depth 多少个纹理
      gl.RGBA, // format
      gl.UNSIGNED_BYTE, // type
      imgs[2]
    );


详细代码请见:https://github.com/kenkozheng/HTML5_research/tree/master/WebGL/sampler2DArray

参考资料

https://www.nxrte.com/jishu/19240.html
https://juejin.cn/post/6844903846678888461
https://developer.mozilla.org/en-US/docs/Web/API/WebGL2RenderingContext/texImage3D
https://github.com/WebGLSamples/WebGL2Samples/blob/master/samples/texture_2d_array.html

posted @ 2023-10-24 11:52 拂晓风起-Kenko 阅读(446) 评论(0) 推荐(0) 编辑
摘要: 这个技术可以用于 UI 自动化等,用脚本自动控制 Android webview 或注入 js 执行。 前提条件:usb 连接手机,adb 能查找到 devices,如果有问题,多拔插几次,确认授权。 第一步,确认安卓 App 编译时已经启用了webview 允许调试(一般是允许的,可以通过连接us 阅读全文
posted @ 2021-08-19 15:30 拂晓风起-Kenko 阅读(1446) 评论(0) 推荐(0) 编辑
摘要: 众所周知,async await 只是 Promise 的语法糖,但具体是什么语法糖,我自己之前也没细究。 昨天在研究 iOS JavaScriptCore 里边如何捕获未处理的 Promise rejection,发现 jscore 本身并不提供任何接口,只能想其他办法绕过去。 参考了 Egret 阅读全文
posted @ 2021-08-06 11:16 拂晓风起-Kenko 阅读(1155) 评论(0) 推荐(0) 编辑
摘要: 大部分同学了解Promise,也知道async await可以实现同步化写法,但实际上对一些细节没有理解到位,就容易导致实际项目中遇到问题。 开始先抛结论,下文将针对主要问题点进行论述。 1、所有async方法调用,必须加await或catch,捕获错误(等待就用await,无需等待就用catch) 阅读全文
posted @ 2020-11-20 12:20 拂晓风起-Kenko 阅读(3972) 评论(1) 推荐(0) 编辑
摘要: 可以分析JS Heap等多种内存占用情况的变化 其中,比较难理解的是Documents。这个代表的是目前tab的内存有多少个Documents,包括当前页面、之前的页面、iframe和插件产生的页面。 如上图1-1表示当前只有1个,就是可见的页面。如果刷新一下再录制,就会变成2-2,再刷新录制,可能 阅读全文
posted @ 2020-11-19 11:23 拂晓风起-Kenko 阅读(1066) 评论(0) 推荐(0) 编辑
摘要: 现象 总体而言,iOS 14 渲染性能变差,可以从以下几个测试看出。 测试1:简单demo,使用egret引擎显示3000个图(都是同一个100*100 png 纹理),逐帧做旋转。(博客园视频播放可能有问题,视频地址:https://github.com/kenkozheng/kenkozheng 阅读全文
posted @ 2020-10-28 20:00 拂晓风起-Kenko 阅读(2167) 评论(0) 推荐(0) 编辑
摘要: Demo: http://kenkozheng.github.io/WebGL/multi-texture-in-one-drawcall/index.html 关键点: 1、fragment shader接受参数(从vertex shader传递vary),动态指定sampler 2、设置samp 阅读全文
posted @ 2020-07-24 18:16 拂晓风起-Kenko 阅读(844) 评论(0) 推荐(0) 编辑
摘要: 本人前端工作10年,结合了常见的"前端技术栈"文章,另外加上我在实际工作的经验(尤其是腾讯、字节跳动工作),汇总而成得到本文的前端技术栈。 在一般技术栈基础上,我标记了4种颜色,分别代表初级入门、工程师、资深工程师、高级工程师的技能要求。 这个划分,不直接对等某个公司的职级,只是我个人的一个简单划分 阅读全文
posted @ 2020-05-28 14:37 拂晓风起-Kenko 阅读(4543) 评论(0) 推荐(0) 编辑
摘要: 我的博客即将搬运同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support plan?invite_code=i5j7gwrxj9x5 阅读全文
posted @ 2018-07-02 11:36 拂晓风起-Kenko 阅读(1483) 评论(0) 推荐(0) 编辑
摘要: 很多人,包括我自己,初看Service Worker多一个Cache Storage的时候,就感觉跟HTTP长缓存没什么区别。 例如大家讲的最多的Service Worker能让网页离线使用,但熟悉HTTP缓存的朋友,会发现,把整站所有资源设置为长缓存(不带校验),也可以实现离线使用。 那么,Ser 阅读全文
posted @ 2018-04-07 09:39 拂晓风起-Kenko 阅读(4346) 评论(0) 推荐(0) 编辑
点击右上角即可分享
微信分享提示