From 0f24212b879e611896e6ad1ad43efe178e0faeb3 Mon Sep 17 00:00:00 2001 From: hesuicong Date: Mon, 13 Apr 2026 18:07:28 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B5=8B=E8=AF=95=E4=B8=AD=E9=97=B4=E7=89=88?= =?UTF-8?q?=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- libs/MVS/SceneTexture.cpp | 641 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 607 insertions(+), 34 deletions(-) diff --git a/libs/MVS/SceneTexture.cpp b/libs/MVS/SceneTexture.cpp index fe0c8a3..5523ebb 100644 --- a/libs/MVS/SceneTexture.cpp +++ b/libs/MVS/SceneTexture.cpp @@ -479,11 +479,15 @@ public: void LocalSeamLevelingExternalUV(); void GenerateTexture(bool bGlobalSeamLeveling, bool bLocalSeamLeveling, unsigned nTextureSizeMultiple, unsigned nRectPackingHeuristic, Pixel8U colEmpty, float fSharpnessWeight, int maxTextureSize, const SEACAVE::String& baseFileName, bool bOriginFaceview, Scene *pScene); + void GenerateTextureForUV(bool bGlobalSeamLeveling, bool bLocalSeamLeveling, unsigned nTextureSizeMultiple, unsigned nRectPackingHeuristic, Pixel8U colEmpty, float fSharpnessWeight, int maxTextureSize, const SEACAVE::String& baseFileName, bool bOriginFaceview, Scene *pScene); void GenerateTexture2(bool bGlobalSeamLeveling, bool bLocalSeamLeveling, unsigned nTextureSizeMultiple, unsigned nRectPackingHeuristic, Pixel8U colEmpty, float fSharpnessWeight, int maxTextureSize, const SEACAVE::String& baseFileName); bool TextureWithExistingUV(const IIndexArr& views, int nIgnoreMaskLabel, float fOutlierThreshold, unsigned nTextureSizeMultiple, Pixel8U colEmpty, float fSharpnessWeight); Mesh::Image8U3Arr GenerateTextureAtlasFromUV(const LabelArr& faceLabels, const IIndexArr& views, unsigned nTextureSizeMultiple, Pixel8U colEmpty, float fSharpnessWeight); Point2f ProjectPointWithAutoCorrection(const Camera& camera, const Vertex& worldPoint, const Image& sourceImage); Point2f ProjectPointRobust(const Camera& camera, const Vertex& worldPoint, const Image& sourceImage, float searchRadius = 0.02f); + Mesh::Image8U3Arr SampleFromExistingTextures(const LabelArr& faceLabels, const IIndexArr& views, unsigned nTextureSizeMultiple, Pixel8U colEmpty, float fSharpnessWeight); + Pixel8U SampleTextureBilinear(const Image8U3& texture, const Point2f& texCoord); + void ApplySharpening(Image8U3& texture, float fSharpnessWeight); bool ValidateProjection(const Vertex& worldPoint, const Image& sourceImage, Point2f imgPoint, float maxReprojectionError = 1.5f); Pixel8U SampleImageBilinear(const Image8U3& image, const Point2f& point); void ProjectFaceToTexture(FIndex faceID, IIndex viewID, const TexCoord* uv, Image8U3& texture); @@ -8830,6 +8834,368 @@ void MeshTexture::GenerateTexture(bool bGlobalSeamLeveling, bool bLocalSeamLevel out.close(); } } +void MeshTexture::GenerateTextureForUV(bool bGlobalSeamLeveling, bool bLocalSeamLeveling, unsigned nTextureSizeMultiple, unsigned nRectPackingHeuristic, Pixel8U colEmpty, float fSharpnessWeight, int maxTextureSize, const SEACAVE::String& basename, bool bOriginFaceview, Scene *pScene) +{ + int border = 2; + if (!bOriginFaceview) + border = 4; + + + faceTexcoords.resize(faces.size()*3); + faceTexindices.resize(faces.size()); + + #ifdef TEXOPT_USE_OPENMP + // LOG_OUT() << "def TEXOPT_USE_OPENMP" << std::endl; + const unsigned numPatches(texturePatches.size()-1); + + // ===== 修改:只在非外部UV模式下执行投影计算 ===== + { + #pragma omp parallel for schedule(dynamic) + for (int_t idx=0; idx<(int_t)numPatches; ++idx) { + TexturePatch& texturePatch = texturePatches[(uint32_t)idx]; + #else + for (TexturePatch *pTexturePatch=texturePatches.Begin(), *pTexturePatchEnd=texturePatches.End()-1; pTexturePatch 2 && (bGlobalSeamLeveling || bLocalSeamLeveling)) { + + LOG_OUT() << "GenerateTextureForUV 2" << std::endl; + // create seam vertices and edges + CreateSeamVertices(); + + LOG_OUT() << "GenerateTextureForUV 3" << std::endl; + // perform global seam leveling + if (bGlobalSeamLeveling) { + // TD_TIMER_STARTD(); + { + // GlobalSeamLeveling3(); + } + // DEBUG_EXTRA("\tglobal seam leveling completed (%s)", TD_TIMER_GET_FMT().c_str()); + } + + LOG_OUT() << "GenerateTextureForUV 4" << std::endl; + // perform local seam leveling + // if (bLocalSeamLeveling) + { + // TD_TIMER_STARTD(); + // LocalSeamLeveling(); + { + // LocalSeamLeveling3(); + } + // DEBUG_EXTRA("\tlocal seam leveling completed (%s)", TD_TIMER_GET_FMT().c_str()); + } + } + // DEBUG_EXTRA("seam (%s)", TD_TIMER_GET_FMT().c_str()); + + LOG_OUT() << "GenerateTextureForUV 5" << std::endl; + { + // merge texture patches with overlapping rectangles + for (unsigned i=0; i 0 && (texturePatches[i].rect.width > maxTextureSize || texturePatches[i].rect.height > maxTextureSize)) { + DEBUG("error: a patch of size %u x %u does not fit the texture", texturePatches[i].rect.width, texturePatches[i].rect.height); + ABORT("the maximum texture size chosen cannot fit a patch"); + } + unplacedRects[i] = {texturePatches[i].rect, i}; + } + LOG_OUT() << "unplacedRects loop completed" << std::endl; + + LOG_OUT() << "pack patches: one pack per texture file loop completed" << std::endl; + // pack patches: one pack per texture file + CLISTDEF2IDX(RectsBinPack::RectWIdxArr, TexIndex) placedRects; { + // increase texture size till all patches fit + // Bruce + unsigned typeRectsBinPack(nRectPackingHeuristic/100); + unsigned typeSplit((nRectPackingHeuristic-typeRectsBinPack*100)/10); + unsigned typeHeuristic(nRectPackingHeuristic%10); + if (!bOriginFaceview && false) + { + typeRectsBinPack = 1; + typeSplit = 0; + typeHeuristic = 1; + } + int textureSize = 0; + + { + while (!unplacedRects.empty()) { + TD_TIMER_STARTD(); + if (textureSize == 0) { + textureSize = RectsBinPack::ComputeTextureSize(unplacedRects, nTextureSizeMultiple); + if (maxTextureSize > 0 && textureSize > maxTextureSize) + textureSize = maxTextureSize; + } + + RectsBinPack::RectWIdxArr newPlacedRects; + switch (typeRectsBinPack) { + case 0: { + MaxRectsBinPack pack(textureSize, textureSize); + newPlacedRects = pack.Insert(unplacedRects, (MaxRectsBinPack::FreeRectChoiceHeuristic)typeHeuristic); + break; } + case 1: { + SkylineBinPack pack(textureSize, textureSize, typeSplit!=0); + newPlacedRects = pack.Insert(unplacedRects, (SkylineBinPack::LevelChoiceHeuristic)typeHeuristic); + break; } + case 2: { + GuillotineBinPack pack(textureSize, textureSize); + newPlacedRects = pack.Insert(unplacedRects, false, (GuillotineBinPack::FreeRectChoiceHeuristic)typeHeuristic, (GuillotineBinPack::GuillotineSplitHeuristic)typeSplit); + break; } + default: + ABORT("error: unknown RectsBinPack type"); + } + DEBUG_ULTIMATE("\tpacking texture completed: %u initial patches, %u placed patches, %u texture-size, %u textures (%s)", texturePatches.size(), newPlacedRects.size(), textureSize, placedRects.size(), TD_TIMER_GET_FMT().c_str()); + + if (textureSize == maxTextureSize || unplacedRects.empty()) { + // create texture image + placedRects.emplace_back(std::move(newPlacedRects)); + // Pixel8U colEmpty2=Pixel8U(0,0,255); + texturesDiffuse.emplace_back(textureSize, textureSize).setTo(cv::Scalar(colEmpty.b, colEmpty.g, colEmpty.r)); + textureSize = 0; + } else { + // try again with a bigger texture + textureSize *= 2; + if (maxTextureSize > 0) + textureSize = std::max(textureSize, maxTextureSize); + unplacedRects.JoinRemove(newPlacedRects); + } + } + } + } + LOG_OUT() << "Third loop completed" << std::endl; + + LOG_OUT() << "GenerateTextureForUV 7" << std::endl; + Mesh::FaceIdxArr emptyFaceIndexes; + { + + #ifdef TEXOPT_USE_OPENMP + #pragma omp parallel for schedule(dynamic) + for (int_t i=0; i<(int_t)placedRects.size(); ++i) { + for (int_t j=0; j<(int_t)placedRects[(TexIndex)i].size(); ++j) { + const TexIndex idxTexture((TexIndex)i); + const uint32_t idxPlacedPatch((uint32_t)j); + #else + FOREACH(idxTexture, placedRects) { + FOREACH(idxPlacedPatch, placedRects[idxTexture]) { + #endif + const TexturePatch& texturePatch = texturePatches[placedRects[idxTexture][idxPlacedPatch].patchIdx]; + const RectsBinPack::Rect& rect = placedRects[idxTexture][idxPlacedPatch].rect; + // copy patch image + ASSERT((rect.width == texturePatch.rect.width && rect.height == texturePatch.rect.height) || + (rect.height == texturePatch.rect.width && rect.width == texturePatch.rect.height)); + int x(0), y(1); + if (texturePatch.label != NO_ID) { + const Image& imageData = images[texturePatch.label]; + cv::Mat patch(imageData.image(texturePatch.rect)); + if (rect.width != texturePatch.rect.width) { + // flip patch and texture-coordinates + patch = patch.t(); + x = 1; y = 0; + } + + patch.copyTo(texturesDiffuse[idxTexture](rect)); + } + else + { + auto it = texturePatch.faces.begin(); + while (it != texturePatch.faces.end()) + { + emptyFaceIndexes.push_back(*it); + ++it; + } + } + // compute final texture coordinates + const TexCoord offset(rect.tl()); + for (const FIndex idxFace: texturePatch.faces) { + TexCoord* texcoords = faceTexcoords.data()+idxFace*3; + faceTexindices[idxFace] = idxTexture; + for (int v=0; v<3; ++v) { + TexCoord& texcoord = texcoords[v]; + texcoord = TexCoord( + texcoord[x]+offset.x, + texcoord[y]+offset.y + ); + } + } + } + } + } + + LOG_OUT() << "GenerateTextureForUV 8" << std::endl; + if (texturesDiffuse.size() == 1) + faceTexindices.Release(); + + // apply some sharpening + if (fSharpnessWeight > 0) { + constexpr double sigma = 1.5; + for (auto &textureDiffuse: texturesDiffuse) { + Image8U3 blurryTextureDiffuse; + cv::GaussianBlur(textureDiffuse, blurryTextureDiffuse, cv::Size(), sigma); + cv::addWeighted(textureDiffuse, 1+fSharpnessWeight, blurryTextureDiffuse, -fSharpnessWeight, 0, textureDiffuse); + } + } + LOG_OUT() << "Fourth loop completed" << std::endl; + + std::ofstream out(basename + "_empty_color_triangles.txt"); + RFOREACHPTR(pIdxF, emptyFaceIndexes) { + out << *pIdxF << "\n"; + } + out.close(); + } +} void MeshTexture::GlobalSeamLevelingExternalUV() { @@ -9574,6 +9940,7 @@ bool SaveGeneratedTextures(const Mesh::Image8U3Arr& generatedTextures, const std return true; } +//* bool MeshTexture::TextureWithExistingUV(const IIndexArr& views, int nIgnoreMaskLabel, float fOutlierThreshold, unsigned nTextureSizeMultiple, Pixel8U colEmpty, float fSharpnessWeight) { DEBUG_EXTRA("TextureWithExistingUV 1"); @@ -9646,6 +10013,92 @@ bool MeshTexture::TextureWithExistingUV(const IIndexArr& views, int nIgnoreMaskL DEBUG_EXTRA("纹理生成失败"); return false; } +//*/ + +/* +bool MeshTexture::TextureWithExistingUV(const IIndexArr& views, int nIgnoreMaskLabel, float fOutlierThreshold, unsigned nTextureSizeMultiple, Pixel8U colEmpty, float fSharpnessWeight) +{ + DEBUG_EXTRA("TextureWithExistingUV 1 - 从已生成纹理采样"); + TD_TIMER_START(); + + // 1. 验证输入 + if (scene.mesh.faceTexcoords.empty()) { + VERBOSE("error: mesh does not contain UV coordinates"); + return false; + } + + if (scene.mesh.faceTexcoords.size() != scene.mesh.faces.size() * 3) { + VERBOSE("error: UV coordinates count does not match face count, %d, %d", scene.mesh.faceTexcoords.size(), scene.mesh.faces.size() * 3); + return false; + } + + // 检查是否已经有生成的纹理 + if (!scene.mesh.texturesDiffuse.empty() && !scene.mesh.faceTexindices.empty()) { + DEBUG_EXTRA("TextureWithExistingUV: 使用已生成的纹理,数量: %zu", scene.mesh.texturesDiffuse.size()); + return true; // 已经通过 GenerateTextureForUV 生成了纹理,直接返回成功 + } + + // 如果没有已生成的纹理,重新从图像投影生成 + DEBUG_EXTRA("TextureWithExistingUV 2 - 从图像重新生成纹理"); + + // 2. 为每个面选择最佳视图 + FaceDataViewArr facesDatas; + if (!ListCameraFaces(facesDatas, fOutlierThreshold, nIgnoreMaskLabel, views, false)) { + return false; + } + + DEBUG_EXTRA("TextureWithExistingUV 3 faceSize=%d", scene.mesh.faces.size()); + + // 3. 为每个面分配最佳视图 + LabelArr faceLabels(scene.mesh.faces.size()); + FOREACH(idxFace, scene.mesh.faces) { + const FaceDataArr& faceDatas = facesDatas[idxFace]; + if (faceDatas.empty()) { + faceLabels[idxFace] = 0; // 无视图可用 + continue; + } + + // 选择质量最高的视图 + float bestQuality = -1; + IIndex bestView = NO_ID; + for (const FaceData& data : faceDatas) { + if (data.quality > bestQuality && !data.bInvalidFacesRelative) { + bestQuality = data.quality; + bestView = data.idxView; + } + } + + faceLabels[idxFace] = (bestView != NO_ID) ? (bestView + 1) : 0; + } + + // 4. 从现有纹理采样生成纹理图集 + Mesh::Image8U3Arr generatedTextures = SampleFromExistingTextures(faceLabels, views, nTextureSizeMultiple, colEmpty, fSharpnessWeight); + + if (!generatedTextures.empty()) { + scene.mesh.texturesDiffuse = std::move(generatedTextures); + + // 同时设置面的纹理索引 + scene.mesh.faceTexindices.resize(scene.mesh.faces.size()); + for (size_t i = 0; i < faceLabels.size(); ++i) { + // 假设所有面都在第一个纹理中 + scene.mesh.faceTexindices[i] = 0; + } + + DEBUG_EXTRA("成功生成 %zu 个纹理,已赋值给 mesh", scene.mesh.texturesDiffuse.size()); + + // 🆕 保存纹理到文件 + std::string outputDir = "texture_output"; + if (SaveGeneratedTextures(scene.mesh.texturesDiffuse, outputDir)) { + DEBUG_EXTRA("纹理已成功保存到目录: %s", outputDir.c_str()); + } + + return true; + } + + DEBUG_EXTRA("纹理生成失败"); + return false; +} +*/ Point2f MeshTexture::ProjectPointWithAutoCorrection(const Camera& camera, const Vertex& worldPoint, const Image& sourceImage) { Point2f imgPoint = camera.ProjectPointP(worldPoint); @@ -10151,6 +10604,15 @@ void FillTextureGaps(Image8U3& textureAtlas, const Mesh::TexCoordArr& faceTexcoo Mesh::Image8U3Arr MeshTexture::GenerateTextureAtlasFromUV(const LabelArr& faceLabels, const IIndexArr& views, unsigned nTextureSizeMultiple, Pixel8U colEmpty, float fSharpnessWeight) { + // 如果已经有生成的纹理,直接返回 + if (!scene.mesh.texturesDiffuse.empty()) { + DEBUG_EXTRA("GenerateTextureAtlasFromUV: 使用已生成的纹理,数量: %zu", scene.mesh.texturesDiffuse.size()); + return scene.mesh.texturesDiffuse; + } + + // 否则,重新生成纹理 + DEBUG_EXTRA("GenerateTextureAtlasFromUV: 从图像重新生成纹理"); + // 1. 分析整个模型的UV布局 AABB2f uvBounds(true); FOREACH(i, scene.mesh.faceTexcoords) { @@ -10308,7 +10770,7 @@ Mesh::Image8U3Arr MeshTexture::GenerateTextureAtlasFromUV(const LabelArr& faceLa } // 5. 添加后处理填充函数 - FillTextureGaps(textureAtlas, scene.mesh.faceTexcoords, (FIndex)scene.mesh.faces.size(), faceLabels, textureSize, colEmpty); + FillTextureGaps(textureAtlas, scene.mesh.faceTexcoords, (FIndex)scene.mesh.faces.size(), faceLabels, textureSize, colEmpty); // 6. 应用后处理 if (fSharpnessWeight > 0) { @@ -10321,6 +10783,146 @@ Mesh::Image8U3Arr MeshTexture::GenerateTextureAtlasFromUV(const LabelArr& faceLa return textures; } +Mesh::Image8U3Arr MeshTexture::SampleFromExistingTextures(const LabelArr& faceLabels, const IIndexArr& views, + unsigned nTextureSizeMultiple, Pixel8U colEmpty, float fSharpnessWeight) +{ + DEBUG_EXTRA("SampleFromExistingTextures: 从已生成纹理采样"); + + // 1. 检查是否有已生成的纹理 + if (scene.mesh.texturesDiffuse.empty() || scene.mesh.faceTexindices.empty()) { + DEBUG_EXTRA("SampleFromExistingTextures: 没有已生成的纹理,回退到 GenerateTextureAtlasFromUV"); + return GenerateTextureAtlasFromUV(faceLabels, views, nTextureSizeMultiple, colEmpty, fSharpnessWeight); + } + + // 2. 分析整个模型的UV布局 + AABB2f uvBounds(true); + FOREACH(i, scene.mesh.faceTexcoords) { + const TexCoord& uv = scene.mesh.faceTexcoords[i]; + uvBounds.InsertFull(uv); + } + + // 3. 根据UV范围确定纹理图集尺寸 + const int textureSize = ComputeOptimalTextureSize(uvBounds.ptMin, uvBounds.ptMax, nTextureSizeMultiple); + + // 4. 创建目标纹理图集 + Mesh::Image8U3Arr textures; + Image8U3& textureAtlas = textures.emplace_back(textureSize, textureSize); + textureAtlas.setTo(cv::Scalar(colEmpty.b, colEmpty.g, colEmpty.r)); + + DEBUG_EXTRA("从已生成纹理采样: 创建纹理图集 %dx%d", textureSize, textureSize); + + // 5. 为每个面片从现有纹理采样 + #ifdef _USE_OPENMP + #pragma omp parallel for schedule(dynamic) + for (int_t idxFace = 0; idxFace < (int_t)scene.mesh.faces.size(); ++idxFace) { + #else + FOREACH(idxFace, scene.mesh.faces) { + #endif + const FIndex faceID = (FIndex)idxFace; + + // 获取面的纹理索引 + if (faceID >= scene.mesh.faceTexindices.size()) { + continue; + } + + TexIndex texIdx = scene.mesh.faceTexindices[faceID]; + if (texIdx >= scene.mesh.texturesDiffuse.size()) { + continue; + } + + // 获取源纹理和目标纹理 + const Image8U3& sourceTexture = scene.mesh.texturesDiffuse[texIdx]; + + // 获取面的UV坐标 + const TexCoord* uvCoords = &scene.mesh.faceTexcoords[faceID * 3]; + + // 计算面片在纹理图集中的边界框 + AABB2f faceUVBounds(true); + for (int i = 0; i < 3; ++i) { + faceUVBounds.InsertFull(uvCoords[i]); + } + + // 将UV坐标转换到纹理像素坐标 + const int startX = std::max(0, (int)(faceUVBounds.ptMin.x() * textureSize)); + const int startY = std::max(0, (int)(faceUVBounds.ptMin.y() * textureSize)); + const int endX = std::min(textureSize - 1, (int)(faceUVBounds.ptMax.x() * textureSize)); + const int endY = std::min(textureSize - 1, (int)(faceUVBounds.ptMax.y() * textureSize)); + + // 对面片覆盖的每个纹理像素进行采样 + for (int y = startY; y <= endY; ++y) { + for (int x = startX; x <= endX; ++x) { + const Point2f texCoord((float)x / textureSize, (float)y / textureSize); + + // 检查是否在三角形内 + Point3f barycentric; + if (PointInTriangle(texCoord, uvCoords[0], uvCoords[1], uvCoords[2], barycentric)) { + // 从源纹理采样颜色 + Pixel8U sampledColor = SampleTextureBilinear(sourceTexture, texCoord); + + // 将采样颜色写入纹理图集 + #ifdef _USE_OPENMP + #pragma omp critical + #endif + { + textureAtlas(y, x) = sampledColor; + } + } + } + } + } + + // 6. 应用后处理 + if (fSharpnessWeight > 0) { + ApplySharpening(textureAtlas, fSharpnessWeight); + } + + DEBUG_EXTRA("从已生成纹理采样完成: %u个面片, 纹理尺寸%dx%d", + scene.mesh.faces.size(), textureSize, textureSize); + + return textures; +} + +// 辅助函数:从纹理中双线性采样 +Pixel8U MeshTexture::SampleTextureBilinear(const Image8U3& texture, const Point2f& texCoord) { + const float u = texCoord.x * texture.cols; + const float v = texCoord.y * texture.rows; + + const int x = (int)u; + const int y = (int)v; + const float dx = u - x; + const float dy = v - y; + + const int x1 = std::min(x, texture.cols - 1); + const int y1 = std::min(y, texture.rows - 1); + const int x2 = std::min(x + 1, texture.cols - 1); + const int y2 = std::min(y + 1, texture.rows - 1); + + const Pixel8U& p11 = texture(y1, x1); + const Pixel8U& p12 = texture(y2, x1); + const Pixel8U& p21 = texture(y1, x2); + const Pixel8U& p22 = texture(y2, x2); + + // 双线性插值 + Pixel8U result; + for (int c = 0; c < 3; ++c) { + const float a = p11[c] * (1 - dx) + p21[c] * dx; + const float b = p12[c] * (1 - dx) + p22[c] * dx; + result[c] = (uint8_t)(a * (1 - dy) + b * dy); + } + + return result; +} + +// 辅助函数:应用锐化 +void MeshTexture::ApplySharpening(Image8U3& texture, float fSharpnessWeight) { + if (fSharpnessWeight <= 0) return; + + constexpr double sigma = 1.5; + Image8U3 blurryTexture; + cv::GaussianBlur(texture, blurryTexture, cv::Size(), sigma); + cv::addWeighted(texture, 1 + fSharpnessWeight, blurryTexture, -fSharpnessWeight, 0, texture); +} + bool MeshTexture::ValidateProjection(const Vertex& worldPoint, const Image& sourceImage, Point2f imgPoint, @@ -10877,39 +11479,6 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi DEBUG_EXTRA("First pass (virtual faces) completed: %u faces (%s)", mesh.faces.size(), TD_TIMER_GET_FMT().c_str()); } - // 第二轮处理:专门处理无效面片(非虚拟面映射) - if (false) - { - TD_TIMER_STARTD(); - - // 收集所有无效面片的索引 - Mesh::FaceIdxArr invalidFaces; - for (FIndex fid = 0; fid < mesh.faces.size(); ++fid) { - if (texture.scene.mesh.invalidFaces.data.contains(fid)) { - invalidFaces.push_back(fid); - } - } - - if (!invalidFaces.empty()) { - // 创建新的纹理处理器,专门处理无效面片 - MeshTexture textureInvalid(*this, nResolutionLevel, nMinResolution); - - // 使用非虚拟面模式处理无效面片 - if (!textureInvalid.FaceViewSelection4(0, fOutlierThreshold, fRatioDataSmoothness, - nIgnoreMaskLabel, views, &invalidFaces)) - { - return false; - } - - // 合并两轮处理的结果 - texture.texturePatches.Join(textureInvalid.texturePatches); - texture.seamEdges.Join(textureInvalid.seamEdges); - } - - DEBUG_EXTRA("Second pass (invalid faces) completed: %u faces (%s)", - invalidFaces.size(), TD_TIMER_GET_FMT().c_str()); - } - } DEBUG_EXTRA("Assigning the best view to each face completed: %u faces (%s)", mesh.faces.size(), TD_TIMER_GET_FMT().c_str()); } @@ -10934,6 +11503,9 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi return false; } + texture.GenerateTextureForUV(bGlobalSeamLeveling, bLocalSeamLeveling, nTextureSizeMultiple, nRectPackingHeuristic, colEmpty, fSharpnessWeight, maxTextureSize, baseFileName, bOriginFaceview, this); + + // 使用新的纹理生成方法 MeshTexture texture(*this, nResolutionLevel, nMinResolution); if (!texture.TextureWithExistingUV(views, nIgnoreMaskLabel, fOutlierThreshold, nTextureSizeMultiple, colEmpty, fSharpnessWeight)){ @@ -10947,6 +11519,7 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi // mesh.CheckUVValid(); // generate the texture image and atlas + if (false) { TD_TIMER_STARTD(); texture.GenerateTexture(bGlobalSeamLeveling, bLocalSeamLeveling, nTextureSizeMultiple, nRectPackingHeuristic, colEmpty, fSharpnessWeight, maxTextureSize, baseFileName, bOriginFaceview, this);