Directx11教程四十六之FBX SDK

在之前的DX11入门系列文章中,有篇有关 Directx11教程四十之加载OBJ模型 读取obj模型数据的博客。不过在obj读取的那篇博客我有些坑并没有说,就是我写的那个obj解析器只能解析特定的obj文件格式,因为后面我用我写的obj模型解析器发现根本无法解析大多数的obj文件格式,真是让人崩溃,因为那时的我是分析特定的一两个obj文件写出来的解析器,实际上obj文件数据的多样性超越我的想象。所以我不推荐大家在使用obj了,请远离obj。 obj能直接查看文本数据,但并不好用。我们紧跟商业引擎的步伐,用FBX格式。

 

惯例,放出本篇博客有关程序对应的结构:

 

关于商业引擎与FBX SDK

我们知道UE4,U3D导入模型是FBX文件,也就是说FBX文件是UE4,U3D的中间模型文件,为什么特意加个“中间”呢?那是因为FBX模型文件仅仅是UE4,U3D的导入模型文件,并不是UE4,U3D游戏运行时加载的模型文件。像UE4,U3D这样的引擎导入FBX文件会生成自己的自定义模型文件。这是为什么呢?用过FBX SDK读取模型数据你就会发现使用 FBX SDK 读取FBX模型文件的直接得到的几何数据(例如顶点数据)存在一定(有时候甚至可以说是严重的)的冗余,并且读取的速度也不够快,因此FBX文件只是商业引擎的中间模型文件。当然我们是DX11教程做案例,没必要搞那么复杂,直接用FBX SDK读取FBX文件的数据作为渲染数据来使用。

 

FBX SDK的环境配置。

我用的是VS2017和 FBX  SDK2017.1,

FBX SDK2017.1下载地址:http://usa.autodesk.com/adsk/servlet/pc/item?siteID=123112&id=26012646

相应的开发文档:http://help.autodesk.com/view/FBX/2017/ENU/?guid=__files_GUID_6E7B1A2E_584C_49C6_999E_CD8367841B7C_htm 可以跟着文档来配置环境

因为用的是VS2017,相应的FBX SDK2017也只给出了VS2015,VS2013的编译版本,环境配置方案有三种,我三种都试过了,发现VS2017可以使用DLL配置的那种。

FBX 文件的读取

关于FBX数据的读取 这里我就不献丑了,直接引用前人写的博客就行了。下面三篇有关FBX读取数据的博客强烈推荐:

1.基于FBX SDK的FBX模型解析与加载 -(一)

2.基于FBX SDK的FBX模型解析与加载 -(二)

3.Working with FBX SDK (2)

4.解析 FBX 模型文件作为 Direct3D 的渲染模型

跟着这三篇博客基本上能理解基本的FBX SDK读取数据的概念。

这里唯一要纠结的是,材质的读取接口已经发生改变了,我们因爲需要在DX11读取文件,就得获取纹理文件的相对路径(不推荐用绝对路径),采用的接口是FBXFileTexture而并非FBXTexture,如下所示:

void ImportFBX::ReadReletiveTextureFileName(FbxProperty* mproperty,
	int materialIndex, map<int, Material>& materialMap)
{
	if (!mproperty || !mproperty->IsValid())
	{
		return;
	}
	string name = mproperty->GetName();
	bool isNeedTexture = false;
	isNeedTexture = (name == FbxSurfaceMaterial::sDiffuse) || (name == FbxSurfaceMaterial::sSpecular)
		|| (name == FbxSurfaceMaterial::sTransparentColor) || (name == FbxSurfaceMaterial::sBump);
	if (!isNeedTexture)
	{
		return;
	}

	int textureNum = mproperty->GetSrcObjectCount<FbxFileTexture>();
	//现在每种纹理仅仅读取一个
	if (textureNum > 0)
	{
		FbxFileTexture* fbxFileTexture = mproperty->GetSrcObject<FbxFileTexture>(0);
		string  relativeFileName = fbxFileTexture->GetRelativeFileName();
		size_t tgaTagPos = relativeFileName.find(".tga");
		if (tgaTagPos != string::npos)
		{
			relativeFileName = relativeFileName.substr(0, tgaTagPos);
			relativeFileName += string(".jpg");
		}
		string fileName = fbxFileNamePre + relativeFileName;
		if (name == FbxSurfaceMaterial::sDiffuse)
		{
			materialMap[materialIndex].diffuseMapFileName = fileName;
		}
		else if (name == FbxSurfaceMaterial::sSpecularFactor)
		{
			materialMap[materialIndex].specularMapFileName = fileName;
		}
		else if (name == FbxSurfaceMaterial::sTransparentColor)
		{
			materialMap[materialIndex].alphaMapFileName = fileName;
		}
		else if (name == FbxSurfaceMaterial::sBump)
		{
			materialMap[materialIndex].bumpMapFileName = fileName;
		}

	}

}

