草庐IT

glTF格式详解

jieqiang3 2023-10-10 原文

文章目录

简介

本文主要通过总结glTF文件格式来对3d模型有一个整体的认识概念,对glTF有了一个清晰的概念后我们可以简单试下如何解析一个glTF文件为模型,并且简单介绍下我对glTF文件在实际项目中应用优化的落地实践。

glTF作为一个标准的3d场景和模型文件格式,类似于图形中的jpeg,有两种文件扩展名:.gltf(JSON/ASCII)或者.gbl(二进制文件)两种类型。下图我们可以看到我在blender里简单放了一个cube后导出glTF2.0文件的话可以选择以下三种格式:glTF二进制(.glb)、glTF分离(.gltf+bin+纹理)、glTF嵌入式(.gltf)。

.gltf+bin+纹理

由于glb和glTF嵌入式主要是把所有的二进制 纹理数据全部打入到了一个文件中,道理是一样的,所以我们这里重点来拆解gltf+bin+纹理结构类型。

我们先来看一个.gltf+bin+纹理图片的文件目录例子,.gltf其实就是一个json文件,里面包括了3d 模型的场景的结构和组成。我们先来看这个json文件下的top-level elements:

这其中包含了一个模型场景中必备的几个元素:
asset标签里介绍了glTF版本这些基础信息;
scene,scenes,nodes标签里包含了一个场景的基本元素;
materials标签里包含了每个对象是如何呈现出来的定义;
meshes 标签里定义了所有的3d对象的几何形状;
textures, images, samplers标签里包含了物体的表面外观,贴纸等信息;
accessors, buffers, bufferViews标签里包含了整个3d模型场景里的数据引用和数据布局的描述。

当然还有我这个gltf里不包括的,也很常见的几个标签元素:
cameras 标签里包含了查看场景的配置信息;
上面标签信息我们下文都会有各自的案例介绍来帮助大家更全面的理解。

asset


glTF2.0版本以及是blender导出的等基础信息。关于glTF2.0与1.0的更新区别 可以参考这里

scene, scenes, nodes

我们先来看我们这个gltf文件的结构

这代表我们scene里有一个场景,这个场景下包括了19个nodes节点,具体每个节点的属性我们看nodes节点,

nodes作为一个jsonArrayList包括了rotation旋转信息,scale大小 信息,translation位移信息等。然后每个节点都会指向一个mesh或者一个camera,上图中我们的模型中的nodes就指向的是meshid为0,name为转义过后的\ue573\u9762的mesh。然后在渲染的时候会将这些元素全部附加到这些个节点上,创建这些元素的实例就会使用节点上的各种旋转信息,大小信息,位移信息等进行变换了。
当然如果你的模型中出现group的时候,那么你的nodes下的节点会出现children,children是一个数组,里面的id对应的是nodes节点下的nodes[x],即为nodes[x]是有children的节点的children。
另外在模型场景中如果我们对某个mesh做动画的话,这里的translation,scale,rotation都是可变的,会随着动画的进行而重新建模。

materials

ok我们接下来来看materials,在3d设计中,要创造出逼真的效果,就必须弄清楚材质属性。材质的意思就是虚拟中模拟物体真实的物体性质,例如颜色、反光、透明、贴图等;而材质球就是对这个材质的属性整合的统称。
这里我们重点来看pbrMetallicRoughness属性,baseColorTexture属性是应用于对象的主要纹理,基本颜色因子包括了三个基础颜色,RED GREEN BLUE和代表alpha的比例因子。如果没有使用纹理的话,这些值的意思就是金属粗糙度纹理的颜色。粗糙度纹理包含金属度值“蓝色”颜色通道,粗糙度值包含“绿色”颜色通道。
metallicFactor代表了金属系数,很明显我们上面的模型中金属系数为0。
当然除了金属粗糙度以外,material可能还包括其他影响对象外观的特性,这里建议直接在threejs editor里调整好再用于开发,editor上可以很直观的看到金属感和粗糙度上变更后效果的差异。

meshes


mesh的意思其实就是模型的几何体,与material配合,每个网格图元都有一个渲染模式,该模式是一个常量,指示是否应渲染为点。线。或三角形基本体还引用顶点的音调和属性,使用访问器的属性来访问其数据。这里我们主要来看primitives属性,primitives引用了顶点的vertices和属性,使用访问器的属性就可以直接访问他的数据了。每个属性都是通过将attributename映射到包含属性数据的访问器的索引来定义的。渲染网格时,此数据将用作顶点属性。例如,属性可以定义顶点的位置和法线:

