G3DB/G3DJ模型详解

我们先将上一篇中的 spacesphere.obj 模型使用 fbx-conv 将其转为g3dj格式,因为g3dj为文本格式,方便我们查看里面的内容:

fbx-conv -o G3DJ spacesphere.obj

得到一个 spacesphere.g3dj 文件,使用一个文本编辑器打开它,它的整体结构如下:

{
	"version": [  0,   1], 
	"id": "", 
	"meshes": [
...
	], 
	"materials": [
...
	], 
	"nodes": [
...
	], 
	"animations": []
}

g3dj 格式是一个 json 文件格式。主要包含六个成员的对象。第一个是版本。第二个是id(一些建模应用程序允许您指定的名称),我们现在不使用它。然后依次是四个数组:网格、材质、节点和动画。现在,我们查看下 Model(com.badlogic.gdx.graphics.g3d.Model) 类的结构:

public class Model implements Disposable {
	public final Array<Mesh> meshes = new Array();
	public final Array<Material> materials = new Array();
	public final Array<Node> nodes = new Array();
	public final Array<Animation> animations = new Array();
	
	public final Array<MeshPart> meshParts = new Array();
	protected final Array<Disposable> disposables = new Array();
	
	...
}

Model类主要包括了网格、材质、节点和动画数组属性。它还有一个meshParts阵列。所以g3dj(和g3db)文件和 Model 类实际上包含的内容的是一对一对应的。事实上,这就是fbx conv的主要功能,它将obj/fbx等文件转换为可供 libGDX框架渲染的运行时格式。这也意味着,通过 g3dj/g3db 文件,我们直接可以得到一个model类。

网格(meshes)数组结构

{
	...
	"meshes": [
		{
			"attributes": ["POSITION", "NORMAL", "TEXCOORD0"], 
			"vertices": [
				 25.000017, -95.105652, -18.163574, -0.269870,  0.942723,  0.196072,  0.050000,  0.900000, 
				 ...
			], 
			"parts": [
				{
					"id": "mpart1", 
					"type": "TRIANGLES", 
					"indices": [
						  0,   1,   2,   1,   0,   3,   3,   4,   5,   4,   3,   0, 
						 ...
					]
				}, 
				{
					"id": "mpart2", 
					"type": "TRIANGLES", 
					"indices": [
						 ...
					]
				}, 
				{
					"id": "mpart4", 
					"type": "TRIANGLES", 
					"indices": [
						 ...
					]
				}
			]
		}, 
		{
			"attributes": ["POSITION", "NORMAL"], 
			"vertices": [
				...
			], 
			"parts": [
				{
					"id": "mpart3", 
					"type": "TRIANGLES", 
					"indices": [
						 ...
					]
				}
			]
		}
	], 
	...
}

网格数组里的每个对象主要包含三个数组:属性数组(attributes)、顶点数组(vertices)和部件数组(parts)。

属性数组(attributes) 指定网格包含哪些顶点属性。数组第一位为位置(POSITION)。第二位为VertexAttribute的值,第三位为“TEXCOORD0”,指定网格包含纹理坐标。

顶点数组(vertices) 只是代表网格的一个庞大的浮点值数组。在每一项上,前三个值代表位置,后三个值代表法线,最后两个值代表纹理坐标(UV)。

部件数组(parts) 的每一项被称为网格部件,主要包含了三个属性。在 Model 类中有一个单独的meshParts数组。该数组包含所有网格中的所有部件。第一个属性是id,这是一个唯一的标识符,在内部用于标识部件。接下来第二个属性是类型,它定义了部件应该如何呈现(基本类型)。理论上这可能是一个不同的值,但在实践中它的值基本上是“三角形(TRIANGLES)”,也就是部件是由三角形组成,其中每个三角形由三个顶点指定。最后一个属性是索引数组。同样,这是一个庞大的数字数组,其中每个数字用于标识顶点数组中的一个顶点。例如,值0表示顶点数组中的第0项,值1表示第1项。由于类型设置为三角形,所以前三个值(0、1、2)指定第一个三角形,后三个值(1、0、3)指定第二个三角形,依此类推。请注意,每条线由12个值组成,这意味着一条线上有四个三角形。

