|
|
|
|
@ -763,6 +763,52 @@ public:
@@ -763,6 +763,52 @@ public:
|
|
|
|
|
unsigned nTextureSizeMultiple, |
|
|
|
|
Pixel8U colEmpty, |
|
|
|
|
Mesh::Image8U3Arr& outTextures); |
|
|
|
|
// ========== 辅助函数 ==========
|
|
|
|
|
|
|
|
|
|
// 双线性采样(适配 SEACAVE::TImage<Pixel8U>)
|
|
|
|
|
static inline Pixel8U SampleBilinear(const Image8U3& img, const Point2f& pt) { |
|
|
|
|
float x = pt.x * img.width() - 0.5f; |
|
|
|
|
float y = pt.y * img.height() - 0.5f; |
|
|
|
|
int x0 = (int)floorf(x), y0 = (int)floorf(y); |
|
|
|
|
int x1 = x0 + 1, y1 = y0 + 1; |
|
|
|
|
float wx = x - x0, wy = y - y0; |
|
|
|
|
|
|
|
|
|
// 边界裁剪
|
|
|
|
|
x0 = std::max(0, std::min(x0, (int)img.width() - 1)); |
|
|
|
|
x1 = std::max(0, std::min(x1, (int)img.width() - 1)); |
|
|
|
|
y0 = std::max(0, std::min(y0, (int)img.height() - 1)); |
|
|
|
|
y1 = std::max(0, std::min(y1, (int)img.height() - 1)); |
|
|
|
|
|
|
|
|
|
const Pixel8U& p00 = img(y0, x0); |
|
|
|
|
const Pixel8U& p01 = img(y0, x1); |
|
|
|
|
const Pixel8U& p10 = img(y1, x0); |
|
|
|
|
const Pixel8U& p11 = img(y1, x1); |
|
|
|
|
|
|
|
|
|
auto lerp = [](uint8_t a, uint8_t b, float t) -> uint8_t { |
|
|
|
|
return (uint8_t)(a * (1 - t) + b * t); |
|
|
|
|
}; |
|
|
|
|
uint8_t r = lerp(lerp(p00.r, p01.r, wx), lerp(p10.r, p11.r, wx), wy); |
|
|
|
|
uint8_t g = lerp(lerp(p00.g, p01.g, wx), lerp(p10.g, p11.g, wx), wy); |
|
|
|
|
uint8_t b = lerp(lerp(p00.b, p01.b, wx), lerp(p10.b, p11.b, wx), wy); |
|
|
|
|
return Pixel8U(b, g, r); // BGR 存储
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 重心坐标计算
|
|
|
|
|
static inline Point3f Barycentric(const TexCoord& p, const TexCoord& a, |
|
|
|
|
const TexCoord& b, const TexCoord& c) { |
|
|
|
|
float den = (b.y - c.y) * (a.x - c.x) + (c.x - b.x) * (a.y - c.y); |
|
|
|
|
if (fabsf(den) < 1e-10f) return Point3f(-1, -1, -1); |
|
|
|
|
float v = ((b.y - c.y) * (p.x - c.x) + (c.x - b.x) * (p.y - c.y)) / den; |
|
|
|
|
float w = ((c.y - a.y) * (p.x - c.x) + (a.x - c.x) * (p.y - c.y)) / den; |
|
|
|
|
float u = 1.0f - v - w; |
|
|
|
|
return Point3f(u, v, w); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 从重心坐标恢复 3D 点
|
|
|
|
|
static inline Point3f BaryTo3D(const Point3f& bary, const Point3f& v0, |
|
|
|
|
const Point3f& v1, const Point3f& v2) { |
|
|
|
|
return v0 * bary.x + v1 * bary.y + v2 * bary.z; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
struct TexelScore { |
|
|
|
|
float score = -1.0f; |
|
|
|
|
@ -14410,6 +14456,119 @@ bool MeshTexture::RasterizeVirtualFaces(
@@ -14410,6 +14456,119 @@ bool MeshTexture::RasterizeVirtualFaces(
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// ========== 接缝区域多视图加权融合 ==========
|
|
|
|
|
// 收集需要融合的面(出现在 seamEdges 中的面)
|
|
|
|
|
std::set<int> seamFaceIndices; |
|
|
|
|
for (const auto& edge : seamEdges) { |
|
|
|
|
seamFaceIndices.insert((int)edge.i); |
|
|
|
|
seamFaceIndices.insert((int)edge.j); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!seamFaceIndices.empty()) { |
|
|
|
|
DEBUG_EXTRA("[Blend] Fusing %zu seam faces...", seamFaceIndices.size()); |
|
|
|
|
|
|
|
|
|
for (int i : seamFaceIndices) { |
|
|
|
|
if (i < 0 || i >= (int)virtualFaceMap.size()) continue; |
|
|
|
|
const VirtualFace& vf = virtualFaceMap[i]; |
|
|
|
|
if (vf.faces.empty() || virtualFaceViews[i].empty()) continue; |
|
|
|
|
|
|
|
|
|
IIndex refView = virtualFaceViews[i][0]; |
|
|
|
|
const auto& candidateViews = virtualFaceViews[i]; |
|
|
|
|
const auto& candidateWeights = virtualFaceViewWeights[i]; |
|
|
|
|
|
|
|
|
|
// 获取该面的 UV 包围盒
|
|
|
|
|
const VirtualFaceGeometry& geom = m_virtualFaceGeometries[i]; |
|
|
|
|
if (!geom.isValid) continue; |
|
|
|
|
|
|
|
|
|
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::max(0, (int)ceil(geom.uvBounds.ptMax.y() * textureSize)); |
|
|
|
|
if (minX > maxX || minY > maxY) continue; |
|
|
|
|
|
|
|
|
|
// 逐像素多视图融合
|
|
|
|
|
for (int y = minY; y <= maxY; ++y) { |
|
|
|
|
for (int x = minX; x <= maxX; ++x) { |
|
|
|
|
size_t idx = y * textureSize + x; |
|
|
|
|
if (m_texelScores[idx].viewID < 0) continue; |
|
|
|
|
|
|
|
|
|
// 只处理当前面贡献的像素(避免覆盖其他面)
|
|
|
|
|
// 用 score 判断:如果当前面不是胜者,跳过
|
|
|
|
|
float myScore = candidateWeights.empty() ? 0.5f : candidateWeights[0]; |
|
|
|
|
if (m_texelScores[idx].score > myScore + 0.01f) continue; |
|
|
|
|
|
|
|
|
|
// 计算 3D 点(从重心坐标)
|
|
|
|
|
// 简化:用该面第一个三角形
|
|
|
|
|
FIndex fid = vf.faces[0]; |
|
|
|
|
const Face& face = scene.mesh.faces[fid]; |
|
|
|
|
const TexCoord* uvs = &scene.mesh.faceTexcoords[fid * 3]; |
|
|
|
|
Point3f bary = Barycentric( |
|
|
|
|
TexCoord((float)x / textureSize, (float)y / textureSize), |
|
|
|
|
uvs[0], uvs[1], uvs[2]); |
|
|
|
|
if (bary.x < 0 || bary.y < 0 || bary.z < 0) continue; |
|
|
|
|
|
|
|
|
|
Point3f pt3D = BaryTo3D(bary, |
|
|
|
|
scene.mesh.vertices[face[0]], |
|
|
|
|
scene.mesh.vertices[face[1]], |
|
|
|
|
scene.mesh.vertices[face[2]]); |
|
|
|
|
|
|
|
|
|
// 多视图采样
|
|
|
|
|
cv::Vec3f blended(0, 0, 0); |
|
|
|
|
float totalW = 0.0f; |
|
|
|
|
|
|
|
|
|
for (size_t k = 0; k < candidateViews.size(); ++k) { |
|
|
|
|
IIndex vid = candidateViews[k]; |
|
|
|
|
float w = candidateWeights[k]; |
|
|
|
|
if (w < 0.01f) continue; |
|
|
|
|
|
|
|
|
|
const Image& img = images[vid]; |
|
|
|
|
Point2f pt2D = img.camera.ProjectPoint(Point3d(pt3D)); |
|
|
|
|
if (pt2D.x < 0 || pt2D.y < 0 || |
|
|
|
|
pt2D.x >= img.image.width() - 1 || |
|
|
|
|
pt2D.y >= img.image.height() - 1) continue; |
|
|
|
|
|
|
|
|
|
Pixel8U c = SampleBilinear(img.image, pt2D); |
|
|
|
|
cv::Vec3f color(c.r, c.g, c.b); |
|
|
|
|
|
|
|
|
|
// 局部增益:以 refView 为参考
|
|
|
|
|
if (vid != refView) { |
|
|
|
|
const Image& refImg = images[refView]; |
|
|
|
|
Point2f refPt = refImg.camera.ProjectPoint(Point3d(pt3D)); |
|
|
|
|
if (refPt.x >= 0 && refPt.y >= 0 && |
|
|
|
|
refPt.x < refImg.image.width() - 1 && |
|
|
|
|
refPt.y < refImg.image.height() - 1) { |
|
|
|
|
Pixel8U rc = SampleBilinear(refImg.image, refPt); |
|
|
|
|
cv::Vec3f refColor(rc.r, rc.g, rc.b); |
|
|
|
|
// 加 1 防除零
|
|
|
|
|
float gr = (refColor[0] + 1.0f) / (color[0] + 1.0f); |
|
|
|
|
float gg = (refColor[1] + 1.0f) / (color[1] + 1.0f); |
|
|
|
|
float gb = (refColor[2] + 1.0f) / (color[2] + 1.0f); |
|
|
|
|
gr = std::max(0.5f, std::min(2.0f, gr)); |
|
|
|
|
gg = std::max(0.5f, std::min(2.0f, gg)); |
|
|
|
|
gb = std::max(0.5f, std::min(2.0f, gb)); |
|
|
|
|
color = cv::Vec3f(color[0]*gr, color[1]*gg, color[2]*gb); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
blended += color * w; |
|
|
|
|
totalW += w; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (totalW > 0.01f) { |
|
|
|
|
blended /= totalW; |
|
|
|
|
atlas(y, x) = Pixel8U( |
|
|
|
|
(uint8_t)std::min(255.0f, blended[2]), // B
|
|
|
|
|
(uint8_t)std::min(255.0f, blended[1]), // G
|
|
|
|
|
(uint8_t)std::min(255.0f, blended[0]) // R
|
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
DEBUG_EXTRA("[Blend] Multi-view seam fusion done"); |
|
|
|
|
} |
|
|
|
|
// ========== 接缝融合 END ==========
|
|
|
|
|
|
|
|
|
|
m_texelScores.clear(); |
|
|
|
|
DEBUG_EXTRA("RC-style Rasterization completed: %s", TD_TIMER_GET_FMT().c_str()); |
|
|
|
|
|
|
|
|
|
|