|
|
|
@ -14073,13 +14073,13 @@ bool MeshTexture::RasterizeVirtualFaces( |
|
|
|
DEBUG_EXTRA("Forward Rasterization Engine: Starting..."); |
|
|
|
DEBUG_EXTRA("Forward Rasterization Engine: Starting..."); |
|
|
|
TD_TIMER_START(); |
|
|
|
TD_TIMER_START(); |
|
|
|
|
|
|
|
|
|
|
|
int totalVF = virtualFaceMap.size(); |
|
|
|
int totalVF = virtualFaceMap.size(); |
|
|
|
int emptyVF = 0; |
|
|
|
int emptyVF = 0; |
|
|
|
for (auto& vf : virtualFaceViews) |
|
|
|
for (auto& vf : virtualFaceViews) |
|
|
|
if (vf.empty()) ++emptyVF; |
|
|
|
if (vf.empty()) ++emptyVF; |
|
|
|
|
|
|
|
|
|
|
|
DEBUG_EXTRA("VirtualFaces: total=%d empty=%d (%.1f%%)", |
|
|
|
DEBUG_EXTRA("VirtualFaces: total=%d empty=%d (%.1f%%)", |
|
|
|
totalVF, emptyVF, 100.0f*emptyVF/totalVF); |
|
|
|
totalVF, emptyVF, 100.0f*emptyVF/totalVF); |
|
|
|
|
|
|
|
|
|
|
|
if (virtualFaceMap.empty() || virtualFaceViews.size() != virtualFaceMap.size()) |
|
|
|
if (virtualFaceMap.empty() || virtualFaceViews.size() != virtualFaceMap.size()) |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
@ -14099,21 +14099,25 @@ bool MeshTexture::RasterizeVirtualFaces( |
|
|
|
int textureSize = ComputeOptimalTextureSize(uvWidth, uvHeight, nTextureSizeMultiple); |
|
|
|
int textureSize = ComputeOptimalTextureSize(uvWidth, uvHeight, nTextureSizeMultiple); |
|
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------
|
|
|
|
// --------------------------------------------------
|
|
|
|
// 2. 创建纹理与累积缓冲区
|
|
|
|
// 2. 创建纹理与缓冲器
|
|
|
|
// --------------------------------------------------
|
|
|
|
// --------------------------------------------------
|
|
|
|
outTextures.emplace_back(textureSize, textureSize); |
|
|
|
outTextures.emplace_back(textureSize, textureSize); |
|
|
|
Image8U3& atlas = outTextures.back(); |
|
|
|
Image8U3& atlas = outTextures.back(); |
|
|
|
atlas.setTo(cv::Scalar(colEmpty.b, colEmpty.g, colEmpty.r)); |
|
|
|
atlas.setTo(cv::Scalar(colEmpty.b, colEmpty.g, colEmpty.r)); |
|
|
|
|
|
|
|
|
|
|
|
cv::Mat1f weightAccum(textureSize, textureSize, 0.f); |
|
|
|
// ✅ 深度缓冲器和面片ID缓冲器
|
|
|
|
cv::Mat3f colorAccum(textureSize, textureSize, cv::Vec3f(0.f, 0.f, 0.f)); |
|
|
|
cv::Mat1f depthBuffer(textureSize, textureSize, -FLT_MAX); |
|
|
|
|
|
|
|
cv::Mat1i faceIDBuffer(textureSize, textureSize, -1); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ✅ 颜色缓冲器
|
|
|
|
|
|
|
|
cv::Mat3f colorBuffer(textureSize, textureSize, cv::Vec3f(0.f, 0.f, 0.f)); |
|
|
|
|
|
|
|
cv::Mat1b validBuffer(textureSize, textureSize, (uchar)0); |
|
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------
|
|
|
|
// --------------------------------------------------
|
|
|
|
// 3. 光栅化参数
|
|
|
|
// 3. 光栅化参数 - 使用单采样保证清晰度
|
|
|
|
// --------------------------------------------------
|
|
|
|
// --------------------------------------------------
|
|
|
|
constexpr int SUPER_SAMPLE = 2; |
|
|
|
constexpr int SUPER_SAMPLE = 1; // ✅ 关键:禁用MSAA,使用单采样
|
|
|
|
constexpr float STEP = 1.f / SUPER_SAMPLE; |
|
|
|
constexpr float STEP = 1.f / SUPER_SAMPLE; |
|
|
|
const float MAX_DIST_SQ = 25.f / (textureSize * textureSize); // 距离阈值
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------
|
|
|
|
// --------------------------------------------------
|
|
|
|
// 4. 正向光栅化主循环
|
|
|
|
// 4. 正向光栅化主循环
|
|
|
|
@ -14140,83 +14144,150 @@ bool MeshTexture::RasterizeVirtualFaces( |
|
|
|
&scene.mesh.vertices[face[2]] |
|
|
|
&scene.mesh.vertices[face[2]] |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// 纹理空间包围盒
|
|
|
|
// ✅ 关键修复1:扩大包围盒,确保覆盖所有像素
|
|
|
|
cv::Rect bbox; |
|
|
|
float minU = std::min({uv[0].x, uv[1].x, uv[2].x}); |
|
|
|
for (int i = 0; i < 3; ++i) { |
|
|
|
float maxU = std::max({uv[0].x, uv[1].x, uv[2].x}); |
|
|
|
int px = int(uv[i].x * textureSize); |
|
|
|
float minV = std::min({uv[0].y, uv[1].y, uv[2].y}); |
|
|
|
int py = int(uv[i].y * textureSize); |
|
|
|
float maxV = std::max({uv[0].y, uv[1].y, uv[2].y}); |
|
|
|
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 采样
|
|
|
|
int minX = std::max(0, (int)floor(minU * textureSize) - 1); |
|
|
|
for (int y = bbox.y; y < bbox.y + bbox.height; ++y) { |
|
|
|
int maxX = std::min(textureSize - 1, (int)ceil(maxU * textureSize) + 1); |
|
|
|
for (int x = bbox.x; x < bbox.x + bbox.width; ++x) { |
|
|
|
int minY = std::max(0, (int)floor(minV * textureSize) - 1); |
|
|
|
|
|
|
|
int maxY = std::min(textureSize - 1, (int)ceil(maxV * textureSize) + 1); |
|
|
|
|
|
|
|
|
|
|
|
for (int sy = 0; sy < SUPER_SAMPLE; ++sy) { |
|
|
|
if (minX > maxX || minY > maxY) continue; |
|
|
|
for (int sx = 0; sx < SUPER_SAMPLE; ++sx) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Point2f texCoord( |
|
|
|
// ✅ 关键修复2:预计算三角形深度范围
|
|
|
|
(x + (sx + 0.5f) * STEP) / textureSize, |
|
|
|
Point3f triCenter = (*verts[0] + *verts[1] + *verts[2]) / 3.0f; |
|
|
|
(y + (sy + 0.5f) * STEP) / textureSize |
|
|
|
float triDepth = cam.PointDepth(triCenter); |
|
|
|
); |
|
|
|
float depthRange = 0.1f * triDepth; // 10%的深度容差
|
|
|
|
|
|
|
|
|
|
|
|
Point3f bary; |
|
|
|
for (int y = minY; y <= maxY; ++y) { |
|
|
|
if (!PointInTriangle(texCoord, uv[0], uv[1], uv[2], bary)) |
|
|
|
for (int x = minX; x <= maxX; ++x) { |
|
|
|
continue; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 3D 世界坐标
|
|
|
|
Point2f texCoord( |
|
|
|
Point3f P = |
|
|
|
(x + 0.5f) / textureSize, |
|
|
|
*verts[0] * bary.x + |
|
|
|
(y + 0.5f) / textureSize |
|
|
|
*verts[1] * bary.y + |
|
|
|
); |
|
|
|
*verts[2] * bary.z; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 投影到图像
|
|
|
|
Point3f bary; |
|
|
|
Point2f imgPt = ProjectPointWithAutoCorrection(cam, P, srcImg); |
|
|
|
if (!PointInTriangle(texCoord, uv[0], uv[1], uv[2], bary)) |
|
|
|
// 允许少量越界
|
|
|
|
continue; |
|
|
|
if (!srcImg.image.isInside(imgPt)) { |
|
|
|
|
|
|
|
imgPt.x = std::clamp(imgPt.x, 0.f, srcImg.image.width() - 1.1f); |
|
|
|
|
|
|
|
imgPt.y = std::clamp(imgPt.y, 0.f, srcImg.image.height() - 1.1f); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 放宽前后判断(允许微小负值)
|
|
|
|
// 3D 世界坐标
|
|
|
|
if (!cam.IsInFront(P) && cam.Distance(P) < -0.01f) |
|
|
|
Point3f P = |
|
|
|
continue; |
|
|
|
*verts[0] * bary.x + |
|
|
|
|
|
|
|
*verts[1] * bary.y + |
|
|
|
|
|
|
|
*verts[2] * bary.z; |
|
|
|
|
|
|
|
|
|
|
|
// 双三次采样
|
|
|
|
// ✅ 关键修复3:简化的深度测试
|
|
|
|
Sampler sampler; |
|
|
|
float depth = cam.PointDepth(P); |
|
|
|
Color c = srcImg.image.sample<Sampler, Color>(sampler, imgPt); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 累加到原子操作区域
|
|
|
|
// 放宽深度测试:允许一定范围的深度差异
|
|
|
|
#ifdef _USE_OPENMP |
|
|
|
if (depth < triDepth - depthRange || depth > triDepth + depthRange) |
|
|
|
#pragma omp critical |
|
|
|
continue; |
|
|
|
#endif |
|
|
|
|
|
|
|
{ |
|
|
|
// ✅ 关键修复4:严格的面片ID检查
|
|
|
|
cv::Vec3f& acc = colorAccum(y, x); |
|
|
|
bool accept = false; |
|
|
|
acc[0] += c[2]; |
|
|
|
#pragma omp critical |
|
|
|
acc[1] += c[1]; |
|
|
|
{ |
|
|
|
acc[2] += c[0]; |
|
|
|
// 如果像素未被占用,或者当前面片ID相同,或者深度更近
|
|
|
|
weightAccum(y, x) += 1.f; |
|
|
|
if (faceIDBuffer(y, x) == -1 || |
|
|
|
|
|
|
|
faceIDBuffer(y, x) == (int)faceID || |
|
|
|
|
|
|
|
depth > depthBuffer(y, x)) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 更新深度和面片ID
|
|
|
|
|
|
|
|
depthBuffer(y, x) = depth; |
|
|
|
|
|
|
|
faceIDBuffer(y, x) = (int)faceID; |
|
|
|
|
|
|
|
accept = true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!accept) continue; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 投影到图像
|
|
|
|
|
|
|
|
Point2f imgPt = ProjectPointWithAutoCorrection(cam, P, srcImg); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ✅ 关键修复5:改进的边界处理
|
|
|
|
|
|
|
|
if (!srcImg.image.isInside(imgPt)) { |
|
|
|
|
|
|
|
// 尝试在图像边界内寻找最近的有效点
|
|
|
|
|
|
|
|
imgPt.x = std::clamp(imgPt.x, 0.f, srcImg.image.width() - 1.1f); |
|
|
|
|
|
|
|
imgPt.y = std::clamp(imgPt.y, 0.f, srcImg.image.height() - 1.1f); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 前后判断
|
|
|
|
|
|
|
|
if (!cam.IsInFront(P) && cam.Distance(P) < -0.01f) |
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ✅ 关键修复6:最近邻采样(最锐利)
|
|
|
|
|
|
|
|
int ix = int(imgPt.x + 0.5f); |
|
|
|
|
|
|
|
int iy = int(imgPt.y + 0.5f); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 边界检查
|
|
|
|
|
|
|
|
if (ix < 0 || ix >= srcImg.image.width() || |
|
|
|
|
|
|
|
iy < 0 || iy >= srcImg.image.height()) |
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 获取像素颜色(OpenCV BGR -> openMVS RGB)
|
|
|
|
|
|
|
|
const cv::Vec3b& pixel = srcImg.image.at<cv::Vec3b>(iy, ix); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#pragma omp critical |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// ✅ 直接写入,不混合
|
|
|
|
|
|
|
|
colorBuffer(y, x) = cv::Vec3f(pixel[2], pixel[1], pixel[0]); |
|
|
|
|
|
|
|
validBuffer(y, x) = 1; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------
|
|
|
|
|
|
|
|
// 5. 空洞填充(修复零散空白点)
|
|
|
|
|
|
|
|
// --------------------------------------------------
|
|
|
|
|
|
|
|
DEBUG_EXTRA("Filling holes in rasterized texture..."); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 使用膨胀操作填充小的空洞
|
|
|
|
|
|
|
|
cv::Mat1b dilatedValid = validBuffer.clone(); |
|
|
|
|
|
|
|
cv::dilate(dilatedValid, dilatedValid, cv::Mat(), cv::Point(-1,-1), 2); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (int y = 0; y < textureSize; ++y) { |
|
|
|
|
|
|
|
for (int x = 0; x < textureSize; ++x) { |
|
|
|
|
|
|
|
if (validBuffer(y, x) == 0 && dilatedValid(y, x) == 1) { |
|
|
|
|
|
|
|
// 找到最近的有效像素
|
|
|
|
|
|
|
|
int bestDist = INT_MAX; |
|
|
|
|
|
|
|
cv::Vec3f bestColor(0,0,0); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (int dy = -3; dy <= 3; ++dy) { |
|
|
|
|
|
|
|
for (int dx = -3; dx <= 3; ++dx) { |
|
|
|
|
|
|
|
int ny = y + dy; |
|
|
|
|
|
|
|
int nx = x + dx; |
|
|
|
|
|
|
|
if (ny >= 0 && ny < textureSize && nx >= 0 && nx < textureSize && |
|
|
|
|
|
|
|
validBuffer(ny, nx) == 1) { |
|
|
|
|
|
|
|
int dist = dy*dy + dx*dx; |
|
|
|
|
|
|
|
if (dist < bestDist) { |
|
|
|
|
|
|
|
bestDist = dist; |
|
|
|
|
|
|
|
bestColor = colorBuffer(ny, nx); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (bestDist < INT_MAX) { |
|
|
|
|
|
|
|
colorBuffer(y, x) = bestColor; |
|
|
|
|
|
|
|
validBuffer(y, x) = 1; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------
|
|
|
|
// --------------------------------------------------
|
|
|
|
// 5. 权重归一化
|
|
|
|
// 6. 最终写入纹理
|
|
|
|
// --------------------------------------------------
|
|
|
|
// --------------------------------------------------
|
|
|
|
for (int y = 0; y < textureSize; ++y) { |
|
|
|
for (int y = 0; y < textureSize; ++y) { |
|
|
|
for (int x = 0; x < textureSize; ++x) { |
|
|
|
for (int x = 0; x < textureSize; ++x) { |
|
|
|
float w = weightAccum(y, x); |
|
|
|
if (validBuffer(y, x) == 1) { |
|
|
|
if (w > 0.f) { |
|
|
|
cv::Vec3f c = colorBuffer(y, x); |
|
|
|
cv::Vec3f c = colorAccum(y, x) / w; |
|
|
|
|
|
|
|
atlas(y, x) = Pixel8U{ |
|
|
|
atlas(y, x) = Pixel8U{ |
|
|
|
(unsigned char)cv::saturate_cast<uchar>(c[0]), |
|
|
|
(unsigned char)cv::saturate_cast<uchar>(c[0]), |
|
|
|
(unsigned char)cv::saturate_cast<uchar>(c[1]), |
|
|
|
(unsigned char)cv::saturate_cast<uchar>(c[1]), |
|
|
|
@ -14556,7 +14627,7 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(VirtualFaceMap& virtualFaceMap, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (faceNeighbors[faceID2].empty()) { |
|
|
|
if (faceNeighbors[faceID2].empty()) { |
|
|
|
DEBUG_EXTRA("FATAL: faceNeighbors[%u] is empty! FaceViewSelection mode error.", faceID2); |
|
|
|
// DEBUG_EXTRA("FATAL: faceNeighbors[%u] is empty! FaceViewSelection mode error.", faceID2);
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
@ -14601,8 +14672,8 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(VirtualFaceMap& virtualFaceMap, |
|
|
|
++fallbackByCenterFace; |
|
|
|
++fallbackByCenterFace; |
|
|
|
|
|
|
|
|
|
|
|
// 降低日志级别,避免刷屏,用 DEBUG 而非 EXTRA
|
|
|
|
// 降低日志级别,避免刷屏,用 DEBUG 而非 EXTRA
|
|
|
|
DEBUG("VF[%zu] EMPTY -> FORCED view %d (ignoring physical occlusion)", |
|
|
|
// DEBUG("VF[%zu] EMPTY -> FORCED view %d (ignoring physical occlusion)",
|
|
|
|
i, forcedView); |
|
|
|
// i, forcedView);
|
|
|
|
continue; |
|
|
|
continue; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|