textures, images, samplers




textures包含有关可应用于渲染对象的纹理的信息:材质引用纹理来定义对象的基本颜色,以及影响对象外观的物理属性。
这些图像定义了用于纹理的imagedata,数据通过一个URI(图像在文件中的位置)来给定,或者通过引用一个bufferView和一个MIME类型来定义存储在缓冲区视图中的iamgedata类型。这个很好理解

accessors, buffers, bufferViews




buffers包含了用于三维模型几何体、动画和蒙皮的数据缓冲区视图等结构信息。每个缓冲区使用URL引用一个二进制数据文件。它是一个原始数据块的源,每个缓冲区视图引用一个缓冲区。它是字节偏移量(byteoffset)和字节长度(abytelength),定义属于bufferview的缓冲区的部分,以及可选的Openglbuffer目标。访问器定义如何对bufferview的数据进行交互。它们可以定义引用bufferview开始的附加字节偏移量,并包含有关bufferview数据类型和布局的信息。
其实可以这么理解,例如,当类型为“VEC2”且组件类型为GL FLOAT(5126)时,可以将数据定义为浮点值的二维向量。所有值的范围存储在“最小值”和“最大值”属性中。多个存取器的数据可以在bufferview中交错。在这种情况下。bufferview将有一个bytestride属性,该属性表示Accessor的一个元素的开头之间有多少字节。

cameras

camera总共分为两种,透视相机和正交相机,它们定义了投影矩阵。透视camerazfar的远剪裁平面距离的值是可选的。当省略时,摄像机会为无限投影使用一个特殊的投影矩阵。
当文件中一个节点引用摄像机时,就可以理解成创建了一个相机的实例,该相机实例的摄像机矩阵其实就是该node下的transform、scale等变换矩阵给到的。

压缩优化

图片压缩

上面我们看到其实一个模型的texutures是直接引用外部的URI链接来引用图片然后链接到面上的贴图,那么如果从图片角度,我们就可以做到图片的压缩来实现模型的压缩。
blender里导出的默认是png格式的图片,那么我们就可以转换成webp格式,至于webp格式比png的区别,如果不知道的朋友可以去看这篇webp相对png、jpg有什么优势

经测试,图一中的模型,压缩前为2.21MB,压缩后为1.01MB,压缩率可见一斑,而且如果你是webp无损压缩的话,理论上来说模型效果其实没啥影响。

这里推荐一个官方出的插件,EXT_texture_webp.

当然这个方案也有一个问题,如果你是使用threejs 的话,webp的不支持浏览器类型和threejs 不支持的浏览器有所出入,适配上需要考虑到这一点。

EXT_meshopt_compression

刚才我们压缩针对的是texture图片资源,那么这个方案我们针对的就是glTF文件中的各种二进制数据了,理论上来说glTF文件中的二进制数据包含了各种顶点属性数据、索引数据、变形目标增量、动画输入输出数据,他们可能会占据整个glTF文件中很大一部分,所以这些二进制数据我们可以采用gzip通用压缩的形式去处理。

使用方式:

gltfpack -i untitled.glb -o untitled-meshopt-compression.glb -cc

经测试,图一中的模型,压缩前为2.21mb,压缩后为1.45mb。

总结

其实gltf官方还给了我们很多优秀的压缩插件,具体我们可以查看glTF官方git地址,https://github.com/KhronosGroup/glTF 查阅。

