fbx sdk 平滑法线 解决硬表面断线问题

https://zhuanlan.zhihu.com/p/361103783

fbx sdk配置

vs2019 / fbx sdk 2020.2
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码

记录一下,add vertexcolor卡了我好久

#include <fbxsdk.h>
#include <string>
using namespace fbxsdk;
using namespace std;

void StoreNormalsToVertColor(FbxNode* node)
{
    
    
	if (node->GetChildCount())
	{
    
    
		for (int i = 0; i < node->GetChildCount(); i++)
		{
    
    
			if (node->GetChild(i)->GetMesh())
			{
    
    
				//获取mesh
				FbxMesh* mesh = node->GetChild(i)->GetMesh();
				
				int num_vertices = mesh->GetPolygonVertexCount(); 
				
				// 如果不存在 自动生成切线 副切线
				if (mesh->GetElementTangentCount() == 0 || mesh->GetElementBinormalCount() == 0)
				{
    
    
					mesh->GenerateTangentsData(0, true);
				}


				//获取layer,顶点色、法切线之类的顶点信息几乎存在layer中
				fbxsdk::FbxLayer* layer0 = mesh->GetLayer(0);


				//依次获取layer中的顶点色层、法线层、切线层、副法线(或者叫副切线)层
				fbxsdk::FbxLayerElementVertexColor* VertColor = layer0->GetVertexColors();
				//VertColor->SetMappingMode(FbxGeometryElement::eByControlPoint);
				//VertColor->SetReferenceMode(FbxGeometryElement::eDirect);


				fbxsdk::FbxLayerElementNormal* VertNormal = layer0->GetNormals();
				fbxsdk::FbxLayerElementTangent* VertTangent = layer0->GetTangents();
				fbxsdk::FbxLayerElementBinormal* VertBinomral = layer0->GetBinormals();
				//逐顶点遍历操作
				for (int j = 0; j < num_vertices; j++)
				{
    
    
					//声明一个整型数组,用于存放与当前遍历顶点同属一个控制点的顶点序列
					//数组用的是FbxSdk内置的数组,是动态数组,比较好使
					FbxArray<int> SameControlPointsIndex;
					for (int k = 0; k < num_vertices; k++)
					{
    
    
						if (mesh->GetPolygonVertices()[k] == mesh->GetPolygonVertices()[j])
						{
    
    
							SameControlPointsIndex.Add(k);
						}
					}

					//声明一个Vector4数组,获取并存放上面声明的顶点序列数组中所有不同方向的法线
					//需要注意的是,与Unity的顶点不同,这里的顶点中有很多法线的方向是重复的
					//如果将重复的法线也参与计算则算出来的值是错误的,轮廓线会扭曲,说出来都是泪
					//所以使用AddUnique保证去掉重复的法线方向
					FbxArray<FbxVector4> Normals;
					for (int x = 0; x < SameControlPointsIndex.Size(); x++)
					{
    
    
						FbxVector4 Normal = VertNormal->GetDirectArray()[SameControlPointsIndex[x]];
						Normals.AddUnique(Normal);
					}
					//将所有不同方向的法线加在一起并归一化获得光滑法线
					FbxVector4 SmoothNormal;
					for (int n = 0; n < Normals.Size(); n++)
					{
    
    
						SmoothNormal += Normals[n];
					}
					SmoothNormal.Normalize();
					//分别获取当前顶点的切线、法线、副切线用于构建模型→切线空间的转换矩阵
					//需要注意的是:法线、切线、副切线的映射方式(也就是存储方式)是与顶点
					//序列一一对应,所以直接GetDirectArray()[顶点序号]就可以

					FbxVector4 Tangent = VertTangent->GetDirectArray()[j];
					FbxVector4 Normal = VertNormal->GetDirectArray()[j];
					FbxVector4 Bitangent = VertBinomral->GetDirectArray()[j];

					//FbxVector4 Bitangent = Normal.CrossProduct(Tangent) * Tangent[3];
					//Bitangent.Normalize();


					//将法线从模型空间转为切线空间
					//FbxSdk的内置矩阵类型不会使,算出来的值有问题,所以还是手动计算
					FbxVector4 tmpVector;
					tmpVector = SmoothNormal;
					tmpVector[0] = Tangent.DotProduct(SmoothNormal);
					tmpVector[1] = Bitangent.DotProduct(SmoothNormal);
					tmpVector[2] = Normal.DotProduct(SmoothNormal);
					tmpVector[3] = 0;
					SmoothNormal = tmpVector;
					SmoothNormal.Normalize();

					//获取当前顶点的颜色信息存放于其layer中的序号
					//与法切副不同,顶点色数据在layer中的存储方式(映射Mapping方式)稍微复杂
					//首先要使用GetIndexArray()[顶点序号]获取其颜色值在DirectArray中的序号
					//然后使用GetDirectArray()[获得的序号]来获得该顶点的顶点色信息
					int VertColorIndex = VertColor->GetIndexArray()[j];
					//声明一个颜色值,将法线数值范围从-1~1处理为0~1后存入RGB通道中,A通道保持
					//不变,因为其中存放着轮廓线大小信息
					FbxColor color;
					color.mRed = SmoothNormal[0] * 0.5f + 0.5f;
					color.mGreen = SmoothNormal[1] * 0.5f + 0.5f;
					color.mBlue = SmoothNormal[2] * 0.5f + 0.5f;
					color.mAlpha = VertColor->GetDirectArray()[VertColorIndex].mAlpha;
					//将颜色写入顶点颜色layer中
					VertColor->GetDirectArray().SetAt(VertColorIndex, color);
				}
			}
			//递归调用,确保场景中所有mesh都得到处理
			StoreNormalsToVertColor(node->GetChild(i));
		}
	}
}

