一个简单mesh顶点合并方式
主要思路
给每个顶点计算一个key,同key的顶点直接去掉,用之前已经存在的顶点的索引替换即可,下面上代码
std::vector<float> inVerties; //处理前的顶点
std::vector<float> inNormals; //处理前的法向
std::vector<unsigned int> inIndices; //处理前的索引
std::vector<float> outVerties; //处理后的顶点
std::vector<float> outNormals; //处理后的法向
std::vector<unsigned int> outIndices; //处理后的索引
if (inVerties.size() <3 || inIndices.size() < 3 || inVerties.size() != inNormals.size() )
{
return;
}
//一次性给输出的vector分配内存
outVerties.reserve(inVerties.size());
outIndices.reserve(inIndices.size());
outNormals.reserve(inNormals.size());
struct Info
{
size_t index;
float normal[3];
};
//用map开存放顶点的key
//我这里key算的比较简单 直接用顶点的xyz值放大取整拼接成字符串,也可以直接用位运算直接算一个long值
//同key 的顶点存储在value内
std::unordered_map<std::string, std::vector<Info>> remap;
for (size_t i = 0; i < inIndices.size(); i++)
{
unsigned int index = inIndices[i];
unsigned int pIndex = index * 3;
char buffer[256];
//计算key
sprintf(buffer, "%d|%d|%d", (int)(inVerties[pIndex] * 10e6 + 0.5), (int)(inVerties[pIndex + 1] * 10e6 + 0.5), (int)(inVerties[pIndex + 2] * 10e6 + 0.5));
std::string key = buffer;
//找不到对应的key 添加顶点 法向 索引到输出数组
if (remap.find(buffer) == remap.end())
{
remap[key] = std::vector<Info>();
remap[key].emplace_back(Info{ outVerties.size() / 3, inNormals[pIndex],inNormals[pIndex + 1],inNormals[pIndex + 2] });
outIndices.push_back(outVerties.size() / 3);
outVerties.push_back(inVerties[pIndex]);
outVerties.push_back(inVerties[pIndex + 1]);
outVerties.push_back(inVerties[pIndex + 2]);
outNormals.push_back(inNormals[pIndex]);
outNormals.push_back(inNormals[pIndex + 1]);
outNormals.push_back(inNormals[pIndex + 2]);
continue;
}
//这里找到key时候分两种情况
//一直是可以合并的,判断条件可以根据直接业务来,我这里是当前顶点的法向和之前对比顶点的法向成锐角切小于60度时候合并,因为这种情况下,这两个顶点构成的三角面是钝角,一般是平面或者比较圆滑一些的面
//如果这两个顶点构成的三角面是锐角或者角度很小的的情况下,直接合并,比如对应一个正方体,有些引擎渲染的时候会渲染的很圆润,但是实际上正方体是需要棱角的;
bool isFind = false;
for (Info& info : remap[key])
{
float* normal = &inNormals[pIndex];
float* n = info.normal;
if (Angle(normal, n) < _PI * 0.3)
{
outIndices.push_back(info.index);
outNormals[info.index * 3] = (outNormals[info.index*3] + inNormals[pIndex]) *0.5;
outNormals[info.index * 3 + 1] = (outNormals[info.index*3 + 1] + inNormals[pIndex + 1]) * 0.5;
outNormals[info.index * 3 + 2] = (outNormals[info.index*3 + 2] + inNormals[pIndex + 2]) * 0.5;
isFind = true;
break;
}
}
//找不到合适合并的顶点直接输出
if (!isFind)
{
remap[key].emplace_back(Info{ outVerties.size() / 3, inNormals[pIndex],inNormals[pIndex + 1],inNormals[pIndex + 2] });
outIndices.push_back(outVerties.size() / 3);
outVerties.push_back(inVerties[pIndex]);
outVerties.push_back(inVerties[pIndex + 1]);
outVerties.push_back(inVerties[pIndex + 2]);
outNormals.push_back(inNormals[pIndex]);
outNormals.push_back(inNormals[pIndex + 1]);
outNormals.push_back(inNormals[pIndex + 2]);
}
}
//由于前面的的法向都是直接加进去的,所以这里把法向取成单位向量即可
size_t size = outNormals.size();
for (size_t i = 0; i < size-2; i+=3)
{
Normalized(&outNormals[i]);
}