一、什么是gltf
- GLTF代表 Graphics Language Transmission Format(图形语言传输格式)。这种跨平台格式已成为Web上的3D对象标准。
- 几乎每个框架都支持GLTF,3D模型可以优选GLTF格式。
- GLTF是对近二十年来各种3D格式的总结,使用最优的数据结构,来保证最大的兼容性以及可伸缩性。GLTF使用json格式进行描述(本质是一个json文件)。GlTF可以包括场景、摄像机、动画等,也可以包括网格、材质、纹理,甚至包括了渲染技术、着色器以及着色器程序。由于json格式的特点,它支持预留一般以及特定供应商的扩展。
1. gltf的两种存储方式
- 嵌入式(所有的数据都在xxx.gltf的文件中);
- 分离式(gltf+bin+image);
2. glb与gltf的关系
GLB文件是.GLTF文件的二进制版本。
glTF是一种高效、可扩展、可互操作的格式,用于传输和加载3D场景和模型。glTF格式的目标包括最大限度地减少文件大小、快速加载、完整的3D场景表示、运行时独立性以及伴随进一步开发的可扩展性。glTF的提出是源自于3D工业和媒体发展的过程中,对3D格式统一化的急迫需求。
GLB是glTF模型的二进制文件格式表示,它存储了glTF的组件,如JSON、BIN文件和图片。GLB避免了使用glTF格式文件变大的问题,通过压缩,GLB能更快地加载,提供完整的三维场景,且能在未来的开发中扩展。
二、gltf的组织架构
- scene: 场景,一个gltf文件中可能存储多个场景,每个场景都包含一个或者多个node;
"scene":0,
"scenes":[
{
"name":"Scene",
"nodes":[
2
]
}
],
- node:场景图层次结构中的一个节点,每个节点都node可以包含一个名为 的数组children,该数组包含其子节点的索引(也就是说,第三个node节点可以包含前两个node节点的信息);
"nodes":[
{
"mesh":0,
"name":"Object_2"
},
{
"children":[
0
],
"name":"apple_test_scale2.obj.cleaner.materialmerger.gles"
},
{
"children":[
1
],
"name":"Sketchfab_model",
"rotation":[
-0.70710688829422,
0,
0,
0.7071066498756409
]
}
],
- camera:渲染场景的视图配置;
- mesh:表示场景中的几何对象,它包含:accessor是用于访问实际几何数据的对象,material几何对象渲染时的外观对象;
- skin:顶点参数信息,来源:accessor;
- animation:节点变化(如:旋转、平移);
- accessor:抽象数据源,数据访问器(mesh、skin、animation的数据来源);
- material:定义几何对象外观参数;
- texture:包含sampler和image,其使用source和sampler指向图片和采样器;
- images:通过 uri定位到图片资源
- sampler:定义图片的采样和滤波方式
gltf格式详解:
译文:https://zhuanlan.zhihu.com/p/65267849
原文:https://github.com/fangcun010/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_003_MinimalGltfFile.md
补充小知识:
-
bufferViews中的target对应的数值表示数据在buffer中的组织形式:
渲染器可以使用此属性来分类缓冲区视图所引用的数据的类型或性质。target可以是一个常量指示数据是用于顶点属性(34962,表示ARRAY_BUFFER),或者该数据被用于顶点索引(34963,表示ELEMENT_ARRAY_BUFFER); -
材质与贴图的区别:
材质包括贴图,材质是一个数据集,主要功能就是给渲染器提供数据和光照算法。贴图就是其中数据的一部分。材质是基础,细节靠纹理和贴图来呈现。
材质用来指定物体的表面或数个面的特性,它决定这些平面在着色时的特性,如颜色,光亮程度,自发光度及不透明度等;制定到材质上的图形则称为贴图。
三、glb转gltf
安装gltf-pipeline工具
参考来源:https://npmmirror.com/package/gltf-pipeline
全局安装方式:
npm install -g gltf-pipeline
gltf转换成glb
gltf-pipeline -i model.gltf -o model.glb
gltf-pipeline -i model.gltf -b
glb转换成gltf
gltf-pipeline -i model.glb -o model.gltf
gltf-pipeline -i model.glb -j
单独输出模型的贴图文件
gltf-pipeline -i model.gltf -t
四、glb中贴图的替换
public String replaceGltfTexture(String gltfURL, String textureURL) {
File tempFile = null;
File tempGltf = null;
try {
//1.解析数据,获取glb模型对象
GltfModelReader modelReader = new GltfModelReader();
DefaultGltfModel gltfModel = (DefaultGltfModel) modelReader.read(new URI(gltfURL));
//2.材质对象
//根据修改的材质的索引信息获取贴图的对象,meshIndex(修改的材质在模型中meshes数组的索引值)meshPrimitiveIndex (材质中primitives修改的primitive的第几个索引值)
MaterialModelV2 materialModel =
(MaterialModelV2) gltfModel.getMeshModel(0).getMeshPrimitiveModels().get(0).getMaterialModel();
//3.贴图对象
DefaultTextureModel baseColorTexture = (DefaultTextureModel) materialModel.getBaseColorTexture();
//4.换贴图 xxx.png贴图文件
replaceMaterialImage(gltfModel, textureURL, baseColorTexture);
//5.将更换贴图的模型输出
GltfModelWriter gltfModelWriter = new GltfModelWriter();
tempFile = File.createTempFile("模型文件贴图替换", System.nanoTime() + ".glb");
gltfModelWriter.writeBinary(gltfModel, tempFile);
//6.glb转成gltf文件
tempGltf = new File("/tmp/" + tempFile.getName().substring(0, tempFile.getName().lastIndexOf(".")) + ".gltf");
if (!tempFile.exists()) {
tempGltf.createNewFile();
}
log.info("命令执行前:{}", tempFile);
String[] cmd = {"gltf-pipeline", "-i", tempFile.getAbsolutePath(), "-o", tempGltf.getAbsolutePath()};
Process process =
Runtime.getRuntime().exec(cmd);
BufferedReader br = new BufferedReader(new InputStreamReader(process.getErrorStream()));
String readLine = br.readLine();
StringBuilder builder = new StringBuilder();
while (readLine != null) {
readLine = br.readLine();
builder.append(readLine);
}
log.error( "#readLine error: " + builder);
process.waitFor();
log.info("命令执行后:{}", tempGltf);
//oss上传
return ossOperateFacade.upload("xxx/", tempGltf.getAbsolutePath(), "时间");
} catch (Exception ex) {
ex.printStackTrace();
} finally {
if (tempFile != null) {
tempFile.delete();
}
if (tempGltf != null) {
tempGltf.delete();
}
}
return null;
}
private void replaceMaterialImage(DefaultGltfModel gltfModel, String imageUrl, DefaultTextureModel baseColorTexture) {
String imageFormat = imageUrl.substring(imageUrl.lastIndexOf(".") + 1);
String imageName = imageUrl.substring(imageUrl.lastIndexOf("/") + 1);
if ("jpg".equalsIgnoreCase(imageFormat)) {
imageFormat = "jpeg";
}
byte[] bytes = imageBytes(imageUrl);
ByteBuffer imageData = ByteBuffer.wrap(bytes);
imageDetailWith(gltfModel, baseColorTexture, imageFormat, imageData, imageName);
}