有关glTF格式详解的更多相关文章

  1. ruby - 使用 ruby​​ 将 HTML 转换为纯文本并维护结构/格式 - 2

    我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h

  2. ruby-on-rails - 将 Ruby 中的日期/时间格式化为 YYYY-MM-DD HH :MM:SS - 2

    这个问题在这里已经有了答案:Railsformattingdate(4个答案)关闭4年前。我想格式化Time.Now函数以显示YYYY-MM-DDHH:MM:SS而不是:“2018-03-0909:47:19+0000”该函数需要放在时间中.现在功能。require‘roo’require‘roo-xls’require‘byebug’file_name=ARGV.first||“Template.xlsx”excel_file=Roo::Spreadsheet.open(“./#{file_name}“,extension::xlsx)xml=Nokogiri::XML::Build

  3. ruby - 我可以将我的 README.textile 以正确的格式放入我的 RDoc 中吗? - 2

    我喜欢使用Textile或Markdown为我的项目编写自述文件,但是当我生成RDoc时,自述文件被解释为RDoc并且看起来非常糟糕。有没有办法让RDoc通过RedCloth或BlueCloth而不是它自己的格式化程序运行文件?它可以配置为自动检测文件后缀的格式吗?(例如README.textile通过RedCloth运行,但README.mdown通过BlueCloth运行) 最佳答案 使用YARD直接代替RDoc将允许您包含Textile或Markdown文件,只要它们的文件后缀是合理的。我经常使用类似于以下Rake任务的东西:

  4. ruby - 是否有用于序列化和反序列化各种格式的对象层次结构的模式? - 2

    给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最

  5. ruby-on-rails - 事件管理员日期过滤器日期格式自定义 - 2

    是否有简单的方法来更改默认ISO格式(yyyy-mm-dd)的ActiveAdmin日期过滤器显示格式? 最佳答案 您可以像这样为日期选择器提供额外的选项,而不是覆盖js:=f.input:my_date,as::datepicker,datepicker_options:{dateFormat:"mm/dd/yy"} 关于ruby-on-rails-事件管理员日期过滤器日期格式自定义,我们在StackOverflow上找到一个类似的问题: https://s

  6. ruby-on-rails - 如何在 Rails 中设置路由的默认格式? - 2

    路由有如下代码:resources:orders,only:[:create],defaults:{format:'json'}resources:users,only:[:create,:update],defaults:{format:'json'}resources:delivery_types,only:[:index],defaults:{format:'json'}resources:time_corrections,only:[:index],defaults:{format:'json'}是否可以使用1个字符串为所有资源设置默认格式,每行不带“默认值”散列?谢谢。

  7. ruby-on-rails - Rails 4 WYSIWYG Bootsy 不显示格式 - 2

    我刚刚按照thebootsygempage上的安装说明进行操作在我保存并查看帖子内容之前,一切看起来都不错。这是输出在View中的样子:HeaderSubhead:似乎没有呈现任何html格式,因为它被引号或类似的东西转义了-其他人有这个问题吗?我没有在github页面或SO上看到任何问题来指出我正确的方向。除了遵循gem安装说明之外,我还没有做任何事情,但也许我错过了什么或者只是犯了一个愚蠢的错误。如果你还有什么想知道的,请尽管问。干杯 最佳答案 你需要有这样的东西,转义html: 关

  8. ruby - 在 Ruby 中将整数格式化为固定长度的字符串 - 2

    有没有一种简单的方法可以将给定的整数格式化为具有固定长度和前导零的字符串?#convertnumberstostringsoffixedlength3[1,12,123,1234].map{|e|???}=>["001","012","123","234"]我找到了解决方案,但也许还有更聪明的方法。format('%03d',e)[-3..-1] 最佳答案 如何使用%1000而不是进行字符串操作来获取最后三位数字?[1,12,123,1234].map{|e|format('%03d',e%1000)}更新:根据theTinMan的

  9. ruby-on-rails - 如何正确格式化字符串,如 'mccdougal' 到 'McDougal' - 2

    什么Ruby或RailsDSL会将字符串"mccdougal"格式化为"McDougal",同时留下字符串"McDougal"原样?将titleize传递给"McDougal"结果如下:"McDougal".titleize#=>"McDougal" 最佳答案 据我所知,没有可以处理这种情况的Rails助手。这是一个非标准的边缘案例,需要特殊处理。但是,您可以创建自定义字符串变形。您可以将这段代码放入初始化程序中:ActiveSupport::Inflector.inflections(:en)do|inflect|inflect.

  10. ruby-on-rails - Rails 格式验证——字母数字,但不是纯数字 - 2

    什么是测试格式验证的最佳方法让我们说一个用户名,使用字母数字的正则表达式,但不是纯数字?我一直在我的模型中使用以下验证validates:username,:format=>{:with=>/^[a-z0-9]+[-a-z0-9]*[a-z0-9]+$/i}数字用户名(例如“342”)通过了验证,这是我不想要的。 最佳答案 您想“向前看”一封信:/\A(?=.*[a-z])[a-z\d]+\Z/i 关于ruby-on-rails-Rails格式验证——字母数字,但不是纯数字,我们在Sta

随机推荐