好吧,这里我只读取diffuseTexture,NormalTexture,SpecularTexture,AlphaTexture,并且DXUT由于无法加载.tga格式的纹理,我转为.tga文件后缀名为.jpg,并且将相应的FBX文件的所有.tga图片在PS里转为了.jpg文件。

 

读取FBX的数据结构:

我们知道FBX的节点是以树的形状来组织的,因此用树来组织是再好不过的,当然我为了教程的简易,只是用了数组来组织。

1.顶点结构如下,没多少好说的

struct VertexPCNTT
{
	XMFLOAT3 pos;
	XMFLOAT3 color;
	XMFLOAT3 normal;
	XMFLOAT3 tangent;
	XMFLOAT2 uv;
};

 

2. 三角形结构,因为在fbx的fbxNode节点中,读取的每个三角形都有一个材质id,索引到相应的材质,相应的材质蕴含相应的相应各种属性(Diffuse,Specular,以及各种纹理)  。 这里得注意一点,材质id是绑定于相应的节点的,比如说fbxNode1和fbxNode2的同一个材质id(例如都为0或者1之类的)是完全不存在关系的,材质id仅仅针对于绑定的节点。

struct Triangle
{
	VertexPCNTT vertexs[3];
	int MaterialId;
};

 

3. 材质结构,这里我们的材质直接用纹理文件名来表示,当读取到相应的纹理文件名为空时,就代表不存在相应纹理,反之则存在相应纹理。

struct Material
{
	string diffuseMapFileName;
	string specularMapFileName;
	string alphaMapFileName;
	string bumpMapFileName;
};

4. mesh结构,我们从前面的四篇博客可以知道,一个mesh的节点读取的所有三角形存在可能不只一个材质id,而每个材质id是绑定于相应的fbxNode的,因为一个mesh类型的fbxNode或者说fbxMesh存在多少个材质id,则就存在多少个mesh.(这里不可能一个三角形就是一个mesh,drawCall得吓死人,所以得根据在读取fbxMesh数据的时候,把材质id相同的三角形归为同一个mesh)。总体来说 一个fbxMesh存在多少个材质id,就生成多少个mesh,也就是一个mesh存在一种材质id

struct Mesh
{
	vector<VertexPCNTT> mVertexData;
	vector<WORD> mIndexData;
	int materialId;
	ID3D11Buffer* mVertexBuffer;
	ID3D11Buffer* mIndexBuffer;
};

