gltf模型详解

一、文件结构

  一个gltf模型一般包含一下几个文件:
  .bin   二进制文件存放顶点、法线、面、uv等数据
  .gltf  描述模型的综合信息
  img文件夹  存放模型贴图

二、gltf模型信息详解

1、数据结构:
  {
    scene:0,
    scenes:[{nodes:0}],
    nodes:[
      {
         name:"rootModel",
         children:[1]
      },
      {
         name:"floor1",
         children:[2]
      },
      {
         name:"room1",
         mesh:0
      }
    ],
    meshes:[
        {
          name:"mesh1",
          primitives:[
              {attributes:{POSITION:0, NORMAL :1,TEXCOORD_0:0},indices:0, material:0,mode:4}
          ]
        }
    ],
    accessors:[
        {name:"postions_0", componentType:5126, count:100, bufferView:0, byteOffset:0,type:"VEC3",max:[],min:[]},
        {name:"texcoods_0", componentType:5126, count:100, bufferView:0, byteOffset:0,type:"VEC2",max:[],min:[]},
        {name:"indices_0", componentType:5123, count:100, bufferView:0, byteOffset:0,type:"SCALAR",max:[],min:[]}
    ],
    bufferViews:[
        {name:"view0",buffer:0,byteLength: 144, byteOffset: 0, byteStride: 12, target: 34962},
        {name:"view1",buffer:0,byteLength: 100, byteOffset: 144, byteStride: 8, target: 34962},
        {name:"view2",buffer:0,byteLength: 100, byteOffset: 244, byteStride: 8, target: 34962}
    ],
    buffers:[{name:1,uri:"1.bin"}],
    materials:[
        {name:"m0", pbrMetallicRoughness:{baseColorTexture:{index:0}}}
    ],
    textures:[{name:"t0",source:0}],
    images:[{name:"img0",uri:"1.jpg"]
    animations:[]
  }
2、概述

模型加载顺序为,先加载gltf文件,然后解析依次读取scenes、nodes、meshes、accessors、bufferViews、buffers、materials、textures、images。其中每个mesh包括一个bufferViews和一个materials。每一层的递进都有数组下标来确定。

3、各字段详解
scenes 场景
  scenes:[{nodes:0}],
  scene:0

一般模型只有一个也是默认场景,如果是多个,则根据对应的scene字段确定哪一个是默认场景,参考数据结构部分的数据,每一个scene都包含一个nodes字段,指定了scene的根结点。本例中nodes对应的值为0,代表根节点为nodes字段下对应的第一个元素。

nodes 节点
  nodes:[
  {
     name:"rootModel",
     children:[1]
  },
  {
     name:"floor1",
     children:[2]
  },
  {
     name:"room1",
     mesh:0
  }
]

nodes用来组装模型层级,第一个节点是父节点,children字段指定它所包含的子节点。
nodes节点分为俩种,一种是有children字段的,最终会渲染成group,一种是有mesh字段的最终渲染为mesh,mesh字段的值为meshes数组的小标。

meshes 网络
  meshes:[
    {
      name:"mesh1",
      primitives:[
          {attributes:{POSITION:0, NORMAL :1,TEXCOORD_0:0},indices:0, material:0,mode:4}
      ]
    }
]

网,由多个面和材质组成,通过primitives字段指定。

  • attributes 指定了顶点、顶点法线、uv坐标在accessors数组的对应数据的下标。
    POSITION - 顶点
    NORMAL - 顶点法线,顶点法线不是必须,导入引擎 时可生成
    TEXCOORD_0 - uv坐标
  • indices 指定了面在accessors数组的对应数据的下标
  • material 指定了该mesh的材质在materials数组中的下标
accessors 访问器
  accessors:[
        {name:"postions_0", componentType:5126, count:100, bufferView:0, byteOffset:0,type:"VEC3",max:[],min:[]}
  ]

访问器是链接bufferView和mesh之间的桥梁,主要作用是对bufferView中数据进行进一步描述

  • componentType 数据类型浮点或者整形
  • count 数量总和(顶点总数或者面总数,通过此字段可计算模型的总顶点数、总面数,对于模型性能分析和优化有很大作用
  • bufferView 对应数据在bufferViews中的下标
bufferViews 缓冲区视图
   bufferViews:[ 
      {name:"view0",buffer:0,byteLength: 144, byteOffset: 0, byteStride: 12, target: 34962}
   ]
  • buffer 对应的数据在buffers数组中的下标
  • byteLength 该缓冲区对于的数据长度
  • byteOffset 在buffer中的起始位置
buffers 缓冲区
  buffers:[{name:1,uri:"1.bin"}]
  • uri 该缓冲区对于的bin文件,bin文件的作用参考第一部分的介绍
从mesh走到bin文件,模型的骨骼已经确定了,顶点、法线、面、都有了,剩下的就是给模型添加材质贴图,这一部分也是从mesh出发,由mesh下的material字段指定对应的材质
materials 材质
  materials:[
        {name:"m0", pbrMetallicRoughness:{baseColorTexture:{index:0}}}
    ]
  • baseColorTexture 对应textures数组下标
textures 纹理
  textures:[{name:"t0",source:0}],
  • source 对应images数组下标
images 贴图
  images:[{name:"img0",uri:"1.jpg"]

总的流程如下图


gltf加载流程图

三、内存占用

模型的内存占用主要包括四个部分,分别是内存、gpu、图片缓存、cpu,其中主要决定性能的是gpu和图片缓存

  • gpu :

对于gpu占用主要有一个问题,如何计算一个模型的gpu占用,有没有公式可以算出来?针对这个问题就需要搞明白那些因素决定gpu占用。

模型内存占用图

通过上图很容易看出,gpu主要有面数、顶点数、和纹理数、图片尺寸有关,仔细观察顶点和面其实存在正相关的,纹理数和图片尺寸其实都属于材质,进而可以得出gpu=f(geomotry)+f(材质)。
对于材质我们很容易得出f(材质)=4*1.33*\sum_{0}^{n}T_{n} .w*T_{n} .h
分析f(geomotry),实际是有点、面、法线、uv等数据决定,其中大致可分为三部分,原始数据占用、顶点着色器运行占用、片元着色器占有。第一部分物理内存占用可精确算出,但后俩者只能粗略估算,因为三角面可能共用顶点,gpu的计算能力也有波动,着色器程序更不可能相同,因此我们只能估算,但面可能存在共点的情况,因此我们可用一个顶点的最大内存占用*顶点数估计一个这部分的最大gpu占用,所以

f(geomotry)=faces*f(fshader)+points*(4*8+f(vshader))

为了方便计算,我们用面替换点,一个最多对应3个点因此可得

f(geomotry)=faces*(f(fshader)+96+3*f(vshader)

仔细观察数据表,大致可以得出绘制一个面的所占有的内存在0.8kb-3.1kb之间,我们取最大值并向上取整,可以大致按每个面3.1k的内存去估算

最终可得出:
f(gpu) <= 4*1.33*\sum_{0}^{n}T_{n} .w*T_{n} .h+faces*3.1
(T代表单个材质,faces代表模型总面数)

  • 图片缓存 :

图片缓存有很多文章说明,基本公式是
f=\sum_{0}^{n}img_{n}+4*img_{n}.w*img_{n}.h
图片缓存其实是纹理缓存,对于场景切换,需要注意注销纹理,释放图片缓存。

四、性能优化

有了上面的内存占用,性能优化的点就比较清晰了

  • 模型面数优化
    可以在制作模型时,通过原始数据减少模型面数,也可以通过脚本动态减面,例如threejs的SimplifyModifier。
  • 纹理优化


    纹理渲染流程图

如果不追求过高的效果,可以直接减小图片尺寸,如果对与效果要求很高,可以采用压缩纹理技术

不同格式gpu占用

压缩纹理方法主要是以上几种,优点是可以减少gpu占用提高渲染效率和初次渲染速度,缺点是这几种文件通常会大于png图片,而且兼容性很差,这个问题直到去年有了比较好的解决方案 - basis_universal(https://github.com/BinomialLLC/basis_universal),谷歌联合各大厂商解决了兼容性问题,原理参考下图,basis文件在gpu端可根据不同设备的情况快速转换为它支持的压缩格式
basis纹理

  • 减少drawcall
    减少cpu与gpu通信次数,类似于雪碧图技术,可通过合并mesh合并材质等方法达到
    (附上一个查看工具:spector)
  • 光照
    适当减少光源数,尽量用贴图代替光照效果,如不需要光照剔除法线亦可减小模型物理内存,提供渲染效率
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342

推荐阅读更多精彩内容