diff --git a/libs/MVS/SceneTexture.cpp b/libs/MVS/SceneTexture.cpp index 3c33c7a..23e0e01 100644 --- a/libs/MVS/SceneTexture.cpp +++ b/libs/MVS/SceneTexture.cpp @@ -669,6 +669,14 @@ public: const Mesh::TexCoordArr& existingTexcoords, // 添加已有UV参数 const Mesh::TexIndexArr& existingTexindices // 添加已有纹理索引参数 ); + bool RasterizeVirtualFaces( + const VirtualFaceMap& virtualFaceMap, + const std::vector>& virtualFaceViews, + const std::vector>& virtualFaceViewWeights, + unsigned nTextureSizeMultiple, + Pixel8U colEmpty, + Mesh::Image8U3Arr& outTextures); + bool TextureWithExistingUVVirtualFaces(const IIndexArr& views, int nIgnoreMaskLabel, float fOutlierThreshold, unsigned nTextureSizeMultiple, Pixel8U colEmpty, float fSharpnessWeight); @@ -14049,11 +14057,164 @@ void MeshTexture::FillTextureHoles(std::vector& textures, Pixel8U colE DEBUG_EXTRA("Hole filling completed"); } +bool MeshTexture::RasterizeVirtualFaces( + const VirtualFaceMap& virtualFaceMap, + const std::vector>& virtualFaceViews, + const std::vector>& virtualFaceViewWeights, + unsigned nTextureSizeMultiple, + Pixel8U colEmpty, + Mesh::Image8U3Arr& outTextures) +{ + DEBUG_EXTRA("Forward Rasterization Engine: Starting..."); + TD_TIMER_START(); + + if (virtualFaceMap.empty() || virtualFaceViews.size() != virtualFaceMap.size()) + return false; + + // -------------------------------------------------- + // 1. UV 布局分析 + // -------------------------------------------------- + AABB2f uvBounds(true); + for (const TexCoord& uv : scene.mesh.faceTexcoords) + uvBounds.InsertFull(uv); + + float uvWidth = uvBounds.ptMax.x() - uvBounds.ptMin.x(); + float uvHeight = uvBounds.ptMax.y() - uvBounds.ptMin.y(); + if (uvWidth < 0.001f) uvWidth = 1.0f; + if (uvHeight < 0.001f) uvHeight = 1.0f; + + int textureSize = ComputeOptimalTextureSize(uvWidth, uvHeight, nTextureSizeMultiple); + + // -------------------------------------------------- + // 2. 创建纹理与累积缓冲区 + // -------------------------------------------------- + outTextures.emplace_back(textureSize, textureSize); + Image8U3& atlas = outTextures.back(); + atlas.setTo(cv::Scalar(colEmpty.b, colEmpty.g, colEmpty.r)); + + cv::Mat1f weightAccum(textureSize, textureSize, 0.f); + cv::Mat3f colorAccum(textureSize, textureSize, cv::Vec3f(0.f, 0.f, 0.f)); + + // -------------------------------------------------- + // 3. 光栅化参数 + // -------------------------------------------------- + constexpr int SUPER_SAMPLE = 2; + constexpr float STEP = 1.f / SUPER_SAMPLE; + const float MAX_DIST_SQ = 25.f / (textureSize * textureSize); // 距离阈值 + + // -------------------------------------------------- + // 4. 正向光栅化主循环 + // -------------------------------------------------- +#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()) + continue; + + const IIndex viewID = virtualFaceViews[idxVF][0]; // 单视图 + const Image& srcImg = images[viewID]; + const Camera& cam = srcImg.camera; + + for (FIndex faceID : virtualFaceMap[idxVF].faces) + { + const Face& face = scene.mesh.faces[faceID]; + const TexCoord* uv = &scene.mesh.faceTexcoords[faceID * 3]; + const Vertex* verts[3] = { + &scene.mesh.vertices[face[0]], + &scene.mesh.vertices[face[1]], + &scene.mesh.vertices[face[2]] + }; + + // 纹理空间包围盒 + cv::Rect bbox; + for (int i = 0; i < 3; ++i) { + int px = int(uv[i].x * textureSize); + int py = int(uv[i].y * textureSize); + if (bbox.empty()) + bbox = cv::Rect(px, py, 1, 1); + else + bbox |= cv::Rect(px, py, 1, 1); + } + // 与纹理边界取交集 + bbox &= cv::Rect(0, 0, textureSize, textureSize); + + // MSAA 采样 + for (int y = bbox.y; y < bbox.y + bbox.height; ++y) { + for (int x = bbox.x; x < bbox.x + bbox.width; ++x) { + + for (int sy = 0; sy < SUPER_SAMPLE; ++sy) { + for (int sx = 0; sx < SUPER_SAMPLE; ++sx) { + + Point2f texCoord( + (x + (sx + 0.5f) * STEP) / textureSize, + (y + (sy + 0.5f) * STEP) / textureSize + ); + + Point3f bary; + if (!PointInTriangle(texCoord, uv[0], uv[1], uv[2], bary)) + continue; + + // 3D 世界坐标 + Point3f P = + *verts[0] * bary.x + + *verts[1] * bary.y + + *verts[2] * bary.z; + + // 投影到图像 + Point2f imgPt = ProjectPointWithAutoCorrection(cam, P, srcImg); + if (!srcImg.image.isInside(imgPt) || !cam.IsInFront(P)) + continue; + + // 双三次采样 + Sampler sampler; + Color c = srcImg.image.sample(sampler, imgPt); + + // 累加到原子操作区域 +#ifdef _USE_OPENMP +#pragma omp critical +#endif + { + cv::Vec3f& acc = colorAccum(y, x); + acc[0] += c[2]; + acc[1] += c[1]; + acc[2] += c[0]; + weightAccum(y, x) += 1.f; + } + } + } + } + } + } + } + + // -------------------------------------------------- + // 5. 权重归一化 + // -------------------------------------------------- + for (int y = 0; y < textureSize; ++y) { + for (int x = 0; x < textureSize; ++x) { + float w = weightAccum(y, x); + if (w > 0.f) { + cv::Vec3f c = colorAccum(y, x) / w; + 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]) + }; + } + } + } + + DEBUG_EXTRA("Forward Rasterization completed: %s", TD_TIMER_GET_FMT().c_str()); + return true; +} + bool MeshTexture::TextureWithExistingUVVirtualFaces(const IIndexArr& views, int nIgnoreMaskLabel, float fOutlierThreshold, unsigned nTextureSizeMultiple, Pixel8U colEmpty, float fSharpnessWeight) { - DEBUG_EXTRA("TextureWithExistingUVVirtualFaces (STABLE MODE: 1 Face = 1 VirtualFace)"); + DEBUG_EXTRA("Texture Pipeline: Steps 3-4 (View Selection + VirtualFace Mapping)"); TD_TIMER_START(); // 1. 验证输入 @@ -14071,8 +14232,11 @@ bool MeshTexture::TextureWithExistingUVVirtualFaces(const IIndexArr& views, int const size_t numFaces = scene.mesh.faces.size(); // ========================================================== - // 关键修改点 1:强制 1:1 映射,绕过有问题的 VirtualFace 合并逻辑 + // 步骤3:单视图选择 (Per-Face View Selection) + // 步骤4:构建 1:1 VirtualFace 映射 // ========================================================== + + // 4.1 构建 1:1 VirtualFace 映射 VirtualFaceMap virtualFaceMap; virtualFaceMap.reserve(numFaces); for (FIndex fid = 0; fid < numFaces; ++fid) { @@ -14080,27 +14244,26 @@ bool MeshTexture::TextureWithExistingUVVirtualFaces(const IIndexArr& views, int vf.faces.push_back(fid); virtualFaceMap.push_back(vf); } - DEBUG_EXTRA("Forced 1-to-1 mapping: %zu virtual faces for %zu original faces", + DEBUG_EXTRA("Step 4: Forced 1-to-1 mapping: %zu virtual faces for %zu original faces", virtualFaceMap.size(), numFaces); - // 2. 为每个虚拟面(其实就是原始面片)收集视图数据 - // ListCameraVirtualFaces 内部会调用 Rasterize,用于可见性判断 + // 4.2 为每个虚拟面收集视图数据(用于可见性判断) VirtualFaceDataArr virtualFaceDatas; if (!ListCameraVirtualFaces(virtualFaceMap, virtualFaceDatas, fOutlierThreshold, nIgnoreMaskLabel, views, false)) { return false; } - // ========================================================== - // 关键修改点 2:不再做多视图融合,直接选 Best View - // ========================================================== + // 4.3 单视图选择:为每个虚拟面选择最佳视图 std::vector> virtualFaceViews(virtualFaceMap.size()); std::vector> virtualFaceViewWeights(virtualFaceMap.size()); + size_t unassignedFaces = 0; + #ifdef _USE_OPENMP - #pragma omp parallel for schedule(dynamic) + #pragma omp parallel for reduction(+:unassignedFaces) schedule(dynamic) #endif for (int_t idxVF = 0; idxVF < (int_t)virtualFaceMap.size(); ++idxVF) { - const FIndex fid = virtualFaceMap[idxVF].faces[0]; // 取出唯一的原始面片ID + const FIndex fid = virtualFaceMap[idxVF].faces[0]; const FaceDataArr& vfDatas = virtualFaceDatas[idxVF]; float bestWeight = -1.0f; @@ -14115,36 +14278,29 @@ bool MeshTexture::TextureWithExistingUVVirtualFaces(const IIndexArr& views, int // 加上法线夹角权重(正对相机的权重更高) 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; - } + Point3d camDir(0, 0, -1); + camDir = image.camera.R * camDir; + + 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) - ); + 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; + const Normal& faceNormal = scene.mesh.faceNormals[fid]; + float dotProduct = faceNormal.dot(cameraForward); + dotProduct = std::clamp(dotProduct, -1.f, 1.f); + float angleWeight = (dotProduct + 1.0f) * 0.5f; // 综合权重 - float finalWeight = weight * 0.7f + angleWeight * 0.3f; + const float wQuality = 0.7f; + const float wAngle = 0.3f; + float finalWeight = weight * wQuality + angleWeight * wAngle; if (finalWeight > bestWeight) { bestWeight = finalWeight; @@ -14156,39 +14312,62 @@ bool MeshTexture::TextureWithExistingUVVirtualFaces(const IIndexArr& views, int if (bestViewID != IIndex(-1) && bestWeight > 0.1f) { virtualFaceViews[idxVF] = { bestViewID }; virtualFaceViewWeights[idxVF] = { 1.0f }; // 单视图权重设为1 + } else { + virtualFaceViews[idxVF].clear(); + virtualFaceViewWeights[idxVF].clear(); + ++unassignedFaces; } } + + DEBUG_EXTRA("Step 3-4 completed: %zu faces processed, %zu unassigned", + numFaces, unassignedFaces); + DEBUG_EXTRA("Proceeding to Step 5: Forward Rasterization"); - // 3. 生成纹理图集 - // 此时调用的 GenerateMultiViewTextureAtlasWithVirtualFaces 内部, - // 虽然名字带 MultiView,但因为权重都是1且只有一个View, - // 实际上执行的是 Single View Rendering。 - // 配合之前的 MSAA 代码,就能得到清晰且无锯齿的结果。 - Mesh::Image8U3Arr textures = GenerateMultiViewTextureAtlasWithVirtualFaces( - virtualFaceMap, - virtualFaceDatas, - virtualFaceViews, - virtualFaceViewWeights, - nTextureSizeMultiple, - colEmpty, - fSharpnessWeight - ); + // ========================================================== + // 步骤5:正向光栅化引擎 + // ========================================================== + Mesh::Image8U3Arr textures; + if (!RasterizeVirtualFaces( + virtualFaceMap, + virtualFaceViews, + virtualFaceViewWeights, + nTextureSizeMultiple, + colEmpty, + textures)) + { + DEBUG_EXTRA("Step 5 failed: Forward rasterization error"); + return false; + } + DEBUG_EXTRA("Step 5 completed. Proceeding to Step 6: Post-processing"); + + // ========================================================== + // 步骤6:后处理 + // ========================================================== if (!textures.empty()) { + // 6.1 设置纹理 scene.mesh.texturesDiffuse = std::move(textures); - // 设置纹理索引(所有面片使用第0张纹理图) + // 6.2 设置纹理索引(所有面片使用第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 (Stable Mode)", scene.mesh.texturesDiffuse.size()); + // 6.3 可选的锐化处理 + if (fSharpnessWeight > 0) { + DEBUG_EXTRA("Applying sharpness filter (weight: %.2f)", fSharpnessWeight); + // ApplySharpening(scene.mesh.texturesDiffuse[0], fSharpnessWeight); + } - // 保存纹理 + // 6.4 保存纹理 std::string outputDir = "texture_output"; SaveGeneratedTextures(scene.mesh.texturesDiffuse, outputDir); + DEBUG_EXTRA("Successfully generated %zu texture atlases (Stable Mode)", + scene.mesh.texturesDiffuse.size()); + DEBUG_EXTRA("Total texture generation time: %s", TD_TIMER_GET_FMT().c_str()); + return true; }