接下来看下Mesh类(com.badlogic.gdx.graphics.Mesh):

public class Mesh implements Disposable {
	...
	final VertexData vertices;
	final IndexData indices;
	...
}

在 Mesh 类中,VertexData可以被视为一个庞大的浮点值数组,它将与g3dj文件中顶点数组(vertices)相匹配。IndexData也可以被视为一个巨大的数组,但现在它是一个 short 值。该数组将填充g3dj文件中 meshes 中的部件属性(parts)的下的indices索引数组,把部件铺平。因此,对于第一个网格,它将包含第一个网格部分(mpart1)的索引,紧接着是第二个网格部分(mpart2)的索引,然后是第三个网格部分(mpart4)的索引。要识别网格中的部件,我们需要知道它在IndexData中的位置。现在让我们看一看MeshPart类:

public class MeshPart {
	/** unique id within model **/
	public String id;
	/** the primitive type, OpenGL constant like GL_TRIANGLES **/
	public int primitiveType;
	/** the offset into a Mesh's indices array **/
	public int offset;
	/** the number of vertices that make up this part **/
	public int size;
	/** the Mesh the part references, also stored in {@link Model} **/
	public Mesh mesh;
}

这正是我们需要的,indexOffset和numVertices值告诉我们IndexData的哪个部件用于这个网格部分。所以在LibGDX 3d api中,我们不渲染网格,而是渲染网格部件。这很有用,因为现在多个不同的ModelInstance可以共享同一个网格。这实际上减少了网格需要绑定的次数,从而提高了性能。事实上,在我们前几篇中的四个模型中来看,现在只有两个网格,但总共有四个网格部分。fbx-conv 已经合并了为我们共享相同属性的网格。第二个网格不包含TEXCOORD0,因为 block 模型上没有纹理。我们可以通过向 block 模型添加纹理坐标,而不应用纹理来优化它。这将使网格绑定的次数仅减少一次。我现在不介绍这个过程,因为它是特定于应用程序的建模。但请记住,具有相同的顶点属性将有助于合并网格。将其使用的模型转换为G3DJ/G3DB格式的模型可以得到明显的优化。

材质(materials)数组结构

{
	...
	"materials": [
		{
			"id": "sphere2_auv1", 
			"diffuse": [ 1.000000,  1.000000,  1.000000], 
			"textures": [
				{
					"id": "file3", 
					"filename": "invader.png", 
					"type": "DIFFUSE"
				}
			]
		}, 
		{
			"id": "lambert2", 
			"diffuse": [ 1.000000,  1.000000,  1.000000], 
			"textures": [
				{
					"id": "file1", 
					"filename": "space.jpg", 
					"type": "DIFFUSE"
				}
			]
		}, 
		{
			"id": "cube1_auv1", 
			"diffuse": [ 1.000000,  1.000000,  1.000000], 
			"textures": [
				{
					"id": "file2", 
					"filename": "ship.png", 
					"type": "DIFFUSE"
				}
			]
		}, 
		{
			"id": "block_default1", 
			"diffuse": [ 0.000000,  0.000000,  1.000000]
		}
	],
		...
}

在上面的结构中,可以看到该文件包含了四种材质。每个材质都有一个唯一的id,该id与建模应用程序中材质的名称相同。最好还是给材质起一个有用的名字比较好。它允许您在 Model 类的 材质(materials)数组中标识材质。接下来,材质包含一个漫反射值(diffuse),该值将材质的漫反射颜色表示为从0到1的红色、绿色和蓝色(RGB)数组。所以[0.5,0.5,0.5]的值为灰色,[1.0,0.0,0.0]的值为红色。请注意,最后一种材质是块模型的材质,具有蓝色漫反射颜色。最后,前三种材质还有一个纹理数组,其中定义了模型的纹理。同样,纹理的id与建模应用程序中纹理的名称相同,这里也是建议给出一个有用的名称。filename的值显然是纹理的文件名。type的值指定如何应用纹理,在本例中是“漫反射”,但另一个值可以是“法线贴图”。我们现在不会深入讨论材质,但请注意,除了id之外的所有值都是可选的。