// 如果没有是顶点色 先手动添加
void AddVertColor(FbxNode* node)
{
    
    
	if (node->GetChildCount())
	{
    
    
		for (int i = 0; i < node->GetChildCount(); i++)
		{
    
    
			if (node->GetChild(i)->GetMesh())
			{
    
    
				//获取mesh
				FbxMesh* mesh = node->GetChild(i)->GetMesh();

				int num_vertices = mesh->GetPolygonVertexCount();

				auto LayerZero = mesh->GetLayer(0);

				fbxsdk::FbxLayerElementVertexColor* VertexColor = fbxsdk::FbxLayerElementVertexColor::Create(mesh, "");
				VertexColor->SetMappingMode(fbxsdk::FbxLayerElement::eByPolygonVertex);
				VertexColor->SetReferenceMode(fbxsdk::FbxLayerElement::eIndexToDirect);
				FbxLayerElementArrayTemplate<FbxColor>& VertexColorArray = VertexColor->GetDirectArray();
				FbxLayerElementArrayTemplate<int>& VertexIndexArray = VertexColor->GetIndexArray();
				LayerZero->SetVertexColors(VertexColor);

				for (int VertIndex = 0; VertIndex < num_vertices; VertIndex++)
				{
    
    
					VertexColorArray.Add(FbxColor(1, 0, 0, 1));
					VertexIndexArray.Add(VertIndex);
				}

				
			}
			//递归调用,确保场景中所有mesh都得到处理
			AddVertColor(node->GetChild(i));
		}
	}
}

int main(int argc, char** argv) {
    
    

	for (int i = 1; i < argc; i++)
	{
    
    
		/*string name = argv[i];
		string name_export = name.replace(name.find(".FBX"), 4, "_outline.FBX");
		printf("%s \n", name_export.c_str());*/


		// lFilename是输入路径,lFilename2是输出路径
		//const char* lFilename = "C:\\Users\\LilithGames\\Desktop\\dsadas.FBX";
		//const char* lFilename2 = "C:\\Users\\LilithGames\\Desktop\\export.FBX";

		char* lFilename = argv[i];
		char* lFilename2 = argv[i];

		//主函数中几乎都是FbxSdk文档中所写的代码,是导入导出fbx所需要的的标准流程

		// Initialize the SDK manager. This object handles all our memory management.
		FbxManager* lSdkManager = FbxManager::Create();

		// Create the IO settings object.
		FbxIOSettings* ios = FbxIOSettings::Create(lSdkManager, IOSROOT);
		lSdkManager->SetIOSettings(ios);

		// Create an importer using the SDK manager.
		FbxImporter* lImporter = FbxImporter::Create(lSdkManager, "");

		// Use the first argument as the filename for the importer.
		if (!lImporter->Initialize(lFilename, -1, lSdkManager->GetIOSettings())) {
    
    
			printf("Call to FbxImporter::Initialize() failed.\n");
			printf("Error returned: %s\n\n", lImporter->GetStatus().GetErrorString());
			exit(-1);
		}

		// Create a new scene so that it can be populated by the imported file.
		FbxScene* lScene = FbxScene::Create(lSdkManager, "myScene");

		// Import the contents of the file into the scene.
		lImporter->Import(lScene);

		// The file is imported; so get rid of the importer.
		lImporter->Destroy();

		//获取场景中根节点,然后对其调用自定义的StoreNormalsToVertColor函数	
		FbxNode* lRootNode = lScene->GetRootNode();
		if (lRootNode) {
    
    
			AddVertColor(lRootNode);
			StoreNormalsToVertColor(lRootNode);
		}

		//导出Fbx文件

		int lFormat = lSdkManager->GetIOPluginRegistry()->FindWriterIDByDescription("FBX ascii (*.fbx)");
		lFormat = -1;

		FbxExporter* lExporter = FbxExporter::Create(lSdkManager, "");
		bool lExportStatus = lExporter->Initialize(lFilename2, lFormat, lSdkManager->GetIOSettings());
		if (!lExportStatus) {
    
    
			printf("Call to FbxExporter::Initialize() failed.\n");
			printf("Error returned: %s\n\n", lExporter->GetStatus().GetErrorString());
			return false;
		}
		lExporter->Export(lScene);
		lExporter->Destroy();

		// Destroy the SDK manager and all the other objects it was handling.
		lSdkManager->Destroy();
	}


	
	return 0;
}

猜你喜欢

转载自blog.csdn.net/A13155283231/article/details/118942801