接缝处理
This commit is contained in:
1 parent
c8969a05cb
commit
5cf68ce634
1 file changed
+498
-3
+498
-3
@@ -545,6 +545,21 @@ struct MeshTexture {
|
||||
AABB2f uvBounds; // UV 包围盒
|
||||
cv::Mat1f homography; // 3x3 单应矩阵(从 UV 到视图)
|
||||
bool isValid = false;
|
||||
|
||||
std::vector<FIndex> neighborFaces; // 邻接面ID
|
||||
std::vector<int> sharedEdgeIdx; // 共享边索引 (0,1,2)
|
||||
};
|
||||
|
||||
struct SeamBlendParams {
|
||||
int blendWidth = 4; // 融合宽度(像素)
|
||||
float sigma = 1.0f; // 高斯权重参数
|
||||
bool enableBlending = true;
|
||||
};
|
||||
|
||||
struct SeamInfo {
|
||||
FIndex faceA, faceB;
|
||||
int edgeIdxA, edgeIdxB; // 在各自面中的边索引
|
||||
std::vector<Point2f> seamPixels; // 接缝上的像素坐标
|
||||
};
|
||||
|
||||
// used to interpolate adjustments color over the whole texture patch
|
||||
@@ -657,8 +672,36 @@ public:
|
||||
return x + 1;
|
||||
}
|
||||
|
||||
void FeatherSeams(
|
||||
const std::vector<SeamInfo>& seams,
|
||||
int textureSize,
|
||||
Image8U3& atlas);
|
||||
void GenerateSeamMask(
|
||||
const std::vector<SeamInfo>& seams,
|
||||
int textureSize,
|
||||
cv::Mat& seamMask); // 输出:CV_32FC1,接缝处值 > 0
|
||||
bool ComputeVirtualFaceGeometry(const VirtualFaceMap& virtualFaceMap);
|
||||
|
||||
bool DetectSeamsForBlending(
|
||||
const VirtualFaceMap& virtualFaceMap,
|
||||
const std::vector<std::vector<IIndex>>& virtualFaceViews,
|
||||
std::vector<SeamInfo>& seams);
|
||||
void ExtractSeamPixels(
|
||||
const SeamInfo& seam,
|
||||
const VirtualFaceGeometry& geomA,
|
||||
const VirtualFaceGeometry& geomB,
|
||||
int textureSize,
|
||||
std::vector<Point2i>& pixelsA,
|
||||
std::vector<Point2i>& pixelsB);
|
||||
void BlendSeamPixels(
|
||||
const SeamInfo& seam,
|
||||
const std::vector<Point2i>& pixelsA,
|
||||
const std::vector<Point2i>& pixelsB,
|
||||
const VirtualFaceGeometry& geomA,
|
||||
const VirtualFaceGeometry& geomB,
|
||||
int textureSize,
|
||||
Image8U3& atlas);
|
||||
float PointToLineDistance(const Point2f& p, const Point2f& a, const Point2f& b);
|
||||
|
||||
bool ComputeHomographyForVirtualFace(
|
||||
const VirtualFace& vf,
|
||||
IIndex viewID,
|
||||
@@ -14334,6 +14377,9 @@ bool MeshTexture::RasterizeVirtualFaces(
|
||||
{
|
||||
for (int y = 0; y < patchH; ++y) {
|
||||
for (int x = 0; x < patchW; ++x) {
|
||||
// 在 RasterizeVirtualFaces 的像素写入循环中
|
||||
// ✅ 只保留最核心的逻辑,不做任何衰减
|
||||
|
||||
cv::Vec3b color = patch.at<cv::Vec3b>(y, x);
|
||||
if (color[0] == 0 && color[1] == 0 && color[2] == 0)
|
||||
continue;
|
||||
@@ -14347,12 +14393,11 @@ bool MeshTexture::RasterizeVirtualFaces(
|
||||
size_t idx = atlasY * textureSize + atlasX;
|
||||
TexelScore& ts = m_texelScores[idx];
|
||||
|
||||
// ✅ 获取当前虚拟面的评分
|
||||
float currentScore = virtualFaceViewWeights[i].empty()
|
||||
? -1.0f
|
||||
: virtualFaceViewWeights[i][0];
|
||||
|
||||
// ✅ 只接受更高评分的写入
|
||||
// ✅ 正常写入,不衰减,不修改
|
||||
if (currentScore > ts.score) {
|
||||
atlas(atlasY, atlasX) =
|
||||
Pixel8U{color[2], color[1], color[0]};
|
||||
@@ -14365,6 +14410,41 @@ bool MeshTexture::RasterizeVirtualFaces(
|
||||
}
|
||||
|
||||
m_texelScores.clear();
|
||||
|
||||
DEBUG_EXTRA("RC-style Rasterization completed: %s", TD_TIMER_GET_FMT().c_str());
|
||||
|
||||
// ✅ 新增:接缝融合后处理
|
||||
if (true) { // 可配置开关
|
||||
TD_TIMER_START();
|
||||
|
||||
// 1. 检测需要融合的接缝
|
||||
std::vector<SeamInfo> seams;
|
||||
if (!DetectSeamsForBlending(virtualFaceMap, virtualFaceViews, seams)) {
|
||||
DEBUG_EXTRA("Seam detection failed");
|
||||
return true; // 不影响主流程
|
||||
}
|
||||
// // 2. 方向性高斯羽化
|
||||
// FeatherSeams(seams, textureSize, atlas);
|
||||
|
||||
// 2. 对每个接缝进行融合
|
||||
for (const SeamInfo& seam : seams) {
|
||||
const VirtualFaceGeometry& geomA = m_virtualFaceGeometries[seam.faceA];
|
||||
const VirtualFaceGeometry& geomB = m_virtualFaceGeometries[seam.faceB];
|
||||
|
||||
// 提取接缝两侧像素
|
||||
std::vector<Point2i> pixelsA, pixelsB;
|
||||
ExtractSeamPixels(seam, geomA, geomB, textureSize, pixelsA, pixelsB);
|
||||
|
||||
if (!pixelsA.empty() || !pixelsB.empty()) {
|
||||
// 执行融合
|
||||
BlendSeamPixels(seam, pixelsA, pixelsB, geomA, geomB, textureSize, atlas);
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_EXTRA("Seam blending completed: %zu seams processed (%s)",
|
||||
seams.size(), TD_TIMER_GET_FMT().c_str());
|
||||
}
|
||||
|
||||
DEBUG_EXTRA("RC-style Rasterization completed: %s", TD_TIMER_GET_FMT().c_str());
|
||||
return true;
|
||||
}
|
||||
@@ -15094,6 +15174,11 @@ bool MeshTexture::ComputeVirtualFaceGeometry(const VirtualFaceMap& virtualFaceMa
|
||||
// ✅ std::vector 用 resize
|
||||
m_virtualFaceGeometries.resize(virtualFaceMap.size());
|
||||
|
||||
// ✅ 确保面邻接关系已计算
|
||||
if (scene.mesh.faceFaces.empty()) {
|
||||
scene.mesh.ListIncidenteFaceFaces();
|
||||
}
|
||||
|
||||
#ifdef _USE_OPENMP
|
||||
#pragma omp parallel for schedule(dynamic)
|
||||
#endif
|
||||
@@ -15106,6 +15191,21 @@ bool MeshTexture::ComputeVirtualFaceGeometry(const VirtualFaceMap& virtualFaceMa
|
||||
continue;
|
||||
}
|
||||
|
||||
FIndex faceID = vf.faces[0];
|
||||
|
||||
// ✅ 存储邻接面信息
|
||||
geom.neighborFaces.clear();
|
||||
geom.sharedEdgeIdx.clear();
|
||||
const Mesh::Face& neighbors = scene.mesh.faceFaces[faceID];
|
||||
|
||||
for (int edgeIdx = 0; edgeIdx < 3; ++edgeIdx) {
|
||||
FIndex neighborID = neighbors[edgeIdx];
|
||||
if (neighborID != NO_ID) {
|
||||
geom.neighborFaces.push_back(neighborID);
|
||||
geom.sharedEdgeIdx.push_back(edgeIdx);
|
||||
}
|
||||
}
|
||||
|
||||
IIndex viewID = faceViews[i][0];
|
||||
geom.isValid = ComputeHomographyForVirtualFace(vf, viewID, geom);
|
||||
}
|
||||
@@ -15113,6 +15213,401 @@ bool MeshTexture::ComputeVirtualFaceGeometry(const VirtualFaceMap& virtualFaceMa
|
||||
return true;
|
||||
}
|
||||
|
||||
void MeshTexture::FeatherSeams(
|
||||
const std::vector<SeamInfo>& seams,
|
||||
int textureSize,
|
||||
Image8U3& atlas)
|
||||
{
|
||||
// 1. 生成接缝掩码
|
||||
cv::Mat seamMask;
|
||||
GenerateSeamMask(seams, textureSize, seamMask);
|
||||
|
||||
// 2. 创建输出缓冲区
|
||||
cv::Mat result(atlas.rows, atlas.cols, CV_8UC3);
|
||||
atlas.copyTo(result); // 先复制原始数据
|
||||
|
||||
const int featherRadius = 8;
|
||||
|
||||
// 3. 对每条接缝做方向性高斯模糊
|
||||
for (const SeamInfo& seam : seams) {
|
||||
int eA = seam.edgeIdxA;
|
||||
const TexCoord& uvA0 = scene.mesh.faceTexcoords[seam.faceA * 3 + eA];
|
||||
const TexCoord& uvA1 = scene.mesh.faceTexcoords[seam.faceA * 3 + ((eA + 1) % 3)];
|
||||
|
||||
// 接缝方向向量(像素空间)
|
||||
float dx = (uvA1.x - uvA0.x) * textureSize;
|
||||
float dy = (uvA1.y - uvA0.y) * textureSize;
|
||||
float len = std::sqrt(dx * dx + dy * dy);
|
||||
if (len < 1e-6f) continue;
|
||||
|
||||
// 单位方向向量
|
||||
float dirX = dx / len;
|
||||
float dirY = dy / len;
|
||||
|
||||
// 沿接缝采样中心点
|
||||
const int samples = 30;
|
||||
for (int s = 0; s <= samples; ++s) {
|
||||
float t = (float)s / samples;
|
||||
int cx = (int)((uvA0.x + t * (uvA1.x - uvA0.x)) * textureSize + 0.5f);
|
||||
int cy = (int)((uvA0.y + t * (uvA1.y - uvA0.y)) * textureSize + 0.5f);
|
||||
|
||||
// 沿接缝方向做一维高斯模糊
|
||||
for (int side = -1; side <= 1; side += 2) {
|
||||
for (int offset = 1; offset <= featherRadius; ++offset) {
|
||||
int px = cx + (int)(side * (-dirY) * offset); // 法线方向
|
||||
int py = cy + (int)(side * (dirX) * offset);
|
||||
|
||||
if (px < 0 || px >= textureSize || py < 0 || py >= textureSize)
|
||||
continue;
|
||||
|
||||
// 高斯权重
|
||||
float sigma = featherRadius / 2.0f;
|
||||
float weight = std::exp(-(offset * offset) / (2 * sigma * sigma));
|
||||
|
||||
// 沿接缝方向采样(一维卷积)
|
||||
cv::Vec3f sum(0, 0, 0);
|
||||
float totalW = 0.0f;
|
||||
|
||||
for (int k = -featherRadius; k <= featherRadius; ++k) {
|
||||
int sx = px + (int)(dirX * k);
|
||||
int sy = py + (int)(dirY * k);
|
||||
|
||||
if (sx < 0 || sx >= textureSize || sy < 0 || sy >= textureSize)
|
||||
continue;
|
||||
|
||||
float kw = std::exp(-(k * k) / (2 * sigma * sigma));
|
||||
Pixel8U p = atlas(sy, sx);
|
||||
sum[0] += p.b * kw;
|
||||
sum[1] += p.g * kw;
|
||||
sum[2] += p.r * kw;
|
||||
totalW += kw;
|
||||
}
|
||||
|
||||
if (totalW > 0.0f) {
|
||||
// 混合原始颜色和模糊颜色
|
||||
Pixel8U orig = atlas(py, px);
|
||||
float maskVal = seamMask.at<float>(py, px);
|
||||
|
||||
cv::Vec3f blended(
|
||||
orig.b * (1.0f - maskVal) + (sum[0] / totalW) * maskVal,
|
||||
orig.g * (1.0f - maskVal) + (sum[1] / totalW) * maskVal,
|
||||
orig.r * (1.0f - maskVal) + (sum[2] / totalW) * maskVal
|
||||
);
|
||||
|
||||
result.at<cv::Vec3b>(py, px) = cv::Vec3b(
|
||||
(uchar)std::min(255.0f, blended[0]),
|
||||
(uchar)std::min(255.0f, blended[1]),
|
||||
(uchar)std::min(255.0f, blended[2])
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 写回
|
||||
for (int y = 0; y < textureSize; ++y) {
|
||||
for (int x = 0; x < textureSize; ++x) {
|
||||
cv::Vec3b c = result.at<cv::Vec3b>(y, x);
|
||||
atlas(y, x) = Pixel8U{c[2], c[1], c[0]};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MeshTexture::GenerateSeamMask(
|
||||
const std::vector<SeamInfo>& seams,
|
||||
int textureSize,
|
||||
cv::Mat& seamMask)
|
||||
{
|
||||
seamMask = cv::Mat(textureSize, textureSize, CV_32FC1, cv::Scalar(0.0f));
|
||||
|
||||
const int featherRadius = 8; // 羽化半径(像素),可调 4~16
|
||||
|
||||
for (const SeamInfo& seam : seams) {
|
||||
// 获取接缝的两个端点(UV 空间)
|
||||
int eA = seam.edgeIdxA;
|
||||
const TexCoord& uvA0 = scene.mesh.faceTexcoords[seam.faceA * 3 + eA];
|
||||
const TexCoord& uvA1 = scene.mesh.faceTexcoords[seam.faceA * 3 + ((eA + 1) % 3)];
|
||||
|
||||
// 沿接缝采样
|
||||
const int samples = 30;
|
||||
for (int s = 0; s <= samples; ++s) {
|
||||
float t = (float)s / samples;
|
||||
|
||||
// 接缝中心点(UV)
|
||||
float u = uvA0.x + t * (uvA1.x - uvA0.x);
|
||||
float v = uvA0.y + t * (uvA1.y - uvA1.y);
|
||||
|
||||
// 转像素坐标
|
||||
int cx = (int)(u * textureSize + 0.5f);
|
||||
int cy = (int)(v * textureSize + 0.5f);
|
||||
|
||||
// 计算接缝方向(用于方向性模糊)
|
||||
float dx = (uvA1.x - uvA0.x) * textureSize;
|
||||
float dy = (uvA1.y - uvA0.y) * textureSize;
|
||||
float len = std::sqrt(dx * dx + dy * dy);
|
||||
if (len < 1e-6f) continue;
|
||||
|
||||
// 法线方向(垂直接缝)
|
||||
float nx = -dy / len;
|
||||
float ny = dx / len;
|
||||
|
||||
// 在法线方向上标记羽化带
|
||||
for (int offset = -featherRadius; offset <= featherRadius; ++offset) {
|
||||
if (offset == 0) continue;
|
||||
|
||||
int px = cx + (int)(nx * offset);
|
||||
int py = cy + (int)(ny * offset);
|
||||
|
||||
if (px < 0 || px >= textureSize || py < 0 || py >= textureSize)
|
||||
continue;
|
||||
|
||||
// 高斯权重:距离越远,权重越低
|
||||
float gauss = std::exp(-(offset * offset) / (2.0f * (featherRadius / 2.0f) * (featherRadius / 2.0f)));
|
||||
|
||||
float& current = seamMask.at<float>(py, px);
|
||||
current = std::max(current, gauss); // 取最大值(多条接缝可能重叠)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool MeshTexture::DetectSeamsForBlending(
|
||||
const VirtualFaceMap& virtualFaceMap,
|
||||
const std::vector<std::vector<IIndex>>& virtualFaceViews,
|
||||
std::vector<SeamInfo>& seams)
|
||||
{
|
||||
seams.clear();
|
||||
|
||||
for (size_t i = 0; i < virtualFaceMap.size(); ++i) {
|
||||
const VirtualFaceGeometry& geomA = m_virtualFaceGeometries[i];
|
||||
if (!geomA.isValid) continue;
|
||||
|
||||
IIndex viewA = virtualFaceViews[i].empty() ? NO_ID : virtualFaceViews[i][0];
|
||||
|
||||
for (size_t n = 0; n < geomA.neighborFaces.size(); ++n) {
|
||||
FIndex neighborID = geomA.neighborFaces[n];
|
||||
int edgeIdxA = geomA.sharedEdgeIdx[n];
|
||||
|
||||
// 避免重复处理(只处理一次)
|
||||
if (neighborID < (FIndex)i) continue;
|
||||
|
||||
const VirtualFaceGeometry& geomB = m_virtualFaceGeometries[neighborID];
|
||||
if (!geomB.isValid) continue;
|
||||
|
||||
IIndex viewB = virtualFaceViews[neighborID].empty() ?
|
||||
NO_ID : virtualFaceViews[neighborID][0];
|
||||
|
||||
// ✅ 核心判断:不同视图才需要融合
|
||||
if (viewA != viewB && viewA != NO_ID && viewB != NO_ID) {
|
||||
SeamInfo seam;
|
||||
seam.faceA = i;
|
||||
seam.faceB = neighborID;
|
||||
seam.edgeIdxA = edgeIdxA;
|
||||
|
||||
// 找到边在 faceB 中的对应索引
|
||||
const Mesh::Face& neighbors = scene.mesh.faceFaces[neighborID];
|
||||
for (int e = 0; e < 3; ++e) {
|
||||
if (neighbors[e] == (FIndex)i) {
|
||||
seam.edgeIdxB = e;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
seams.push_back(seam);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_EXTRA("Detected %zu seams requiring blending", seams.size());
|
||||
return true;
|
||||
}
|
||||
void MeshTexture::ExtractSeamPixels(
|
||||
const SeamInfo& seam,
|
||||
const VirtualFaceGeometry& /*geomA*/,
|
||||
const VirtualFaceGeometry& /*geomB*/,
|
||||
int textureSize,
|
||||
std::vector<Point2i>& pixelsA,
|
||||
std::vector<Point2i>& pixelsB)
|
||||
{
|
||||
pixelsA.clear();
|
||||
pixelsB.clear();
|
||||
|
||||
// ✅ 从 mesh 直接取 UV(唯一正确方式)
|
||||
FIndex faceIDA = seam.faceA;
|
||||
int edgeIdxA = seam.edgeIdxA;
|
||||
const TexCoord& uvA0 = scene.mesh.faceTexcoords[faceIDA * 3 + edgeIdxA];
|
||||
const TexCoord& uvA1 = scene.mesh.faceTexcoords[faceIDA * 3 + ((edgeIdxA + 1) % 3)];
|
||||
|
||||
FIndex faceIDB = seam.faceB;
|
||||
int edgeIdxB = seam.edgeIdxB;
|
||||
const TexCoord& uvB0 = scene.mesh.faceTexcoords[faceIDB * 3 + edgeIdxB];
|
||||
const TexCoord& uvB1 = scene.mesh.faceTexcoords[faceIDB * 3 + ((edgeIdxB + 1) % 3)];
|
||||
|
||||
// ✅ 在纹理空间中采样接缝线段
|
||||
const int samples = 20;
|
||||
for (int s = 0; s <= samples; ++s) {
|
||||
float t = (float)s / samples;
|
||||
|
||||
// 接缝上的点(UV 坐标)
|
||||
Point2f uvOnSeamA(
|
||||
uvA0.x + t * (uvA1.x - uvA0.x),
|
||||
uvA0.y + t * (uvA1.y - uvA0.y)
|
||||
);
|
||||
Point2f uvOnSeamB(
|
||||
uvB0.x + t * (uvB1.x - uvB0.x),
|
||||
uvB0.y + t * (uvB1.y - uvB0.y)
|
||||
);
|
||||
|
||||
// 转换为纹理像素坐标
|
||||
Point2i pixelA(
|
||||
(int)(uvOnSeamA.x * textureSize + 0.5f),
|
||||
(int)(uvOnSeamA.y * textureSize + 0.5f)
|
||||
);
|
||||
Point2i pixelB(
|
||||
(int)(uvOnSeamB.x * textureSize + 0.5f),
|
||||
(int)(uvOnSeamB.y * textureSize + 0.5f)
|
||||
);
|
||||
|
||||
// ✅ 计算接缝方向向量
|
||||
Point2f dirA(uvA1.x - uvA0.x, uvA1.y - uvA0.y);
|
||||
Point2f dirB(uvB1.x - uvB0.x, uvB1.y - uvB0.y);
|
||||
|
||||
// 计算法线(逆时针旋转90度)
|
||||
Point2f normalA(-dirA.y, dirA.x);
|
||||
Point2f normalB(-dirB.y, dirB.x);
|
||||
|
||||
// 归一化
|
||||
float lenA = std::sqrt(normalA.x*normalA.x + normalA.y*normalA.y);
|
||||
float lenB = std::sqrt(normalB.x*normalB.x + normalB.y*normalB.y);
|
||||
|
||||
if (lenA > 1e-6f) {
|
||||
normalA.x /= lenA; normalA.y /= lenA;
|
||||
}
|
||||
if (lenB > 1e-6f) {
|
||||
normalB.x /= lenB; normalB.y /= lenB;
|
||||
}
|
||||
|
||||
// ✅ 提取两侧像素带
|
||||
const int blendWidth = 4;
|
||||
for (int offset = 1; offset <= blendWidth; ++offset) {
|
||||
// 面A侧
|
||||
Point2f pixelOffsetA(
|
||||
uvOnSeamA.x + normalA.x * (offset / (float)textureSize),
|
||||
uvOnSeamA.y + normalA.y * (offset / (float)textureSize)
|
||||
);
|
||||
Point2i pA(
|
||||
(int)(pixelOffsetA.x * textureSize + 0.5f),
|
||||
(int)(pixelOffsetA.y * textureSize + 0.5f)
|
||||
);
|
||||
if (pA.x >= 0 && pA.x < textureSize && pA.y >= 0 && pA.y < textureSize) {
|
||||
pixelsA.push_back(pA);
|
||||
}
|
||||
|
||||
// 面B侧
|
||||
Point2f pixelOffsetB(
|
||||
uvOnSeamB.x - normalB.x * (offset / (float)textureSize),
|
||||
uvOnSeamB.y - normalB.y * (offset / (float)textureSize)
|
||||
);
|
||||
Point2i pB(
|
||||
(int)(pixelOffsetB.x * textureSize + 0.5f),
|
||||
(int)(pixelOffsetB.y * textureSize + 0.5f)
|
||||
);
|
||||
if (pB.x >= 0 && pB.x < textureSize && pB.y >= 0 && pB.y < textureSize) {
|
||||
pixelsB.push_back(pB);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ 去重
|
||||
std::sort(pixelsA.begin(), pixelsA.end(),
|
||||
[](const Point2i& a, const Point2i& b) {
|
||||
return a.y < b.y || (a.y == b.y && a.x < b.x);
|
||||
});
|
||||
pixelsA.erase(std::unique(pixelsA.begin(), pixelsA.end()), pixelsA.end());
|
||||
|
||||
std::sort(pixelsB.begin(), pixelsB.end(),
|
||||
[](const Point2i& a, const Point2i& b) {
|
||||
return a.y < b.y || (a.y == b.y && a.x < b.x);
|
||||
});
|
||||
pixelsB.erase(std::unique(pixelsB.begin(), pixelsB.end()), pixelsB.end());
|
||||
}
|
||||
void MeshTexture::BlendSeamPixels(
|
||||
const SeamInfo& seam,
|
||||
const std::vector<Point2i>& pixelsA,
|
||||
const std::vector<Point2i>& pixelsB,
|
||||
const VirtualFaceGeometry& geomA,
|
||||
const VirtualFaceGeometry& geomB,
|
||||
int textureSize,
|
||||
Image8U3& atlas)
|
||||
{
|
||||
return;
|
||||
const int GUTTER_PX = 6; // 4px gutter,可根据效果调3~6
|
||||
|
||||
// ==========================================
|
||||
// 处理面A侧:只降低边缘像素的评分,不修改颜色
|
||||
// ==========================================
|
||||
for (const Point2i& pixel : pixelsA) {
|
||||
Point2f uvPixel(pixel.x / (float)textureSize, pixel.y / (float)textureSize);
|
||||
|
||||
// 计算到接缝边的距离(UV空间转像素距离)
|
||||
int eA = seam.edgeIdxA;
|
||||
const TexCoord& uv0 = scene.mesh.faceTexcoords[seam.faceA * 3 + eA];
|
||||
const TexCoord& uv1 = scene.mesh.faceTexcoords[seam.faceA * 3 + ((eA + 1) % 3)];
|
||||
float dist = PointToLineDistance(uvPixel, Point2f(uv0.x, uv0.y), Point2f(uv1.x, uv1.y)) * textureSize;
|
||||
|
||||
// ✅ 核心:只衰减评分,不碰颜色
|
||||
// 距离接缝越近,评分越低,越容易被对面覆盖
|
||||
if (dist < GUTTER_PX) {
|
||||
size_t idx = pixel.y * textureSize + pixel.x;
|
||||
if (idx < m_texelScores.size()) {
|
||||
// 二次曲线衰减,比线性衰减更平滑
|
||||
float attenuation = (dist * dist * dist) / (float)(GUTTER_PX * GUTTER_PX * GUTTER_PX);
|
||||
m_texelScores[idx].score *= attenuation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// 处理面B侧:对称操作,只降低边缘像素评分
|
||||
// ==========================================
|
||||
for (const Point2i& pixel : pixelsB) {
|
||||
Point2f uvPixel(pixel.x / (float)textureSize, pixel.y / (float)textureSize);
|
||||
|
||||
int eB = seam.edgeIdxB;
|
||||
const TexCoord& uv0 = scene.mesh.faceTexcoords[seam.faceB * 3 + eB];
|
||||
const TexCoord& uv1 = scene.mesh.faceTexcoords[seam.faceB * 3 + ((eB + 1) % 3)];
|
||||
float dist = PointToLineDistance(uvPixel, Point2f(uv0.x, uv0.y), Point2f(uv1.x, uv1.y)) * textureSize;
|
||||
|
||||
if (dist < GUTTER_PX) {
|
||||
size_t idx = pixel.y * textureSize + pixel.x;
|
||||
if (idx < m_texelScores.size()) {
|
||||
float attenuation = (dist * dist * dist) / (float)(GUTTER_PX * GUTTER_PX * GUTTER_PX);
|
||||
m_texelScores[idx].score *= attenuation;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ 辅助函数:点到直线距离
|
||||
float MeshTexture::PointToLineDistance(const Point2f& p, const Point2f& a, const Point2f& b) {
|
||||
float dx = b.x - a.x;
|
||||
float dy = b.y - a.y;
|
||||
float len2 = dx*dx + dy*dy;
|
||||
if (len2 < 1e-12f) {
|
||||
float dx2 = p.x - a.x;
|
||||
float dy2 = p.y - a.y;
|
||||
return std::sqrt(dx2*dx2 + dy2*dy2);
|
||||
}
|
||||
float t = ((p.x - a.x)*dx + (p.y - a.y)*dy) / len2;
|
||||
t = std::max(0.0f, std::min(1.0f, t));
|
||||
float projx = a.x + t*dx;
|
||||
float projy = a.y + t*dy;
|
||||
float distx = p.x - projx;
|
||||
float disty = p.y - projy;
|
||||
return std::sqrt(distx*distx + disty*disty);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 2. 核心:计算单应矩阵 / 仿射矩阵
|
||||
// - 3 个点 → Affine(getAffineTransform)
|
||||
|
||||
Reference in new issue
Block a user