节点(nodes)数组结构

{
	...
	"nodes": [
		{
			"id": "space", 
			"parts": [
				{
					"meshpartid": "mpart1", 
					"materialid": "lambert2", 
					"uvMapping": [[  0]]
				}
			]
		}, 
		{
			"id": "ship", 
			"rotation": [ 0.000000,  1.000000,  0.000000,  0.000000], 
			"translation": [ 0.000000,  0.000000,  6.000000], 
			"parts": [
				{
					"meshpartid": "mpart2", 
					"materialid": "cube1_auv1", 
					"uvMapping": [[  0]]
				}
			]
		}, 
		{
			"id": "block1", 
			"translation": [-5.000000,  0.000000,  3.000000], 
			"parts": [
				{
					"meshpartid": "mpart3", 
					"materialid": "block_default1"
				}
			]
		}, 
		...
		{
			"id": "block6", 
			"translation": [ 5.000000,  0.000000,  3.000000], 
			"parts": [
				{
					"meshpartid": "mpart3", 
					"materialid": "block_default1"
				}
			]
		}, 
		{
			"id": "invader1", 
			"translation": [-5.000000,  0.000000,  0.000000], 
			"parts": [
				{
					"meshpartid": "mpart4", 
					"materialid": "sphere2_auv1", 
					"uvMapping": [[  0]]
				}
			]
		}, 
		...
		{
			"id": "invader30", 
			"translation": [ 5.000000,  0.000000, -8.000000], 
			"parts": [
				{
					"meshpartid": "mpart4", 
					"materialid": "sphere2_auv1", 
					"uvMapping": [[  0]]
				}
			]
		}
	], 
	...
}

节点数据包含我们在建模应用程序中创建的每个模型实例。每个节点都有一个id,该id与我们在建模应用程序中使用的名称相匹配,我们在前面的教程中使用了该id来创建每个ModelInstance。某些节点还具有平移(translation)或旋转(rotation)值。下一个值是parts数组,它是所有内容组合在一起的地方。它描述了应该如何呈现节点。meshpartid为网格的部件ID,materialid为材质的ID。uvMapping值用于指定哪个纹理应使用哪个纹理坐标。记住我们有一个“TEXCOORD0”顶点属性和一个“漫反射”纹理。现在考虑一个场景,我们将同时拥有“TEXCOORD0”和“TEXCOORD1”顶点属性,以及“DIFFUSE”和“NORMALMAP”纹理。uvMapping数组指定哪个纹理应用于TEXCOORD0(例如DIFFUSE),哪个纹理应用于TEXCOORD1(例如NORMALMAP)。

在这种情况下,部件数组只包含一个节点零件。但请考虑一个汽车模型,在该模型中,您希望使用应用的纹理渲染整个模型,但窗口除外,窗口应使用黑色渲染。在这种情况下,您将有两个部分,一个包含窗户的网格部分和黑色材质,另一个包含汽车剩余部分的网格部分和具有纹理的材质。你应该永远记住这一点。无论何时将两种或更多不同材质应用于建模应用程序中的模型,它都将被拆分为多个节点部件。以汽车为例,我们可以通过向纹理中添加一个小的黑色矩形,并设置窗口的纹理坐标,使其仅覆盖该小黑色矩形的中间部分来优化此设置。

同样,g3dj文件中的节点与模型类中的节点一一匹配。因此,模型中的节点与建模应用程序中的节点匹配。大多数建模应用程序都允许这些节点具有层次结构,模型类也是如此。每个节点都有一个子数组,其中包含其子节点。现在我们将不再深入探讨这个问题,但请记住,如果在建模应用程序中使用层次结构,它也将反映在模型类中。

动画(animations)数组结构

它显然用于动画模型。后面教程将讨论动画。

转载:https://www.javagame.top/column/DXBLOG202203210716246285267/DXBLOG202203220238217488388/detail.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值