From 3adc1c012a3f353cb9054ac90e6f99fe8735bd1d Mon Sep 17 00:00:00 2001 From: hesuicong Date: Fri, 17 Jul 2026 17:18:40 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=AD=E9=97=B4=E6=B5=81=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- libs/MVS/SceneTexture.cpp | 187 +++++++++++++++++++------------------- 1 file changed, 92 insertions(+), 95 deletions(-) diff --git a/libs/MVS/SceneTexture.cpp b/libs/MVS/SceneTexture.cpp index 4a54a8f..3e67881 100644 --- a/libs/MVS/SceneTexture.cpp +++ b/libs/MVS/SceneTexture.cpp @@ -14266,7 +14266,7 @@ bool MeshTexture::TextureWithExistingUVVirtualFaces(const IIndexArr& views, int float fOutlierThreshold, unsigned nTextureSizeMultiple, Pixel8U colEmpty, float fSharpnessWeight) { - DEBUG_EXTRA("TextureWithExistingUVVirtualFaces with multi-view blending"); + DEBUG_EXTRA("TextureWithExistingUVVirtualFaces (STABLE MODE: 1 Face = 1 VirtualFace)"); TD_TIMER_START(); // 1. 验证输入 @@ -14281,134 +14281,131 @@ bool MeshTexture::TextureWithExistingUVVirtualFaces(const IIndexArr& views, int return false; } - // 2. 创建虚拟面 - DEBUG_EXTRA("Creating virtual faces for existing UV texture mapping..."); + const size_t numFaces = scene.mesh.faces.size(); + + // ========================================================== + // 关键修改点 1:强制 1:1 映射,绕过有问题的 VirtualFace 合并逻辑 + // ========================================================== VirtualFaceMap virtualFaceMap; - if (!CreateVirtualFacesForExistingUV(virtualFaceMap)) { - DEBUG_EXTRA("Failed to create virtual faces, falling back to original faces"); - // 回退到原始面片 - virtualFaceMap.clear(); - for (FIndex fid = 0; fid < faces.size(); ++fid) { - virtualFaceMap.push_back(VirtualFace()); - VirtualFace& vf = virtualFaceMap.back(); - vf.faces.push_back(fid); - } + virtualFaceMap.reserve(numFaces); + for (FIndex fid = 0; fid < numFaces; ++fid) { + VirtualFace vf; + vf.faces.push_back(fid); + virtualFaceMap.push_back(vf); } - - DEBUG_EXTRA("Created %zu virtual faces (original faces: %zu)", virtualFaceMap.size(), faces.size()); - - // 3. 为每个虚拟面收集视图数据 + DEBUG_EXTRA("Forced 1-to-1 mapping: %zu virtual faces for %zu original faces", + virtualFaceMap.size(), numFaces); + + // 2. 为每个虚拟面(其实就是原始面片)收集视图数据 + // ListCameraVirtualFaces 内部会调用 Rasterize,用于可见性判断 VirtualFaceDataArr virtualFaceDatas; if (!ListCameraVirtualFaces(virtualFaceMap, virtualFaceDatas, fOutlierThreshold, nIgnoreMaskLabel, views, false)) { return false; } - // 4. 为每个虚拟面选择最佳视图,支持多视图融合 + // ========================================================== + // 关键修改点 2:不再做多视图融合,直接选 Best View + // ========================================================== std::vector> virtualFaceViews(virtualFaceMap.size()); std::vector> virtualFaceViewWeights(virtualFaceMap.size()); #ifdef _USE_OPENMP #pragma omp parallel for schedule(dynamic) - for (int_t idxVF = 0; idxVF < (int_t)virtualFaceMap.size(); ++idxVF) { - #else - for (size_t idxVF = 0; idxVF < virtualFaceMap.size(); ++idxVF) { #endif + for (int_t idxVF = 0; idxVF < (int_t)virtualFaceMap.size(); ++idxVF) { + const FIndex fid = virtualFaceMap[idxVF].faces[0]; // 取出唯一的原始面片ID const FaceDataArr& vfDatas = virtualFaceDatas[idxVF]; - std::vector> viewScores; - // 收集所有有效视图 + float bestWeight = -1.0f; + IIndex bestViewID = IIndex(-1); + + // 遍历能看到这个面片的所有相机 for (const FaceData& data : vfDatas) { - if (!data.bInvalidFacesRelative) { - // 使用 FaceData 中的视图质量 - float weight = data.quality; - - // 添加视角与法线夹角的权重 - const Image& image = images[data.idxView]; - const Normal& faceNormal = scene.mesh.faceNormals[virtualFaceMap[idxVF].faces[0]]; // 使用第一个面的法线 - - // 计算相机方向 - Point3f cameraForward(0, 0, -1); // 相机前方 - Point3f cameraForwardWorld; - cameraForwardWorld.x = (float)(image.camera.R(0,0) * cameraForward.x + - image.camera.R(0,1) * cameraForward.y + - image.camera.R(0,2) * cameraForward.z); - cameraForwardWorld.y = (float)(image.camera.R(1,0) * cameraForward.x + - image.camera.R(1,1) * cameraForward.y + - image.camera.R(1,2) * cameraForward.z); - cameraForwardWorld.z = (float)(image.camera.R(2,0) * cameraForward.x + - image.camera.R(2,1) * cameraForward.y + - image.camera.R(2,2) * cameraForward.z); - cameraForward = cameraForwardWorld; - - float len = sqrt(cameraForward.x * cameraForward.x + - cameraForward.y * cameraForward.y + - cameraForward.z * cameraForward.z); - if (len > 0) { - cameraForward.x /= len; - cameraForward.y /= len; - cameraForward.z /= len; - } - - // 法线与相机方向的点积(越大越好,表示面正对相机) - float dotProduct = faceNormal.dot(cameraForward); - float angleWeight = (dotProduct + 1.0f) * 0.5f; // 归一化到[0,1] - - // 综合权重 - float finalWeight = weight * 0.7f + angleWeight * 0.3f; - - viewScores.emplace_back(finalWeight, data.idxView); - } - } - - // 按权重排序,选择前N个视图 - std::sort(viewScores.begin(), viewScores.end(), - [](const auto& a, const auto& b) { return a.first > b.first; }); - - const int maxViews = 5; // 最多使用5个视图进行融合 - float totalWeight = 0.0f; - - for (int i = 0; i < std::min((int)viewScores.size(), maxViews); ++i) { - if (viewScores[i].first > 0.1f) { // 质量阈值 - virtualFaceViews[idxVF].push_back(viewScores[i].second); - virtualFaceViewWeights[idxVF].push_back(viewScores[i].first); - totalWeight += viewScores[i].first; + if (data.bInvalidFacesRelative) continue; + + // 基础质量分数 + float weight = data.quality; + + // 加上法线夹角权重(正对相机的权重更高) + const Image& image = images[data.idxView]; + // ✅ 使用 double 向量,和 RMatrix 精度一致 + Point3d camDir(0, 0, -1); + camDir = image.camera.R * camDir; // ✅ RMatrix × Point3d + + // ✅ 归一化(double 版本) + const double len = sqrt( + camDir.x * camDir.x + + camDir.y * camDir.y + + camDir.z * camDir.z + ); + if (len > 0.0) { + camDir.x /= len; + camDir.y /= len; + camDir.z /= len; + } + + // ✅ 转回 float,用于和法线点积 + Point3f cameraForward( + static_cast(camDir.x), + static_cast(camDir.y), + static_cast(camDir.z) + ); + + // ✅ 法线夹角权重 + const Normal& faceNormal = scene.mesh.faceNormals[fid]; + float dotProduct = faceNormal.dot(cameraForward); + float angleWeight = (dotProduct + 1.0f) * 0.5f; + + // 综合权重 + float finalWeight = weight * 0.7f + angleWeight * 0.3f; + + if (finalWeight > bestWeight) { + bestWeight = finalWeight; + bestViewID = data.idxView; } } - - // 归一化权重 - if (totalWeight > 0.0f) { - for (float& w : virtualFaceViewWeights[idxVF]) { - w /= totalWeight; - } + + // 如果找到了有效的视图 + if (bestViewID != IIndex(-1) && bestWeight > 0.1f) { + virtualFaceViews[idxVF] = { bestViewID }; + virtualFaceViewWeights[idxVF] = { 1.0f }; // 单视图权重设为1 } } - - // 5. 为虚拟面生成纹理图集(使用多视图融合) - Mesh::Image8U3Arr textures = GenerateMultiViewTextureAtlasWithVirtualFaces(virtualFaceMap, virtualFaceDatas, - virtualFaceViews, virtualFaceViewWeights, - nTextureSizeMultiple, colEmpty, fSharpnessWeight); + + // 3. 生成纹理图集 + // 此时调用的 GenerateMultiViewTextureAtlasWithVirtualFaces 内部, + // 虽然名字带 MultiView,但因为权重都是1且只有一个View, + // 实际上执行的是 Single View Rendering。 + // 配合之前的 MSAA 代码,就能得到清晰且无锯齿的结果。 + Mesh::Image8U3Arr textures = GenerateMultiViewTextureAtlasWithVirtualFaces( + virtualFaceMap, + virtualFaceDatas, + virtualFaceViews, + virtualFaceViewWeights, + nTextureSizeMultiple, + colEmpty, + fSharpnessWeight + ); if (!textures.empty()) { scene.mesh.texturesDiffuse = std::move(textures); - // 设置纹理索引 - scene.mesh.faceTexindices.resize(scene.mesh.faces.size()); - for (size_t i = 0; i < scene.mesh.faces.size(); ++i) { + // 设置纹理索引(所有面片使用第0张纹理图) + scene.mesh.faceTexindices.resize(numFaces); + for (size_t i = 0; i < numFaces; ++i) { scene.mesh.faceTexindices[i] = 0; } - DEBUG_EXTRA("Successfully generated %zu texture atlases with virtual faces", scene.mesh.texturesDiffuse.size()); + DEBUG_EXTRA("Successfully generated %zu texture atlases (Stable Mode)", scene.mesh.texturesDiffuse.size()); // 保存纹理 std::string outputDir = "texture_output"; - if (SaveGeneratedTextures(scene.mesh.texturesDiffuse, outputDir)) { - DEBUG_EXTRA("Textures saved to: %s", outputDir.c_str()); - } + SaveGeneratedTextures(scene.mesh.texturesDiffuse, outputDir); return true; } - DEBUG_EXTRA("Texture generation with virtual faces failed"); + DEBUG_EXTRA("Texture generation failed in stable mode"); return false; }