清晰化
This commit is contained in:
1 parent
e8e3632341
commit
af1c3c0510
1 file changed
+128
-116
+128
-116
@@ -14606,10 +14606,8 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(VirtualFaceMap& virtualFaceMap,
|
||||
m_virtualFaceGeometries.Resize(virtualFaceMap.size());
|
||||
}
|
||||
|
||||
for (auto& viewList : faceViews)
|
||||
viewList.clear();
|
||||
for (auto& weightList : faceViewWeights)
|
||||
weightList.clear();
|
||||
for (auto& viewList : faceViews) viewList.clear();
|
||||
for (auto& weightList : faceViewWeights) weightList.clear();
|
||||
|
||||
// ===== Debug 统计 =====
|
||||
size_t emptyCandidateViews = 0;
|
||||
@@ -14617,128 +14615,142 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(VirtualFaceMap& virtualFaceMap,
|
||||
size_t fallbackByCenterFace = 0;
|
||||
size_t successVF = 0;
|
||||
|
||||
// ✅ 面片 → 视图 映射表
|
||||
std::vector<IIndex> faceToView(scene.mesh.faces.size(), NO_ID);
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// 1. 初始视图分配
|
||||
// ----------------------------------------------------------------
|
||||
for (size_t i = 0; i < virtualFaceMap.size(); ++i) {
|
||||
const VirtualFace& vf = virtualFaceMap[i];
|
||||
FIndex faceID2 = vf.faces[0];
|
||||
if (vf.faces.empty()) continue;
|
||||
|
||||
if (faceID2 >= faceNeighbors.size()) {
|
||||
DEBUG_EXTRA("FATAL: faceID %u out of range!", faceID2);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (faceNeighbors[faceID2].empty()) {
|
||||
// DEBUG_EXTRA("FATAL: faceNeighbors[%u] is empty! FaceViewSelection mode error.", faceID2);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// 1. 收集每个面片的候选视图
|
||||
//------------------------------------------------------------------
|
||||
std::unordered_set<IIndex> candidateViews;
|
||||
std::vector<std::vector<IIndex>> faceViewCandidates(vf.faces.size());
|
||||
|
||||
for (size_t j = 0; j < vf.faces.size(); ++j) {
|
||||
FIndex faceID = vf.faces[j];
|
||||
if (faceID >= faceNeighbors.size())
|
||||
continue;
|
||||
|
||||
for (IIndex viewID : faceNeighbors[faceID]) {
|
||||
if (views.empty() || views.FindFirst(viewID) != NO_ID) {
|
||||
candidateViews.insert(viewID);
|
||||
faceViewCandidates[j].push_back(viewID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// 2. 【终极兜底】候选视图为空 → 强制分配一个视图
|
||||
// 物理遮挡不重要,逻辑可见性最重要
|
||||
//------------------------------------------------------------------
|
||||
if (candidateViews.empty()) {
|
||||
++emptyCandidateViews;
|
||||
|
||||
// 找一个可用的相机ID
|
||||
// 优先级:views参数指定的 > 所有images中的第一个
|
||||
IIndex forcedView = NO_ID;
|
||||
|
||||
if (!views.empty()) {
|
||||
forcedView = views[0];
|
||||
} else if (!images.empty()) {
|
||||
forcedView = 0;
|
||||
}
|
||||
|
||||
if (forcedView != NO_ID) {
|
||||
faceViews[i].push_back(forcedView);
|
||||
faceViewWeights[i].push_back(1.0f);
|
||||
++fallbackByCenterFace;
|
||||
|
||||
// 降低日志级别,避免刷屏,用 DEBUG 而非 EXTRA
|
||||
// DEBUG("VF[%zu] EMPTY -> FORCED view %d (ignoring physical occlusion)",
|
||||
// i, forcedView);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 实在没救(连images都没有),这才跳过
|
||||
DEBUG_EXTRA("VF[%zu] truly hopeless: no images available", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// 3. 【关键放松】不再强求“所有面片共视”
|
||||
// 只要视图能看到任意一个面片即可
|
||||
//------------------------------------------------------------------
|
||||
std::vector<IIndex> usableViews(
|
||||
candidateViews.begin(), candidateViews.end()
|
||||
);
|
||||
|
||||
// (可选)按质量排序,取前 N 个
|
||||
// std::sort(usableViews.begin(), usableViews.end(),
|
||||
// [&](IIndex a, IIndex b) { return Quality(a) > Quality(b); });
|
||||
|
||||
if (usableViews.size() < minCommonCameras) {
|
||||
faceViews[i] = usableViews;
|
||||
} else {
|
||||
// 仍尊重 minCommonCameras,但不强求共视
|
||||
faceViews[i].assign(
|
||||
usableViews.begin(),
|
||||
usableViews.begin() + minCommonCameras
|
||||
);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// 4. 【二次兜底】usableViews 为空(理论上不会发生)
|
||||
//------------------------------------------------------------------
|
||||
if (faceViews[i].empty()) {
|
||||
++emptyCommonViews;
|
||||
|
||||
if (!vf.faces.empty()) {
|
||||
FIndex centerFace = vf.faces[0];
|
||||
if (centerFace < faceNeighbors.size() &&
|
||||
!faceNeighbors[centerFace].empty()) {
|
||||
|
||||
IIndex bestView = faceNeighbors[centerFace][0];
|
||||
faceViews[i].push_back(bestView);
|
||||
faceViewWeights[i].push_back(1.0f);
|
||||
|
||||
DEBUG_EXTRA("VF[%zu] empty usableViews -> force view %d",
|
||||
i, bestView);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
FIndex faceID2 = vf.faces[0];
|
||||
if (faceID2 >= faceNeighbors.size()) {
|
||||
DEBUG_EXTRA("FATAL: faceID %u out of range!", faceID2);
|
||||
continue;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// 5. 权重分配(均等,最稳定)
|
||||
//------------------------------------------------------------------
|
||||
float weight = 1.0f / static_cast<float>(faceViews[i].size());
|
||||
faceViewWeights[i].resize(faceViews[i].size(), weight);
|
||||
// 收集候选视图
|
||||
std::unordered_set<IIndex> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// 2. 兜底:候选视图为空
|
||||
// ------------------------------------------------------------
|
||||
if (candidateViews.empty()) {
|
||||
++emptyCandidateViews;
|
||||
|
||||
IIndex forcedView = NO_ID;
|
||||
if (!views.empty()) {
|
||||
forcedView = views[0];
|
||||
} else if (!images.empty()) {
|
||||
forcedView = 0;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
for (FIndex fid : vf.faces) {
|
||||
if (fid < faceToView.size())
|
||||
faceToView[fid] = bestView;
|
||||
}
|
||||
|
||||
++successVF;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// 6. Debug 汇总(非常重要)
|
||||
//--------------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------
|
||||
// 4. 【轻量 Patch 一致性传播】
|
||||
// ----------------------------------------------------------------
|
||||
if (scene.mesh.faceFaces.empty()) {
|
||||
scene.mesh.ListIncidenteFaceFaces();
|
||||
}
|
||||
|
||||
const int PROPAGATION_ITER = 2;
|
||||
for (int iter = 0; iter < PROPAGATION_ITER; ++iter) {
|
||||
std::vector<IIndex> newFaceToView = faceToView;
|
||||
|
||||
for (FIndex fid = 0; fid < (FIndex)faceToView.size(); ++fid) {
|
||||
if (faceToView[fid] == NO_ID) continue;
|
||||
if (fid >= (FIndex)scene.mesh.faceFaces.size()) continue;
|
||||
|
||||
const Mesh::Face& neighbors = scene.mesh.faceFaces[fid];
|
||||
|
||||
std::unordered_map<IIndex, int> vote;
|
||||
vote[faceToView[fid]] = 1;
|
||||
|
||||
for (int k = 0; k < 3; ++k) {
|
||||
FIndex nb = neighbors[k];
|
||||
if (nb == NO_ID) continue;
|
||||
if (nb < (FIndex)faceToView.size() && faceToView[nb] != NO_ID) {
|
||||
vote[faceToView[nb]]++;
|
||||
}
|
||||
}
|
||||
|
||||
IIndex majorityView = faceToView[fid];
|
||||
int maxVote = 0;
|
||||
for (const auto& v : vote) {
|
||||
if (v.second > maxVote) {
|
||||
maxVote = v.second;
|
||||
majorityView = v.first;
|
||||
}
|
||||
}
|
||||
|
||||
if (maxVote >= 3 && majorityView != faceToView[fid]) {
|
||||
newFaceToView[fid] = majorityView;
|
||||
}
|
||||
}
|
||||
|
||||
faceToView.swap(newFaceToView);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// 5. 根据传播后的结果,更新 virtual face 的视图
|
||||
// ----------------------------------------------------------------
|
||||
for (size_t i = 0; i < virtualFaceMap.size(); ++i) {
|
||||
const VirtualFace& vf = virtualFaceMap[i];
|
||||
if (vf.faces.empty() || faceViews[i].empty()) continue;
|
||||
|
||||
FIndex firstFace = vf.faces[0];
|
||||
if (firstFace < faceToView.size() && faceToView[firstFace] != NO_ID) {
|
||||
IIndex consistentView = faceToView[firstFace];
|
||||
faceViews[i] = {consistentView};
|
||||
faceViewWeights[i] = {1.0f};
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// 6. Debug 汇总
|
||||
// ----------------------------------------------------------------
|
||||
DEBUG_EXTRA("====== Virtual Face View Selection Summary ======");
|
||||
DEBUG_EXTRA("Total virtual faces : %zu", virtualFaceMap.size());
|
||||
DEBUG_EXTRA("Successfully assigned : %zu", successVF);
|
||||
|
||||
Reference in new issue
Block a user