5.model结构, 刚才我们说过一个mesh的fbxNode或者说fbxMesh可以解析出多个mesh,则一个fbxNode的所有mesh解析成了model.不过得注意我在model结构添加了材质哈希表,mesh可以通过自身的材质id,在model结构找到相应的材质(相应的纹理相对路径。

struct Model
{
	vector<Mesh> mMeshList;
	map<int, Material> mMaterialMap;
};

 

6.FBXModel结构,我们上面说model为一个节点解析出来的结构,而一个fbx文件如果含有n多个分fbxNode(fbxMesh),也就是生成了n多个Model结构体,我解析其为FBXModel.注意我用了一个哈希表来查询相应的 ID3D11ShaderResourceView* 资源,这个mSRVMap的键为纹理的相对路径,可能看到这你可豁然开朗。

//保证了一个FBX加载的所有纹理文件都仅仅加载一次
struct FBXModel
{
	vector<Model> mModelList;
	map<string, ID3D11ShaderResourceView*> mSRVMap;
};

这里思路很明确,在渲染的时候,我们是以mesh为个体,一个一个进行渲染的,思路过程:

(1) 设置mesh的顶点缓存和索引缓存

  (2) 根据 mesh 的材质MaterialId在相应的 model 材质名哈希表 找到相应的Material,最后用Material对应的四种纹理相应路径名在FBXModel 材质哈希表找到相应的ID3D11ShaderResourceView* 资源,然后用相应的Shader进行渲染,好吧,一切已然豁然开朗。

 

FBX数据读取的坐标系空间纠正:

因为我们是在3DS MAX建模的,而我们3DS MAX的坐标系空间如下:

3DS MAX的坐标系空间是Z轴向上的右手坐标系,而D3D11的是Y轴向上的左手坐标系。因此如果你直接拿FBX读取的数据渲染,会发现模型 往往是躺着的。我采用了绕X轴渲染 -90度 的办法,对读取的顶点数据做如下变换:

1.位置(XMMatrixRotationX(-XM_PI/2.0)),

//由于3DS MAX里坐标轴为Z轴向上的,Y轴向里的右手坐标系, D3D11为左手坐标系
//参考https://www.cnblogs.com/wantnon/p/4372764.html
void ImportFBX::ReadVertexPos(FbxMesh* mesh, int ctrlPointIndex, XMFLOAT3* pos)
{
	FbxNode* meshNode = mesh->GetNode();
	FbxAnimEvaluator* lEvaluator = mScene->GetAnimationEvaluator();
	FbxMatrix lGlobal;

	lGlobal.SetIdentity();
	lGlobal = lEvaluator->GetNodeGlobalTransform(meshNode);
	FbxDouble3 scaling = meshNode->LclScaling.Get();
	FbxVector4 * ctrPoints = mesh->GetControlPoints();
	pos->x = ctrPoints[ctrlPointIndex][0] * scaling[0];
	pos->y = ctrPoints[ctrlPointIndex][2] * scaling[2];
	pos->z = -ctrPoints[ctrlPointIndex][1] * scaling[1];


}

 

2.顶点法线(顶点位置变换的逆反矩阵变换)

 

void ImportFBX::ReadVertexNormal(FbxMesh* mesh, int ctrlPointIndex, int vertexCount, XMFLOAT3* normal)
{
	if (mesh->GetElementNormalCount() < 1)
	{
		return;
	}

	FbxGeometryElementNormal* vertexNormal = mesh->GetElementNormal(0);

	switch (vertexNormal->GetMappingMode())
	{
	case FbxGeometryElement::eByControlPoint:
	{
		switch (vertexNormal->GetReferenceMode())
		{
		case FbxGeometryElement::eDirect:
		{
			normal->x = vertexNormal->GetDirectArray().GetAt(ctrlPointIndex).mData[0];
			normal->y = vertexNormal->GetDirectArray().GetAt(ctrlPointIndex).mData[2];
			normal->z = -vertexNormal->GetDirectArray().GetAt(ctrlPointIndex).mData[1];
		}
		break;
		case FbxGeometryElement::eIndexToDirect:
		{
			int id = vertexNormal->GetIndexArray().GetAt(ctrlPointIndex);
			normal->x = vertexNormal->GetDirectArray().GetAt(id).mData[0];
			normal->y = vertexNormal->GetDirectArray().GetAt(id).mData[2];
			normal->z = -vertexNormal->GetDirectArray().GetAt(id).mData[1];
		}
		break;

		default:
			break;
		}
	}
	break;

	case FbxGeometryElement::eByPolygonVertex:
	{
		switch (vertexNormal->GetReferenceMode())
		{
		case FbxGeometryElement::eDirect:
		{
			normal->x = vertexNormal->GetDirectArray().GetAt(vertexCount).mData[0];
			normal->y = vertexNormal->GetDirectArray().GetAt(vertexCount).mData[2];
			normal->z = -vertexNormal->GetDirectArray().GetAt(vertexCount).mData[1];
		}
		break;

		case FbxGeometryElement::eIndexToDirect:
		{
			int id = vertexNormal->GetIndexArray().GetAt(vertexCount);
			normal->x = vertexNormal->GetDirectArray().GetAt(id).mData[0];
			normal->y = vertexNormal->GetDirectArray().GetAt(id).mData[2];
			normal->z = -vertexNormal->GetDirectArray().GetAt(id).mData[1];
		}
		break;

		default:
			break;
		}
		break;
	}
	}
}

 

3.顶点切线(跟顶点位置变换一致)

 

void ImportFBX::ReadVertexTangent(FbxMesh* mesh, int ctrlPointIndex, int vertexCount, XMFLOAT3* tangent)
{
	if (mesh->GetElementTangentCount() < 1)
	{
		return;
	}

	FbxGeometryElementTangent* vertexTangent = mesh->GetElementTangent(0);

	switch (vertexTangent->GetMappingMode())
	{
	case FbxGeometryElement::eByControlPoint:
	{
		switch (vertexTangent->GetReferenceMode())
		{
		case FbxGeometryElement::eDirect:
		{
			tangent->x = vertexTangent->GetDirectArray().GetAt(ctrlPointIndex).mData[0];
			tangent->y = vertexTangent->GetDirectArray().GetAt(ctrlPointIndex).mData[2];
			tangent->z = -vertexTangent->GetDirectArray().GetAt(ctrlPointIndex).mData[1];
		}
		break;
		case FbxGeometryElement::eIndexToDirect:
		{
			int id = vertexTangent->GetIndexArray().GetAt(ctrlPointIndex);
			tangent->x = vertexTangent->GetDirectArray().GetAt(id).mData[0];
			tangent->y = vertexTangent->GetDirectArray().GetAt(id).mData[2];
			tangent->z = -vertexTangent->GetDirectArray().GetAt(id).mData[1];
		}
		break;

		default:
			break;
		}
	}
	break;

	case FbxGeometryElement::eByPolygonVertex:
	{
		switch (vertexTangent->GetReferenceMode())
		{
		case FbxGeometryElement::eDirect:
		{
			tangent->x = vertexTangent->GetDirectArray().GetAt(vertexCount).mData[0];
			tangent->y = vertexTangent->GetDirectArray().GetAt(vertexCount).mData[2];
			tangent->z = -vertexTangent->GetDirectArray().GetAt(vertexCount).mData[1];
		}
		break;

		case FbxGeometryElement::eIndexToDirect:
		{
			int id = vertexTangent->GetIndexArray().GetAt(vertexCount);
			tangent->x = vertexTangent->GetDirectArray().GetAt(id).mData[0];
			tangent->y = vertexTangent->GetDirectArray().GetAt(id).mData[2];
			tangent->z = -vertexTangent->GetDirectArray().GetAt(id).mData[1];
		}
		break;

		default:
			break;
		}
		break;
	}
	}
}

 

4.顶点UV,u2 = u1, v2 = 1.0 - v1,则就是U不变,而v反过来(DX11与OPenGL相反)。

 

void ImportFBX::ReadVertexUV(FbxMesh* mesh, int ctrlPointIndex, int uvIndex, XMFLOAT2* uv)
{
	if (mesh->GetElementUVCount() < 1)
	{
		return;
	}

	FbxGeometryElementUV* vertexUV = mesh->GetElementUV(0);

	switch (vertexUV->GetMappingMode())
	{
	case FbxGeometryElement::eByControlPoint:
	{
		switch (vertexUV->GetReferenceMode())
		{
		case FbxGeometryElement::eDirect:
		{
			//因为这些这些数据个Opengl坐标一样,而我们是需要在D3D11里渲染的数据,所以部分数据得改变
			//v反转
			uv->x = vertexUV->GetDirectArray().GetAt(ctrlPointIndex).mData[0];
			uv->y = 1.0f - vertexUV->GetDirectArray().GetAt(ctrlPointIndex).mData[1];

		}
		break;
		case FbxGeometryElement::eIndexToDirect:
		{
			int id = vertexUV->GetIndexArray().GetAt(ctrlPointIndex);
			uv->x = vertexUV->GetDirectArray().GetAt(id).mData[0];
			uv->y = 1.0f - vertexUV->GetDirectArray().GetAt(id).mData[1];
		}
		break;

		default:
			break;
		}
	}
	break;

	case FbxGeometryElement::eByPolygonVertex:
	{
		switch (vertexUV->GetReferenceMode())
		{
		case FbxGeometryElement::eDirect:
		case FbxGeometryElement::eIndexToDirect:
		{
			uv->x = vertexUV->GetDirectArray().GetAt(uvIndex).mData[0];
			uv->y = 1.0f - vertexUV->GetDirectArray().GetAt(uvIndex).mData[1];
		}
		break;

		default:
			break;
		}
		break;
	}
	}
}

最终渲染结果:

 

 

 

参考资料:

【1】Working with FBX SDK (2)

【2】基于FBX SDK的FBX模型解析与加载 -(一)

【3】基于FBX SDK的FBX模型解析与加载 -(二)

【4】FBX 2017.1开发者文档

 

源码链接:

    集成了FBX SDK的3D渲染引擎源码:

    https://github.com/2047241149/SDEngine

 

未来待提高

【1】命名有问题,不过懒得改了,Mesh应该改为SubMesh, Model改为Mesh,FBXModel改为Model

【2】FBXModel管理Model更应该用树结构,而非vector数组。

【3】纹理哈希表不应该与FBXModel绑定在一起,因为加载的多个FBX模型很有可能存在相同的加载纹理或者说是相同的纹理相对路径名。更应该建立一个全局的纹理管理类,避免纹理加载资源的重复性。

【4】顶点数据存在大量的冗余,可以自定义文件来存储模型数据,FBX还是作为导入模型比较合适,不适合作为游戏运行时的加载模型文件。

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
不错的dx11入门教程 Tutorial 1: Setting up DirectX 11 with Visual Studio Tutorial 2: Creating a Framework and Window Tutorial 3: Initializing DirectX 11 Tutorial 4: Buffers, Shaders, and HLSL Tutorial 5: Texturing Tutorial 6: Diffuse Lighting Tutorial 7: 3D Model Rendering Tutorial 8: Loading Maya 2011 Models Tutorial 9: Ambient Lighting Tutorial 10: Specular Lighting Tutorial 11: 2D Rendering Tutorial 12: Font Engine Tutorial 13: Direct Input Tutorial 14: Direct Sound Tutorial 15: FPS, CPU Usage, and Timers Tutorial 16: Frustum Culling Tutorial 17: Multitexturing and Texture Arrays Tutorial 18: Light Maps Tutorial 19: Alpha Mapping Tutorial 20: Bump Mapping Tutorial 21: Specular Mapping Tutorial 22: Render to Texture Tutorial 23: Fog Tutorial 24: Clipping Planes Tutorial 25: Texture Translation Tutorial 26: Transparency Tutorial 27: Reflection Tutorial 28: Screen Fades Tutorial 29: Water Tutorial 30: Multiple Point Lights Tutorial 31: 3D Sound Tutorial 32: Glass and Ice Tutorial 33: Fire Tutorial 34: Billboarding Tutorial 35: Depth Buffer Tutorial 36: Blur Tutorial 37: Coming Soon... DirectX 10 Tutorials: Tutorial 1: Setting up DirectX 10 with Visual Studio Tutorial 2: Creating a Framework and Window Tutorial 3: Initializing DirectX 10 Tutorial 4: Buffers, Shaders, and HLSL Tutorial 5: Texturing Tutorial 6: Diffuse Lighting Tutorial 7: 3D Model Rendering Tutorial 8: Loading Maya 2011 Models Tutorial 9: Ambient Lighting Tutorial 10: Specular Lighting Tutorial 11: 2D Rendering Tutorial 12: Font Engine Tutorial 13: Direct Input Tutorial 14: Direct Sound Tutorial 15: FPS, CPU Usage, and Timers Tutorial 16: Frustum Culling Tutorial 17: Multitexturing and Texture Arrays Tutorial 18: Light Maps Tutorial 19: Alpha Mapping Tutorial 20: Bump Mapping Tutorial 21: Specular Mapping Tutorial 22: Render to Texture Tutorial 23: Fog Tutorial 24: Clipping Planes Tutorial 25: Texture Translation Tutorial 26: Transparency Tutorial 27: Reflection Tutorial 28: Screen Fades Tutorial 29: Water Tutorial 30: Multiple Point Lights Tutorial 31: 3D Sound Tutorial 32: Glass and Ice Tutorial 33: Fire Tutorial 34: Billboarding Tutorial 35: Depth Buffer Tutorial 36: Blur Tutorial 37: Coming Soon... DirectX 10 Terrain Tutorials: Tutorial 1: Grid and Camera Movement Tutorial 2: Height Maps Tutorial 3: Terrain Lighting Tutorial 4: Terrain Texturing Tutorial 5: Color Mapped Terrain Tutorial 6: Quad Trees Tutorial 7: Coming Soon... 。。。。。。。。
### 回答1: FBX SDK for Unity是为Unity游戏引擎设计的一种软件开发工具包(SDK)。FBX SDK是由Autodesk开发的一套用于处理和管理FBX文件格式的工具。FBX是一种常见的3D文件格式,用于在不同的3D应用程序之间进行互操作。使用FBX SDK,开发人员可以轻松地将FBX文件导入到Unity中,以便在游戏中使用。 FBX SDK for Unity提供了一些功能,使开发人员能够更好地在Unity中处理和管理FBX文件。首先,它允许开发人员读取和写入FBX文件。开发人员可以使用FBX SDK在Unity中读取FBX文件的内容,例如模型、材质、动画等,并将其转换为Unity能够使用的数据格式。相反地,开发人员还可以使用FBX SDK将Unity中的内容导出为FBX文件,以便在其他应用程序中使用。 其次,FBX SDK for Unity还提供了一些用于编辑和处理FBX文件的工具和函数。开发人员可以使用FBX SDK更改模型的层次结构、材质属性、动画轨迹等。这使得开发人员能够更好地控制和调整FBX文件以适应他们的游戏需求。 此外,FBX SDK for Unity还提供了一些用于导入和导出FBX文件的辅助功能。开发人员可以使用FBX SDK处理FBX文件的单位、坐标系、标记和动画设置等。这些功能可以让开发人员更加方便地与FBX文件进行交互,并确保在不同应用程序之间进行正确的数据转换。 总之,FBX SDK for Unity是一个非常有用的工具包,使开发人员能够更好地在Unity中处理和管理FBX文件,并与其他3D应用程序进行交互和互操作。它为开发人员提供了更多的灵活性和控制权,以实现他们的游戏开发目标。 ### 回答2: FBX SDK for Unity是一款Unity引擎的插件,用于导入和导出FBX文件格式。FBX文件是一种用于在不同3D软件之间交换模型、动画、材质等数据的通用文件格式。 FBX SDK for Unity提供了许多有用的功能和工具,可以帮助开发者更轻松地在Unity中使用FBX文件。通过该插件,开发者可以导入FBX文件,并将其转换为Unity中可用的资源,如3D模型、骨骼动画、材质、贴图等。同时,开发者也可以将Unity中的资源导出为FBX文件,以便与其他支持FBX格式的软件进行交流和共享。 FBX SDK for Unity还支持模型的网格优化、纹理压缩和骨骼动画的精细调整。通过可以控制导入和导出设置,开发者可以根据需求对FBX文件进行自定义处理,以获得更好的效果和性能。 除此之外,FBX SDK for Unity还提供了一些扩展工具,用于处理FBX文件中的蒙皮、法线、顶点色等数据,以及支持多个FBX文件的批量操作。 总之,FBX SDK for Unity是一款功能强大的插件,提供了导入和导出FBX文件的功能,以帮助Unity开发者更好地使用FBX文件,并加强与其他3D软件的兼容性和交互性。无论是从外部软件导入资源,还是将Unity资源导出到其他软件使用,FBX SDK for Unity都是一个非常有用的工具。 ### 回答3: FBX SDK for Unity是一种用于Unity开发环境的软件开发工具包,用于处理和加载FBX文件格式。FBX文件是一种常用的3D模型和动画文件格式,广泛用于游戏开发和动画制作等领域。 通过FBX SDK for Unity,开发者可以在Unity中轻松地导入、加载和展示来自于FBX文件的3D模型和动画。这个SDK提供了一系列功能强大的工具和接口,可以处理FBX文件中的各种元素,如网格、材质、动画、骨骼等等。开发者可以使用这些工具和接口来编辑、控制和操纵FBX模型和动画,在游戏中实现各种效果和功能。 FBX SDK for Unity还支持多种模型和动画文件的转换和导出功能。这意味着开发者可以将FBX文件转换为其他格式,如Unity支持的模型和动画格式,以便在不同的开发环境中使用。同时,也可以将Unity中创建的模型和动画导出为FBX文件,用于与其他软件或平台进行交互和共享。 FBX SDK for Unity为开发者提供了更多的选择和自由,使他们能够更灵活地使用FBX文件中的资源。通过这个SDK,开发者可以更加高效地创建和操作3D模型和动画,加快开发进度,并实现更具创意和吸引力的游戏效果。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值