From e2088836385d87432a0ec22884edf0d6afba3242 Mon Sep 17 00:00:00 2001 From: hesuicong Date: Tue, 4 Aug 2026 16:52:45 +0800 Subject: [PATCH] =?UTF-8?q?=E5=90=88=E5=B9=B6=E5=B0=8F=E4=B8=89=E8=A7=92?= =?UTF-8?q?=E5=BD=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- libs/MVS/SceneTexture.cpp | 800 +++++++++++++++----------------------- 1 file changed, 312 insertions(+), 488 deletions(-) diff --git a/libs/MVS/SceneTexture.cpp b/libs/MVS/SceneTexture.cpp index 969a31e..ac7282d 100644 --- a/libs/MVS/SceneTexture.cpp +++ b/libs/MVS/SceneTexture.cpp @@ -539,6 +539,14 @@ struct MeshTexture { } }; + struct VirtualFaceGeometry { + Point3f center; // 面片中心(世界坐标) + Point3f normal; // 面片法线 + AABB2f uvBounds; // UV 包围盒 + cv::Mat1f homography; // 3x3 单应矩阵(从 UV 到视图) + bool isValid = false; + }; + // used to interpolate adjustments color over the whole texture patch typedef TImage ColorMap; @@ -550,6 +558,7 @@ struct MeshTexture { : r(_r), g(_g), b(_b), a(_a) {} }; */ + std::vector m_virtualFaceGeometries; public: MeshTexture(Scene& _scene, unsigned _nResolutionLevel=0, unsigned _nMinResolution=640); @@ -621,12 +630,20 @@ public: unsigned minCommonCameras, float fOutlierThreshold, float fRatioDataSmoothness, int nIgnoreMaskLabel, const IIndexArr& views); + + bool ComputeVirtualFaceGeometry(const VirtualFaceMap& virtualFaceMap); + + bool ComputeHomographyForVirtualFace( + const VirtualFace& vf, + IIndex viewID, + VirtualFaceGeometry& geom); + std::vector> faceViews; std::vector> faceViewWeights; std::vector virtualFaceDatas; std::vector> faceNeighbors; - VirtualFaceGeometryArr m_virtualFaceGeometries; + // VirtualFaceGeometryArr m_virtualFaceGeometries; inline Point3f NormalizePoint3(Point3f& p) { @@ -14161,6 +14178,9 @@ void MeshTexture::FillTextureHoles(std::vector& textures, Pixel8U colE DEBUG_EXTRA("Hole filling completed"); } +// ============================================================ +// 3. RC 风格光栅化主函数 +// ============================================================ bool MeshTexture::RasterizeVirtualFaces( const VirtualFaceMap& virtualFaceMap, const std::vector>& virtualFaceViews, @@ -14169,7 +14189,7 @@ bool MeshTexture::RasterizeVirtualFaces( Pixel8U colEmpty, Mesh::Image8U3Arr& outTextures) { - DEBUG_EXTRA("Forward Rasterization Engine: Starting..."); + DEBUG_EXTRA("RC-style Rasterization Engine: Starting..."); TD_TIMER_START(); if (virtualFaceMap.empty() || virtualFaceViews.size() != virtualFaceMap.size()) @@ -14190,162 +14210,109 @@ bool MeshTexture::RasterizeVirtualFaces( int textureSize = ComputeOptimalTextureSize(uvWidth, uvHeight, nTextureSizeMultiple); // -------------------------------------------------- - // 2. 创建纹理与缓冲器 + // 2. 创建纹理 // -------------------------------------------------- outTextures.emplace_back(textureSize, textureSize); Image8U3& atlas = outTextures.back(); atlas.setTo(cv::Scalar(colEmpty.b, colEmpty.g, colEmpty.r)); - // ✅ 深度缓冲器初始化为正无穷(越小越近) - cv::Mat1f depthBuffer(textureSize, textureSize, FLT_MAX); - cv::Mat3f colorBuffer(textureSize, textureSize, cv::Vec3f(0.f, 0.f, 0.f)); - cv::Mat1b validBuffer(textureSize, textureSize, (uchar)0); + // ✅ 计算所有虚拟面的几何和映射矩阵 + if (!ComputeVirtualFaceGeometry(virtualFaceMap)) { + DEBUG_EXTRA("Failed to compute virtual face geometries"); + return false; + } // -------------------------------------------------- - // 3. 正向光栅化主循环 + // 3. RC 风格光栅化:按虚拟面批量处理 // -------------------------------------------------- #ifdef _USE_OPENMP #pragma omp parallel for schedule(dynamic) #endif - for (int_t idxVF = 0; idxVF < (int_t)virtualFaceMap.size(); ++idxVF) { - if (virtualFaceViews[idxVF].empty()) + for (int i = 0; i < (int)virtualFaceMap.size(); ++i) { + const VirtualFace& vf = virtualFaceMap[i]; + const VirtualFaceGeometry& geom = m_virtualFaceGeometries[i]; + + if (!geom.isValid || vf.faces.empty() || virtualFaceViews[i].empty()) continue; - const IIndex viewID = virtualFaceViews[idxVF][0]; + IIndex viewID = virtualFaceViews[i][0]; if (viewID >= (IIndex)images.size()) continue; - + const Image& srcImg = images[viewID]; if (srcImg.image.empty() || srcImg.image.cols < 2 || srcImg.image.rows < 2) continue; - - const Camera& cam = srcImg.camera; - const int srcW = srcImg.image.cols; - const int srcH = srcImg.image.rows; - for (FIndex faceID : virtualFaceMap[idxVF].faces) { - if (faceID >= (FIndex)scene.mesh.faces.size()) continue; - - const Face& face = scene.mesh.faces[faceID]; - const TexCoord* uv = &scene.mesh.faceTexcoords[faceID * 3]; - - // 验证顶点索引 - bool validIndices = true; - for (int i = 0; i < 3; ++i) { - if (face[i] >= scene.mesh.vertices.size()) { - validIndices = false; - break; - } - } - if (!validIndices) continue; - - const Point3f* verts[3] = { - &scene.mesh.vertices[face[0]], - &scene.mesh.vertices[face[1]], - &scene.mesh.vertices[face[2]] - }; + // ✅ UV 包围盒 → 纹理像素范围 + int minX = std::max(0, (int)floor(geom.uvBounds.ptMin.x() * textureSize)); + int maxX = std::min(textureSize - 1, (int)ceil(geom.uvBounds.ptMax.x() * textureSize)); + int minY = std::max(0, (int)floor(geom.uvBounds.ptMin.y() * textureSize)); + int maxY = std::min(textureSize - 1, (int)ceil(geom.uvBounds.ptMax.y() * textureSize)); - // 计算包围盒 - float minU = std::min({uv[0].x, uv[1].x, uv[2].x}); - float maxU = std::max({uv[0].x, uv[1].x, uv[2].x}); - float minV = std::min({uv[0].y, uv[1].y, uv[2].y}); - float maxV = std::max({uv[0].y, uv[1].y, uv[2].y}); - - int minX = std::max(0, (int)floor(minU * textureSize)); - int maxX = std::min(textureSize - 1, (int)ceil(maxU * textureSize)); - int minY = std::max(0, (int)floor(minV * textureSize)); - int maxY = std::min(textureSize - 1, (int)ceil(maxV * textureSize)); - - if (minX > maxX || minY > maxY) continue; + if (minX > maxX || minY > maxY) continue; - // ✅ 预计算顶点深度(用于透视校正) - float vertexDepths[3]; - for (int i = 0; i < 3; ++i) { - vertexDepths[i] = cam.PointDepth(*verts[i]); - if (vertexDepths[i] <= 0.0f) { - validIndices = false; - break; + int patchW = maxX - minX + 1; + int patchH = maxY - minY + 1; + + // ✅ 映射矩阵 + cv::Mat mapX(patchH, patchW, CV_32FC1); + cv::Mat mapY(patchH, patchW, CV_32FC1); + + // ✅ 直接展开 H 系数(无临时 Mat,RC 标准写法) + const float* H = geom.homography.ptr(); + + for (int y = minY; y <= maxY; ++y) { + for (int x = minX; x <= maxX; ++x) { + float u = (float)x / (float)textureSize; + float v = (float)y / (float)textureSize; + + float w = H[6] * u + H[7] * v + H[8]; + + // ✅ 数值保护(防止除零) + if (std::abs(w) < 1e-12f) { + mapX.at(y - minY, x - minX) = -1.0f; + mapY.at(y - minY, x - minX) = -1.0f; + continue; } - } - if (!validIndices) continue; - for (int y = minY; y <= maxY; ++y) { - for (int x = minX; x <= maxX; ++x) { - Point2f texCoord( - (x + 0.5f) / textureSize, - (y + 0.5f) / textureSize - ); + float imgX = (H[0] * u + H[1] * v + H[2]) / w; + float imgY = (H[3] * u + H[4] * v + H[5]) / w; - Point3f bary; - if (!PointInTriangle(texCoord, uv[0], uv[1], uv[2], bary)) - continue; + mapX.at(y - minY, x - minX) = imgX; + mapY.at(y - minY, x - minX) = imgY; + } + } - // ✅ 透视校正插值(double 精度) - double invZ = bary.x / vertexDepths[0] + - bary.y / vertexDepths[1] + - bary.z / vertexDepths[2]; - if (invZ <= 0.0) continue; - - // ✅ 校正后的重心坐标 - double u0 = (bary.x / vertexDepths[0]) / invZ; - double u1 = (bary.y / vertexDepths[1]) / invZ; - double u2 = (bary.z / vertexDepths[2]) / invZ; - - // ✅ 透视校正的世界坐标(double) - Point3d P_double( - verts[0]->x * u0 + verts[1]->x * u1 + verts[2]->x * u2, - verts[0]->y * u0 + verts[1]->y * u1 + verts[2]->y * u2, - verts[0]->z * u0 + verts[1]->z * u1 + verts[2]->z * u2 - ); - - // ✅ 正确的投影(double → float) - Point2d imgPtDouble = cam.ProjectPoint(P_double); - Point2f imgPt(static_cast(imgPtDouble.x), - static_cast(imgPtDouble.y)); - - // ✅ 边界检查 - if (imgPt.x < 0.5f || imgPt.y < 0.5f || - imgPt.x >= srcW - 0.5f || imgPt.y >= srcH - 0.5f) - continue; + // ✅ 一次性 remap 整个 patch + cv::Mat patch; + cv::remap(srcImg.image, patch, mapX, mapY, + cv::INTER_LINEAR, cv::BORDER_CONSTANT, + cv::Scalar(0, 0, 0)); - // ✅ 双线性采样 - Color color = BilinearSample(srcImg.image, imgPt); - if (color[0] < 0 || color[1] < 0 || color[2] < 0) continue; + // ✅ 拷贝到 atlas(OpenMP critical 区) +#pragma omp critical + { + for (int y = 0; y < patchH; ++y) { + for (int x = 0; x < patchW; ++x) { + cv::Vec3b color = patch.at(y, x); + // 跳过无效像素(BORDER_CONSTANT 产生的黑色) + if (color[0] == 0 && color[1] == 0 && color[2] == 0) + continue; - // ✅ 深度测试(float 缓冲,double 比较) - float depthFloat = static_cast(1.0 / invZ); + int atlasX = x + minX; + int atlasY = y + minY; - #pragma omp critical - { - if (depthFloat < depthBuffer(y, x)) { - depthBuffer(y, x) = depthFloat; - colorBuffer(y, x) = cv::Vec3f( - color[0], color[1], color[2] - ); - validBuffer(y, x) = 1; - } - } - } - } - } - } + // ✅ 边界保护 + if (atlasX < 0 || atlasX >= textureSize || + atlasY < 0 || atlasY >= textureSize) + continue; - // -------------------------------------------------- - // 4. 最终写入纹理 - // -------------------------------------------------- - for (int y = 0; y < textureSize; ++y) { - for (int x = 0; x < textureSize; ++x) { - if (validBuffer(y, x)) { - cv::Vec3f c = colorBuffer(y, x); - atlas(y, x) = Pixel8U{ - (unsigned char)cv::saturate_cast(c[0]), - (unsigned char)cv::saturate_cast(c[1]), - (unsigned char)cv::saturate_cast(c[2]) - }; + atlas(atlasY, atlasX) = Pixel8U{color[2], color[1], color[0]}; + } } } } - DEBUG_EXTRA("Forward Rasterization completed: %s", TD_TIMER_GET_FMT().c_str()); + DEBUG_EXTRA("RC-style Rasterization completed: %s", TD_TIMER_GET_FMT().c_str()); return true; } @@ -14639,92 +14606,76 @@ bool MeshTexture::GenerateTextureWithVirtualFaces(bool bGlobalSeamLeveling, bool } } -bool MeshTexture::SelectBestViewsForVirtualFaces(VirtualFaceMap& virtualFaceMap, - unsigned minCommonCameras, float fOutlierThreshold, - float fRatioDataSmoothness, int nIgnoreMaskLabel, - const IIndexArr& views) +// ============================================================ +// 5. SelectBestViewsForVirtualFaces(带 patch 一致性传播) +// ============================================================ +bool MeshTexture::SelectBestViewsForVirtualFaces( + VirtualFaceMap& virtualFaceMap, + unsigned minCommonCameras, float fOutlierThreshold, + float fRatioDataSmoothness, int nIgnoreMaskLabel, + const IIndexArr& views) { DEBUG_EXTRA("Selecting best views for %zu virtual faces", virtualFaceMap.size()); faceViews.resize(virtualFaceMap.size()); faceViewWeights.resize(virtualFaceMap.size()); - if (m_virtualFaceGeometries.empty() || - m_virtualFaceGeometries.size() < virtualFaceMap.size()) { - m_virtualFaceGeometries.Resize(virtualFaceMap.size()); + for (auto& v : faceViews) v.clear(); + for (auto& w : faceViewWeights) w.clear(); + + // 确保 faceNeighbors 已填充 + if (faceNeighbors.empty()) { + DEBUG_EXTRA("faceNeighbors empty, running ComputePureFaceVisibility first"); + if (!ComputePureFaceVisibility(fOutlierThreshold, nIgnoreMaskLabel, views)) { + return false; + } } - for (auto& viewList : faceViews) viewList.clear(); - for (auto& weightList : faceViewWeights) weightList.clear(); + // faceToView 映射 + std::vector faceToView(scene.mesh.faces.size(), NO_ID); - // ===== Debug 统计 ===== + // ---------- 1. 初始视图分配 ---------- size_t emptyCandidateViews = 0; - size_t emptyCommonViews = 0; size_t fallbackByCenterFace = 0; size_t successVF = 0; - // ✅ 面片 → 视图 映射表 - std::vector faceToView(scene.mesh.faces.size(), NO_ID); - - // ---------------------------------------------------------------- - // 1. 初始视图分配 - // ---------------------------------------------------------------- for (size_t i = 0; i < virtualFaceMap.size(); ++i) { const VirtualFace& vf = virtualFaceMap[i]; if (vf.faces.empty()) continue; - FIndex faceID2 = vf.faces[0]; - if (faceID2 >= faceNeighbors.size()) { - DEBUG_EXTRA("FATAL: faceID %u out of range!", faceID2); - continue; - } + FIndex faceID = vf.faces[0]; + if (faceID >= faceNeighbors.size()) continue; // 收集候选视图 std::unordered_set candidateViews; - for (FIndex faceID : vf.faces) { - if (faceID >= faceNeighbors.size()) continue; - for (IIndex viewID : faceNeighbors[faceID]) { - if (views.empty() || views.FindFirst(viewID) != NO_ID) { - candidateViews.insert(viewID); + for (FIndex fid : vf.faces) { + if (fid >= faceNeighbors.size()) continue; + for (IIndex vid : faceNeighbors[fid]) { + if (views.empty() || views.FindFirst(vid) != NO_ID) { + candidateViews.insert(vid); } } } - // ------------------------------------------------------------ - // 2. 兜底:候选视图为空 - // ------------------------------------------------------------ if (candidateViews.empty()) { ++emptyCandidateViews; - - IIndex forcedView = NO_ID; - if (!views.empty()) { - forcedView = views[0]; - } else if (!images.empty()) { - forcedView = 0; - } - + // 兜底:用第一个可用视图 + IIndex forcedView = (!views.empty()) ? views[0] : + (!images.empty()) ? 0 : NO_ID; if (forcedView != NO_ID) { faceViews[i].push_back(forcedView); faceViewWeights[i].push_back(1.0f); - for (FIndex fid : vf.faces) { if (fid < faceToView.size()) faceToView[fid] = forcedView; } - ++fallbackByCenterFace; - continue; } - - DEBUG_EXTRA("VF[%zu] truly hopeless: no images available", i); continue; } - // ------------------------------------------------------------ - // 3. 选择最佳视图(简单策略:第一个候选) - // ------------------------------------------------------------ + // 选第一个候选(简单策略,后续可优化为角度最优) IIndex bestView = *candidateViews.begin(); - faceViews[i].push_back(bestView); faceViewWeights[i].push_back(1.0f); @@ -14732,13 +14683,10 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(VirtualFaceMap& virtualFaceMap, if (fid < faceToView.size()) faceToView[fid] = bestView; } - ++successVF; } - // ---------------------------------------------------------------- - // 4. 【轻量 Patch 一致性传播】 - // ---------------------------------------------------------------- + // ---------- 2. Patch 一致性传播 ---------- if (scene.mesh.faceFaces.empty()) { scene.mesh.ListIncidenteFaceFaces(); } @@ -14777,13 +14725,10 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(VirtualFaceMap& virtualFaceMap, newFaceToView[fid] = majorityView; } } - faceToView.swap(newFaceToView); } - // ---------------------------------------------------------------- - // 5. 根据传播后的结果,更新 virtual face 的视图 - // ---------------------------------------------------------------- + // ---------- 3. 写回 ---------- for (size_t i = 0; i < virtualFaceMap.size(); ++i) { const VirtualFace& vf = virtualFaceMap[i]; if (vf.faces.empty() || faceViews[i].empty()) continue; @@ -14796,22 +14741,150 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(VirtualFaceMap& virtualFaceMap, } } - // ---------------------------------------------------------------- - // 6. Debug 汇总 - // ---------------------------------------------------------------- DEBUG_EXTRA("====== Virtual Face View Selection Summary ======"); DEBUG_EXTRA("Total virtual faces : %zu", virtualFaceMap.size()); DEBUG_EXTRA("Successfully assigned : %zu", successVF); DEBUG_EXTRA("Empty candidates : %zu", emptyCandidateViews); DEBUG_EXTRA("Fallback by center face : %zu", fallbackByCenterFace); - DEBUG_EXTRA("Empty after relaxation : %zu", emptyCommonViews); - DEBUG_EXTRA("Expected empty ratio : %.2f%%", - 100.0 * (emptyCandidateViews - fallbackByCenterFace) / virtualFaceMap.size()); DEBUG_EXTRA("================================================="); return true; } +// ============================================================ +// 1. 计算虚拟面几何(Affine for triangle, Homography for patch) +// ============================================================ +bool MeshTexture::ComputeVirtualFaceGeometry(const VirtualFaceMap& virtualFaceMap) { + // ✅ std::vector 用 resize + m_virtualFaceGeometries.resize(virtualFaceMap.size()); + +#ifdef _USE_OPENMP +#pragma omp parallel for schedule(dynamic) +#endif + for (int i = 0; i < (int)virtualFaceMap.size(); ++i) { + const VirtualFace& vf = virtualFaceMap[i]; + VirtualFaceGeometry& geom = m_virtualFaceGeometries[i]; + + if (vf.faces.empty() || faceViews[i].empty()) { + geom.isValid = false; + continue; + } + + IIndex viewID = faceViews[i][0]; + geom.isValid = ComputeHomographyForVirtualFace(vf, viewID, geom); + } + + return true; +} + +// ============================================================ +// 2. 核心:计算单应矩阵 / 仿射矩阵 +// - 3 个点 → Affine(getAffineTransform) +// - ≥ 4 个点 → Homography(findHomography) +// ============================================================ +bool MeshTexture::ComputeHomographyForVirtualFace( + const VirtualFace& vf, + IIndex viewID, + VirtualFaceGeometry& geom) +{ + if (viewID >= (IIndex)images.size()) return false; + const Camera& cam = images[viewID].camera; + + // ---------- 1. 收集顶点和 UV ---------- + std::vector points3D; + std::vector pointsUV; + points3D.reserve(vf.faces.size() * 3); + pointsUV.reserve(vf.faces.size() * 3); + + for (FIndex faceID : vf.faces) { + if (faceID >= (FIndex)scene.mesh.faces.size()) continue; + + const Face& face = scene.mesh.faces[faceID]; + const TexCoord* uv = &scene.mesh.faceTexcoords[faceID * 3]; + + for (int i = 0; i < 3; ++i) { + if (face[i] >= scene.mesh.vertices.size()) continue; + + points3D.push_back(scene.mesh.vertices[face[i]]); + pointsUV.push_back(Point2f(uv[i].x, uv[i].y)); + } + } + + if (points3D.size() < 3) return false; + + // ---------- 2. 计算中心(OpenCV 风格)---------- + geom.center = Point3f(0, 0, 0); + for (const auto& p : points3D) geom.center += p; + geom.center *= 1.0f / (float)points3D.size(); + + // ---------- 3. 计算法线(OpenCV 风格)---------- + if (!vf.faces.empty()) { + FIndex faceID = vf.faces[0]; + const Face& face = scene.mesh.faces[faceID]; + const Point3f& v0 = scene.mesh.vertices[face[0]]; + const Point3f& v1 = scene.mesh.vertices[face[1]]; + const Point3f& v2 = scene.mesh.vertices[face[2]]; + + Point3f edge1 = v1 - v0; + Point3f edge2 = v2 - v0; + Point3f normal = edge1.cross(edge2); + + float length = cv::norm(normal); + if (length > 1e-8f) { + normal *= 1.0f / length; + } else { + normal = Point3f(0, 0, 1); + } + geom.normal = normal; + } + + // ---------- 4. UV 包围盒 ---------- + geom.uvBounds.Reset(); + for (const auto& uv : pointsUV) { + geom.uvBounds.InsertFull(uv); + } + + // ---------- 5. 投影到图像空间(double → float)---------- + std::vector pointsImage; + pointsImage.reserve(points3D.size()); + + for (const auto& p3D : points3D) { + Point3d p64(p3D.x, p3D.y, p3D.z); + Point2d proj = cam.ProjectPoint(p64); + pointsImage.emplace_back((float)proj.x, (float)proj.y); + } + + // ---------- 6. 核心分支:Affine vs Homography ---------- + if (pointsUV.size() == 3) { + // ✅ 三角形:用仿射变换(Affine = 精确映射) + cv::Point2f src[3] = {pointsUV[0], pointsUV[1], pointsUV[2]}; + cv::Point2f dst[3] = {pointsImage[0], pointsImage[1], pointsImage[2]}; + + cv::Mat affine = cv::getAffineTransform(src, dst); + if (affine.empty()) return false; + + // 转成 3x3 齐次矩阵(与 Homography 统一格式) + geom.homography = cv::Mat1f(3, 3, 0.0f); + for (int r = 0; r < 2; ++r) { + for (int c = 0; c < 3; ++c) { + geom.homography(r, c) = affine.at(r, c); + } + } + geom.homography(2, 2) = 1.0f; + } + else if (pointsUV.size() >= 4) { + // ✅ Patch:用 RANSAC 单应矩阵 + geom.homography = cv::findHomography( + pointsUV, pointsImage, cv::RANSAC, 2.0); + } + else { + return false; + } + + geom.isValid = !geom.homography.empty(); + return geom.isValid; +} + bool MeshTexture::GenerateTextureWithVirtualFacesInternal(bool bGlobalSeamLeveling, bool bLocalSeamLeveling, unsigned nTextureSizeMultiple, unsigned nRectPackingHeuristic, Pixel8U colEmpty, float fSharpnessWeight, int maxTextureSize, @@ -14918,282 +14991,34 @@ bool MeshTexture::ConvertVectorToVirtualFaceDataArr(const std::vector(10)); ++i) { // 使用static_cast - const Mesh::Face& face = scene.mesh.faces[i]; - bool valid = true; - for (int j = 0; j < 3; ++j) { - if (face[j] >= numVertices) { - DEBUG_EXTRA("ERROR: Face %zu has invalid vertex index %u (max: %zu)", - i, face[j], numVertices - 1); - valid = false; - } - } - if (valid) { - DEBUG_EXTRA("Face %zu: vertices [%u, %u, %u] - OK", - i, face[0], face[1], face[2]); - } - } - - // 验证UV坐标索引 - if (numFaceTexcoords > 0) { - DEBUG_EXTRA("Validating UV coordinate indices..."); - if (numFaceTexcoords < numFaces * 3) { - DEBUG_EXTRA("ERROR: Not enough UV coordinates! Expected %zu, have %zu", - numFaces * 3, numFaceTexcoords); - return false; - } - } - - // 1. 确保网格拓扑已计算 - DEBUG_EXTRA("Computing mesh topology..."); - if (scene.mesh.faceFaces.empty()) { - DEBUG_EXTRA(" Computing incident faces..."); - try { - scene.mesh.ListIncidenteFaces(); - scene.mesh.ListIncidenteFaceFaces(); - DEBUG_EXTRA(" Done computing incident faces"); - } catch (const std::exception& e) { - DEBUG_EXTRA(" ERROR computing incident faces: %s", e.what()); - return false; - } - } - - if (scene.mesh.faceNormals.empty()) { - DEBUG_EXTRA(" Computing face normals..."); - try { - scene.mesh.ComputeNormalFaces(); - DEBUG_EXTRA(" Done computing face normals"); - } catch (const std::exception& e) { - DEBUG_EXTRA(" ERROR computing face normals: %s", e.what()); - return false; - } - } - - std::vector processedFaces(numFaces, false); - - // 3. 基于曲率分割网格 - DEBUG_EXTRA("Segmenting mesh based on curvature..."); - Mesh::FaceIdxArr regionMap; - - try { - scene.SegmentMeshBasedOnCurvature(regionMap, 0.2f); - DEBUG_EXTRA("Mesh segmentation completed, regionMap size: %zu", regionMap.size()); - - if (regionMap.size() != numFaces) { - DEBUG_EXTRA("ERROR: regionMap size (%zu) doesn't match number of faces (%u)", - regionMap.size(), numFaces); - return false; - } - } catch (const std::exception& e) { - DEBUG_EXTRA("ERROR during mesh segmentation: %s", e.what()); - return false; - } - - // 4. 统计每个区域的面积 - DEBUG_EXTRA("Calculating region areas..."); - std::unordered_map regionAreas; - - for (FIndex fid = 0; fid < numFaces; ++fid) { - if (fid % 10000 == 0 && fid > 0) { - DEBUG_EXTRA(" Processed %u/%u faces", fid, numFaces); - } - - int region = regionMap[fid]; - - // 验证面片索引 - if (fid >= scene.mesh.faces.size()) { - DEBUG_EXTRA("ERROR: Face index %u out of bounds (mesh has %zu faces)", - fid, scene.mesh.faces.size()); - continue; - } - - const Mesh::Face& face = scene.mesh.faces[fid]; - - // 验证顶点索引 - for (int i = 0; i < 3; ++i) { - if (face[i] >= scene.mesh.vertices.size()) { - DEBUG_EXTRA("ERROR: Vertex index %u out of bounds in face %u (mesh has %zu vertices)", - face[i], fid, scene.mesh.vertices.size()); - continue; - } - } - - const Point3f& v0 = scene.mesh.vertices[face[0]]; - const Point3f& v1 = scene.mesh.vertices[face[1]]; - const Point3f& v2 = scene.mesh.vertices[face[2]]; - - // 计算三角形面积 - Point3f edge1 = v1 - v0; - Point3f edge2 = v2 - v0; - - Point3f crossProd( - edge1.y * edge2.z - edge1.z * edge2.y, - edge1.z * edge2.x - edge1.x * edge2.z, - edge1.x * edge2.y - edge1.y * edge2.x - ); - - float area = 0.5f * std::sqrt( - crossProd.x * crossProd.x + - crossProd.y * crossProd.y + - crossProd.z * crossProd.z - ); - - if (area < 0) { - DEBUG_EXTRA("WARNING: Negative area calculated for face %u: %f", fid, area); - area = 0.0f; - } - - regionAreas[region] += area; - } - - DEBUG_EXTRA("Region area calculation completed, found %zu regions", regionAreas.size()); - - // 5. 为每个区域收集面片 - DEBUG_EXTRA("Grouping faces by region..."); - std::unordered_map> regionFaces; - for (FIndex fid = 0; fid < numFaces; ++fid) { - int region = regionMap[fid]; - regionFaces[region].push_back(fid); - } - - DEBUG_EXTRA("Found %zu regions with faces", regionFaces.size()); - - // 6. 创建虚拟面 - DEBUG_EXTRA("Creating virtual faces..."); - virtualFaceMap.clear(); - virtualFaceMap.reserve(regionFaces.size()); - - int regionCount = 0; - int smallRegionCount = 0; - int uvDiscontinuousCount = 0; - int createdVirtualFaces = 0; - - for (const auto& region : regionFaces) { - regionCount++; - int regionID = region.first; - const std::vector& faceList = region.second; - - if (regionCount % 100 == 0) { - DEBUG_EXTRA(" Processing region %d/%d (%zu faces)", regionCount, regionFaces.size(), faceList.size()); - } - - // 检查区域面积是否足够大 - float regionArea = regionAreas[regionID]; - if (regionArea < 0.001f) { // 面积阈值 - DEBUG_EXTRA(" Region %d is too small (area: %f), using individual faces", regionID, regionArea); - smallRegionCount++; - - // 区域太小,不创建虚拟面 - for (FIndex fid : faceList) { - virtualFaceMap.push_back(VirtualFace()); - virtualFaceMap.back().faces.push_back(fid); - } - continue; - } - - // 检查UV连续性 - if (!CheckUVContinuity(faceList)) { - DEBUG_EXTRA(" Region %d has discontinuous UV, using individual faces", regionID); - uvDiscontinuousCount++; - - // UV不连续,保持原始面片 - for (FIndex fid : faceList) { - virtualFaceMap.push_back(VirtualFace()); - virtualFaceMap.back().faces.push_back(fid); - } - continue; - } - - // 创建虚拟面 - VirtualFace vf; - vf.faces = faceList; - - // 计算虚拟面的中心、法线和面积 - if (!CalculateVirtualFaceProperties(vf)) { - DEBUG_EXTRA(" Failed to calculate properties for virtual face in region %d", regionID); - // 计算失败,回退到原始面片 - for (FIndex fid : faceList) { - virtualFaceMap.push_back(VirtualFace()); - virtualFaceMap.back().faces.push_back(fid); - } - continue; - } - - // 计算虚拟面的UV边界 - if (!CalculateVirtualFaceUVBounds(vf)) { - DEBUG_EXTRA(" Failed to calculate UV bounds for virtual face in region %d", regionID); - // 计算失败,回退到原始面片 - for (FIndex fid : faceList) { - virtualFaceMap.push_back(VirtualFace()); - virtualFaceMap.back().faces.push_back(fid); - } - continue; - } - - virtualFaceMap.push_back(vf); - createdVirtualFaces++; - } - - DEBUG_EXTRA("Virtual face creation completed:"); - DEBUG_EXTRA(" Total regions: %zu", regionFaces.size()); - DEBUG_EXTRA(" Small regions (area < 0.001): %d", smallRegionCount); - DEBUG_EXTRA(" UV discontinuous regions: %d", uvDiscontinuousCount); - DEBUG_EXTRA(" Created virtual faces: %d", createdVirtualFaces); - DEBUG_EXTRA(" Total virtual faces (including individual faces): %zu", virtualFaceMap.size()); - - if (virtualFaceMap.empty()) { - DEBUG_EXTRA("ERROR: No virtual faces created!"); - return false; - } - + DEBUG_EXTRA("Created %zu virtual faces (one per triangle)", virtualFaceMap.size()); return true; } @@ -18813,49 +18638,48 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi return false; } - // ✅✅✅ 关键修复:重新计算拓扑(否则 faceFaces 是坏的) - mesh.ListIncidenteFaces(); - mesh.ListIncidenteFaceFaces(); - mesh.ComputeNormalFaces(); - mesh.ListBoundaryVertices(); - - // 纯可见性计算(初始化faceNeighbors) - if (!texture.ComputePureFaceVisibility(fOutlierThreshold, nIgnoreMaskLabel, views)) { - // ❗ 兜底:如果ComputePureFaceVisibility没初始化faceNeighbors,手动填充 - if (texture.faceNeighbors.empty()) { - texture.faceNeighbors.resize(mesh.faces.size()); - for (FIndex fid = 0; fid < (FIndex)mesh.faces.size(); ++fid) { - if (!views.empty()) { - texture.faceNeighbors[fid].insert(texture.faceNeighbors[fid].end(), views.begin(), views.end()); - } else { - for (IIndex vid = 0; vid < (IIndex)images.size(); ++vid) - texture.faceNeighbors[fid].push_back(vid); - } - } - DEBUG_EXTRA("Forced fill faceNeighbors for %zu faces", mesh.faces.size()); - } - return false; - } + // ✅ 确保拓扑信息存在 + if (mesh.faceFaces.empty()) { + mesh.ListIncidenteFaces(); + mesh.ListIncidenteFaceFaces(); + } + if (mesh.faceNormals.empty()) { + mesh.ComputeNormalFaces(); + } + if (mesh.vertexBoundary.empty()) { + mesh.ListBoundaryVertices(); + } - // 创建虚拟面(1:1映射) - MeshTexture::VirtualFaceMap virtualFaceMap; - if (!texture.CreateVirtualFacesForExistingUV(virtualFaceMap)) return false; + // ✅ 1. 创建虚拟面(每三角形一个) + MeshTexture::VirtualFaceMap virtualFaceMap; + if (!texture.CreateVirtualFacesForExistingUV(virtualFaceMap)) + return false; - // 选最佳视图(带patch一致性) - if (!texture.SelectBestViewsForVirtualFaces( - virtualFaceMap, 1, fOutlierThreshold, fRatioDataSmoothness, nIgnoreMaskLabel, views)) - return false; + // ✅ 2. 计算可见性(填充 faceNeighbors) + if (!texture.ComputePureFaceVisibility( + fOutlierThreshold, nIgnoreMaskLabel, views)) { + return false; + } - // 直接光栅化(不需要生成图集) - Mesh::Image8U3Arr textures; - if (!texture.RasterizeVirtualFaces( - virtualFaceMap, texture.faceViews, texture.faceViewWeights, - nTextureSizeMultiple, colEmpty, textures)) - return false; + // ✅ 3. 选择最佳视图(带 patch 一致性) + if (!texture.SelectBestViewsForVirtualFaces( + virtualFaceMap, 1, fOutlierThreshold, + fRatioDataSmoothness, nIgnoreMaskLabel, views)) + return false; + + // ✅ 4. RC 风格光栅化(Affine + Homography 混合) + Mesh::Image8U3Arr textures; + if (!texture.RasterizeVirtualFaces( + virtualFaceMap, texture.faceViews, + texture.faceViewWeights, + nTextureSizeMultiple, colEmpty, textures)) + return false; + + mesh.texturesDiffuse = std::move(textures); + DEBUG_EXTRA("Existing UV texturing completed: %u faces (%s)", + mesh.faces.size(), TD_TIMER_GET_FMT().c_str()); + return true; - mesh.texturesDiffuse = std::move(textures); - DEBUG_EXTRA("Existing UV texturing completed: %u faces (%s)", mesh.faces.size(), TD_TIMER_GET_FMT().c_str()); - return true; // 直接返回,不走后面的图集逻辑 } else { // 3. 通用虚拟面模式(无预计算UV) if (!texture.FaceViewSelectionWithVirtualFaces(