|
|
|
@ -479,10 +479,27 @@ public: |
|
|
|
void LocalSeamLevelingExternalUV(); |
|
|
|
void LocalSeamLevelingExternalUV(); |
|
|
|
|
|
|
|
|
|
|
|
void GenerateTexture(bool bGlobalSeamLeveling, bool bLocalSeamLeveling, unsigned nTextureSizeMultiple, unsigned nRectPackingHeuristic, Pixel8U colEmpty, float fSharpnessWeight, int maxTextureSize, const SEACAVE::String& baseFileName, bool bOriginFaceview, Scene *pScene); |
|
|
|
void GenerateTexture(bool bGlobalSeamLeveling, bool bLocalSeamLeveling, unsigned nTextureSizeMultiple, unsigned nRectPackingHeuristic, Pixel8U colEmpty, float fSharpnessWeight, int maxTextureSize, const SEACAVE::String& baseFileName, bool bOriginFaceview, Scene *pScene); |
|
|
|
void GenerateTextureForUV(bool bGlobalSeamLeveling, bool bLocalSeamLeveling, unsigned nTextureSizeMultiple, unsigned nRectPackingHeuristic, Pixel8U colEmpty, float fSharpnessWeight, int maxTextureSize, const SEACAVE::String& basename, bool bOriginFaceview, Scene *pScene); |
|
|
|
void GenerateTextureForUV(bool bGlobalSeamLeveling, bool bLocalSeamLeveling, unsigned nTextureSizeMultiple, unsigned nRectPackingHeuristic, Pixel8U colEmpty, float fSharpnessWeight, int maxTextureSize, const SEACAVE::String& basename, bool bOriginFaceview, Scene *pScene, Mesh::TexCoordArr& existingTexcoords, Mesh::TexIndexArr& existingTexindices); |
|
|
|
void GenerateTexture2(bool bGlobalSeamLeveling, bool bLocalSeamLeveling, unsigned nTextureSizeMultiple, unsigned nRectPackingHeuristic, Pixel8U colEmpty, float fSharpnessWeight, int maxTextureSize, const SEACAVE::String& baseFileName); |
|
|
|
void GenerateTexture2(bool bGlobalSeamLeveling, bool bLocalSeamLeveling, unsigned nTextureSizeMultiple, unsigned nRectPackingHeuristic, Pixel8U colEmpty, float fSharpnessWeight, int maxTextureSize, const SEACAVE::String& baseFileName); |
|
|
|
bool TextureWithExistingUV(const IIndexArr& views, int nIgnoreMaskLabel, float fOutlierThreshold, unsigned nTextureSizeMultiple, Pixel8U colEmpty, float fSharpnessWeight); |
|
|
|
bool TextureWithExistingUV( |
|
|
|
Mesh::Image8U3Arr GenerateTextureAtlasFromUV(const LabelArr& faceLabels, const IIndexArr& views, unsigned nTextureSizeMultiple, Pixel8U colEmpty, float fSharpnessWeight); |
|
|
|
const IIndexArr& views, |
|
|
|
|
|
|
|
int nIgnoreMaskLabel, |
|
|
|
|
|
|
|
float fOutlierThreshold, |
|
|
|
|
|
|
|
unsigned nTextureSizeMultiple, |
|
|
|
|
|
|
|
Pixel8U colEmpty, |
|
|
|
|
|
|
|
float fSharpnessWeight, |
|
|
|
|
|
|
|
const Mesh::Image8U3Arr& existingTextures, // 添加已有纹理参数
|
|
|
|
|
|
|
|
const Mesh::TexCoordArr& existingTexcoords, // 添加已有UV参数
|
|
|
|
|
|
|
|
const Mesh::TexIndexArr& existingTexindices // 添加已有纹理索引参数
|
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
Mesh::Image8U3Arr GenerateTextureAtlasFromUV( |
|
|
|
|
|
|
|
const Mesh::Image8U3Arr& sourceTextures, // 已有纹理数组
|
|
|
|
|
|
|
|
const Mesh::TexCoordArr& sourceTexcoords, // 已有UV坐标
|
|
|
|
|
|
|
|
const Mesh::TexIndexArr& sourceTexindices, // 已有纹理索引
|
|
|
|
|
|
|
|
unsigned nTextureSizeMultiple, |
|
|
|
|
|
|
|
Pixel8U colEmpty, |
|
|
|
|
|
|
|
float fSharpnessWeight |
|
|
|
|
|
|
|
); |
|
|
|
Point2f ProjectPointWithAutoCorrection(const Camera& camera, const Vertex& worldPoint, const Image& sourceImage); |
|
|
|
Point2f ProjectPointWithAutoCorrection(const Camera& camera, const Vertex& worldPoint, const Image& sourceImage); |
|
|
|
Point2f ProjectPointRobust(const Camera& camera, const Vertex& worldPoint, const Image& sourceImage, float searchRadius = 0.02f); |
|
|
|
Point2f ProjectPointRobust(const Camera& camera, const Vertex& worldPoint, const Image& sourceImage, float searchRadius = 0.02f); |
|
|
|
bool ValidateProjection(const Vertex& worldPoint, const Image& sourceImage, Point2f imgPoint, float maxReprojectionError = 1.5f); |
|
|
|
bool ValidateProjection(const Vertex& worldPoint, const Image& sourceImage, Point2f imgPoint, float maxReprojectionError = 1.5f); |
|
|
|
@ -8834,14 +8851,14 @@ void MeshTexture::GenerateTexture(bool bGlobalSeamLeveling, bool bLocalSeamLevel |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void MeshTexture::GenerateTextureForUV(bool bGlobalSeamLeveling, bool bLocalSeamLeveling, unsigned nTextureSizeMultiple, unsigned nRectPackingHeuristic, Pixel8U colEmpty, float fSharpnessWeight, int maxTextureSize, const SEACAVE::String& basename, bool bOriginFaceview, Scene *pScene) |
|
|
|
void MeshTexture::GenerateTextureForUV(bool bGlobalSeamLeveling, bool bLocalSeamLeveling, unsigned nTextureSizeMultiple, unsigned nRectPackingHeuristic, Pixel8U colEmpty, float fSharpnessWeight, int maxTextureSize, const SEACAVE::String& basename, bool bOriginFaceview, Scene *pScene, Mesh::TexCoordArr& existingTexcoords, Mesh::TexIndexArr& existingTexindices) |
|
|
|
{ |
|
|
|
{ |
|
|
|
int border = 2; |
|
|
|
int border = 2; |
|
|
|
if (!bOriginFaceview) |
|
|
|
if (!bOriginFaceview) |
|
|
|
border = 4; |
|
|
|
border = 4; |
|
|
|
|
|
|
|
|
|
|
|
Mesh::TexCoordArr& faceTexcoords2 = scene.mesh.faceTexcoords; |
|
|
|
Mesh::TexCoordArr& faceTexcoords2 = existingTexcoords; |
|
|
|
Mesh::TexIndexArr& faceTexindices2 = scene.mesh.faceTexindices; |
|
|
|
Mesh::TexIndexArr& faceTexindices2 = existingTexindices; |
|
|
|
|
|
|
|
|
|
|
|
faceTexcoords2.resize(faces.size()*3); |
|
|
|
faceTexcoords2.resize(faces.size()*3); |
|
|
|
faceTexindices2.resize(faces.size()); |
|
|
|
faceTexindices2.resize(faces.size()); |
|
|
|
@ -9929,11 +9946,19 @@ bool SaveGeneratedTextures(const Mesh::Image8U3Arr& generatedTextures, const std |
|
|
|
return true; |
|
|
|
return true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
bool MeshTexture::TextureWithExistingUV(const IIndexArr& views, int nIgnoreMaskLabel, |
|
|
|
bool MeshTexture::TextureWithExistingUV( |
|
|
|
float fOutlierThreshold, unsigned nTextureSizeMultiple, |
|
|
|
const IIndexArr& views, |
|
|
|
Pixel8U colEmpty, float fSharpnessWeight) |
|
|
|
int nIgnoreMaskLabel, |
|
|
|
|
|
|
|
float fOutlierThreshold, |
|
|
|
|
|
|
|
unsigned nTextureSizeMultiple, |
|
|
|
|
|
|
|
Pixel8U colEmpty, |
|
|
|
|
|
|
|
float fSharpnessWeight, |
|
|
|
|
|
|
|
const Mesh::Image8U3Arr& existingTextures, // 添加已有纹理参数
|
|
|
|
|
|
|
|
const Mesh::TexCoordArr& existingTexcoords, // 添加已有UV参数
|
|
|
|
|
|
|
|
const Mesh::TexIndexArr& existingTexindices // 添加已有纹理索引参数
|
|
|
|
|
|
|
|
) |
|
|
|
{ |
|
|
|
{ |
|
|
|
DEBUG_EXTRA("TextureWithExistingUV 1"); |
|
|
|
DEBUG_EXTRA("TextureWithExistingUV - Using existing texture data"); |
|
|
|
TD_TIMER_START(); |
|
|
|
TD_TIMER_START(); |
|
|
|
|
|
|
|
|
|
|
|
// 1. 验证输入
|
|
|
|
// 1. 验证输入
|
|
|
|
@ -9942,69 +9967,50 @@ bool MeshTexture::TextureWithExistingUV(const IIndexArr& views, int nIgnoreMaskL |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (scene.mesh.faceTexcoords.size() != scene.mesh.faces.size() * 3) { |
|
|
|
if (existingTextures.empty() || existingTexcoords.empty()) { |
|
|
|
VERBOSE("error: UV coordinates count does not match face count, %d, %d", |
|
|
|
VERBOSE("error: no existing texture data provided"); |
|
|
|
scene.mesh.faceTexcoords.size(), scene.mesh.faces.size() * 3); |
|
|
|
|
|
|
|
return false; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
DEBUG_EXTRA("TextureWithExistingUV 2"); |
|
|
|
// 验证UV坐标数量匹配
|
|
|
|
|
|
|
|
if (existingTexcoords.size() != scene.mesh.faceTexcoords.size()) { |
|
|
|
// 2. 为每个面选择最佳视图
|
|
|
|
VERBOSE("error: UV coordinate count mismatch: existing=%zu, mesh=%zu", |
|
|
|
FaceDataViewArr facesDatas; |
|
|
|
existingTexcoords.size(), scene.mesh.faceTexcoords.size()); |
|
|
|
if (!ListCameraFaces(facesDatas, fOutlierThreshold, nIgnoreMaskLabel, views, false)) { |
|
|
|
|
|
|
|
return false; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
DEBUG_EXTRA("TextureWithExistingUV 3 faceSize=%d", scene.mesh.faces.size()); |
|
|
|
DEBUG_EXTRA("Processing %zu faces with existing texture data", scene.mesh.faces.size()); |
|
|
|
|
|
|
|
|
|
|
|
// 3. 为每个面分配最佳视图
|
|
|
|
|
|
|
|
LabelArr faceLabels(scene.mesh.faces.size()); |
|
|
|
|
|
|
|
FOREACH(idxFace, scene.mesh.faces) { |
|
|
|
|
|
|
|
const FaceDataArr& faceDatas = facesDatas[idxFace]; |
|
|
|
|
|
|
|
if (faceDatas.empty()) { |
|
|
|
|
|
|
|
faceLabels[idxFace] = 0; // 无视图可用
|
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 选择质量最高的视图
|
|
|
|
|
|
|
|
float bestQuality = -1; |
|
|
|
|
|
|
|
IIndex bestView = NO_ID; |
|
|
|
|
|
|
|
for (const FaceData& data : faceDatas) { |
|
|
|
|
|
|
|
if (data.quality > bestQuality && !data.bInvalidFacesRelative) { |
|
|
|
|
|
|
|
bestQuality = data.quality; |
|
|
|
|
|
|
|
bestView = data.idxView; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
faceLabels[idxFace] = (bestView != NO_ID) ? (bestView + 1) : 0; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 4. 生成纹理图集(使用模型原始UV坐标)
|
|
|
|
// 2. 生成纹理图集(从已有纹理采样,但使用模型原始UV)
|
|
|
|
Mesh::Image8U3Arr generatedTextures = GenerateTextureAtlasFromUV( |
|
|
|
Mesh::Image8U3Arr generatedTextures = GenerateTextureAtlasFromUV( |
|
|
|
faceLabels, views, nTextureSizeMultiple, colEmpty, fSharpnessWeight); |
|
|
|
existingTextures, |
|
|
|
|
|
|
|
existingTexcoords, |
|
|
|
|
|
|
|
existingTexindices, |
|
|
|
|
|
|
|
nTextureSizeMultiple, |
|
|
|
|
|
|
|
colEmpty, |
|
|
|
|
|
|
|
fSharpnessWeight |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
if (!generatedTextures.empty()) { |
|
|
|
if (!generatedTextures.empty()) { |
|
|
|
scene.mesh.texturesDiffuse = std::move(generatedTextures); |
|
|
|
scene.mesh.texturesDiffuse = std::move(generatedTextures); |
|
|
|
|
|
|
|
|
|
|
|
// 同时设置面的纹理索引
|
|
|
|
// 设置面的纹理索引
|
|
|
|
scene.mesh.faceTexindices.resize(scene.mesh.faces.size()); |
|
|
|
if (scene.mesh.texturesDiffuse.size() > 1) { |
|
|
|
for (size_t i = 0; i < scene.mesh.faces.size(); ++i) { |
|
|
|
scene.mesh.faceTexindices.resize(scene.mesh.faces.size()); |
|
|
|
scene.mesh.faceTexindices[i] = 0; // 所有面使用第一个纹理
|
|
|
|
for (size_t i = 0; i < scene.mesh.faces.size(); ++i) { |
|
|
|
|
|
|
|
scene.mesh.faceTexindices[i] = 0; // 所有面使用第一个纹理
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
scene.mesh.faceTexindices.Release(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
DEBUG_EXTRA("成功生成 %zu 个纹理,已赋值给 mesh", scene.mesh.texturesDiffuse.size()); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 保存纹理到文件
|
|
|
|
DEBUG_EXTRA("Generated %zu textures from existing data", |
|
|
|
std::string outputDir = "texture_output"; |
|
|
|
scene.mesh.texturesDiffuse.size()); |
|
|
|
if (SaveGeneratedTextures(scene.mesh.texturesDiffuse, outputDir)) { |
|
|
|
|
|
|
|
DEBUG_EXTRA("纹理已成功保存到目录: %s", outputDir.c_str()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return true; |
|
|
|
return true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
DEBUG_EXTRA("纹理生成失败"); |
|
|
|
DEBUG_EXTRA("Texture generation failed"); |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@ -10450,179 +10456,159 @@ void ApplySharpening(Image8U3& texture, float weight) |
|
|
|
cv::addWeighted(texture, 1.0 + weight, blurred, -weight, 0, texture); |
|
|
|
cv::addWeighted(texture, 1.0 + weight, blurred, -weight, 0, texture); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
Mesh::Image8U3Arr MeshTexture::GenerateTextureAtlasFromUV(const LabelArr& faceLabels, const IIndexArr& views, |
|
|
|
Mesh::Image8U3Arr MeshTexture::GenerateTextureAtlasFromUV( |
|
|
|
unsigned nTextureSizeMultiple, Pixel8U colEmpty, float fSharpnessWeight) |
|
|
|
const Mesh::Image8U3Arr& sourceTextures, // 已有纹理数组
|
|
|
|
|
|
|
|
const Mesh::TexCoordArr& sourceTexcoords, // 已有UV坐标
|
|
|
|
|
|
|
|
const Mesh::TexIndexArr& sourceTexindices, // 已有纹理索引
|
|
|
|
|
|
|
|
unsigned nTextureSizeMultiple, |
|
|
|
|
|
|
|
Pixel8U colEmpty, |
|
|
|
|
|
|
|
float fSharpnessWeight |
|
|
|
|
|
|
|
) |
|
|
|
{ |
|
|
|
{ |
|
|
|
// 1. 分析整个模型的UV布局
|
|
|
|
DEBUG_EXTRA("Generating texture atlas from existing UV data with %zu source textures", |
|
|
|
|
|
|
|
sourceTextures.size()); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 1. 分析模型原始UV布局
|
|
|
|
AABB2f uvBounds(true); |
|
|
|
AABB2f uvBounds(true); |
|
|
|
FOREACH(i, scene.mesh.faceTexcoords) { |
|
|
|
FOREACH(i, scene.mesh.faceTexcoords) { |
|
|
|
const TexCoord& uv = scene.mesh.faceTexcoords[i]; |
|
|
|
const TexCoord& uv = scene.mesh.faceTexcoords[i]; |
|
|
|
uvBounds.InsertFull(uv); |
|
|
|
uvBounds.InsertFull(uv); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 2. 根据UV范围确定纹理图集尺寸
|
|
|
|
// 确保UV在[0,1]范围内
|
|
|
|
// 修正这里:计算UV的宽度和高度,然后传递给函数
|
|
|
|
if (uvBounds.ptMin.x() < 0 || uvBounds.ptMin.y() < 0 || |
|
|
|
|
|
|
|
uvBounds.ptMax.x() > 1 || uvBounds.ptMax.y() > 1) { |
|
|
|
|
|
|
|
// UV超出范围,进行归一化
|
|
|
|
|
|
|
|
DEBUG_EXTRA("UV coordinates out of [0,1] range, normalizing..."); |
|
|
|
|
|
|
|
uvBounds = AABB2f(true); |
|
|
|
|
|
|
|
for (size_t i = 0; i < scene.mesh.faceTexcoords.size(); i += 3) { |
|
|
|
|
|
|
|
for (int v = 0; v < 3; ++v) { |
|
|
|
|
|
|
|
const TexCoord& uv = scene.mesh.faceTexcoords[i + v]; |
|
|
|
|
|
|
|
uvBounds.InsertFull(uv); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 计算纹理尺寸
|
|
|
|
const float uvWidth = uvBounds.ptMax.x() - uvBounds.ptMin.x(); |
|
|
|
const float uvWidth = uvBounds.ptMax.x() - uvBounds.ptMin.x(); |
|
|
|
const float uvHeight = uvBounds.ptMax.y() - uvBounds.ptMin.y(); |
|
|
|
const float uvHeight = uvBounds.ptMax.y() - uvBounds.ptMin.y(); |
|
|
|
const int textureSize = ComputeOptimalTextureSize(uvWidth, uvHeight, nTextureSizeMultiple); |
|
|
|
const int textureSize = ComputeOptimalTextureSize(uvWidth, uvHeight, nTextureSizeMultiple); |
|
|
|
|
|
|
|
|
|
|
|
// 3. 创建单个纹理图集
|
|
|
|
// 2. 创建单个纹理图集
|
|
|
|
Mesh::Image8U3Arr textures; |
|
|
|
Mesh::Image8U3Arr textures; |
|
|
|
Image8U3& textureAtlas = textures.emplace_back(textureSize, textureSize); |
|
|
|
Image8U3& textureAtlas = textures.emplace_back(textureSize, textureSize); |
|
|
|
textureAtlas.setTo(cv::Scalar(colEmpty.b, colEmpty.g, colEmpty.r)); |
|
|
|
textureAtlas.setTo(cv::Scalar(colEmpty.b, colEmpty.g, colEmpty.r)); |
|
|
|
|
|
|
|
|
|
|
|
DEBUG_EXTRA("生成纹理图集: 尺寸=%dx%d, UV范围=[%.3f,%.3f]-[%.3f,%.3f], UV宽高=[%.3f,%.3f]", |
|
|
|
DEBUG_EXTRA("Creating texture atlas: %dx%d, UV bounds: [%.3f,%.3f]-[%.3f,%.3f]", |
|
|
|
textureSize, textureSize, |
|
|
|
textureSize, textureSize, |
|
|
|
uvBounds.ptMin.x(), uvBounds.ptMin.y(), |
|
|
|
uvBounds.ptMin.x(), uvBounds.ptMin.y(), |
|
|
|
uvBounds.ptMax.x(), uvBounds.ptMax.y(), |
|
|
|
uvBounds.ptMax.x(), uvBounds.ptMax.y()); |
|
|
|
uvWidth, uvHeight); |
|
|
|
|
|
|
|
|
|
|
|
// 3. 为每个面采样颜色
|
|
|
|
|
|
|
|
int processedFaces = 0; |
|
|
|
|
|
|
|
int failedFaces = 0; |
|
|
|
|
|
|
|
|
|
|
|
// 4. 为每个面片采样颜色并填充纹理图集
|
|
|
|
|
|
|
|
#ifdef _USE_OPENMP |
|
|
|
#ifdef _USE_OPENMP |
|
|
|
#pragma omp parallel for schedule(dynamic) |
|
|
|
#pragma omp parallel for schedule(dynamic) reduction(+:processedFaces, failedFaces) |
|
|
|
for (int_t idxFace = 0; idxFace < (int_t)scene.mesh.faces.size(); ++idxFace) { |
|
|
|
|
|
|
|
#else |
|
|
|
|
|
|
|
FOREACH(idxFace, scene.mesh.faces) { |
|
|
|
|
|
|
|
#endif |
|
|
|
#endif |
|
|
|
|
|
|
|
for (int_t idxFace = 0; idxFace < (int_t)scene.mesh.faces.size(); ++idxFace) { |
|
|
|
const FIndex faceID = (FIndex)idxFace; |
|
|
|
const FIndex faceID = (FIndex)idxFace; |
|
|
|
const Label label = faceLabels[faceID]; |
|
|
|
const Face& face = scene.mesh.faces[faceID]; |
|
|
|
if (label == 0) continue; // 跳过无视图的面片
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const IIndex idxView = label - 1; |
|
|
|
// 获取模型原始UV坐标
|
|
|
|
if (idxView >= images.size()) continue; |
|
|
|
const TexCoord* modelUVs = &scene.mesh.faceTexcoords[faceID * 3]; |
|
|
|
|
|
|
|
|
|
|
|
// 获取面的UV坐标和几何信息
|
|
|
|
// 获取对应source纹理中的UV坐标
|
|
|
|
const TexCoord* uvCoords = &scene.mesh.faceTexcoords[faceID * 3]; |
|
|
|
const TexCoord* sourceUVs = &sourceTexcoords[faceID * 3]; |
|
|
|
const Face& face = scene.mesh.faces[faceID]; |
|
|
|
const TexIndex textureIdx = sourceTexindices.empty() ? 0 : sourceTexindices[faceID]; |
|
|
|
const Image& sourceImage = images[idxView]; |
|
|
|
|
|
|
|
|
|
|
|
if (textureIdx >= sourceTextures.size()) { |
|
|
|
|
|
|
|
failedFaces++; |
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const Image8U3& sourceTexture = sourceTextures[textureIdx]; |
|
|
|
|
|
|
|
|
|
|
|
// 计算面片在纹理图集中的边界框
|
|
|
|
// 计算面片在目标纹理中的UV边界
|
|
|
|
AABB2f faceUVBounds(true); |
|
|
|
AABB2f faceUVBounds(true); |
|
|
|
for (int i = 0; i < 3; ++i) { |
|
|
|
for (int i = 0; i < 3; ++i) { |
|
|
|
faceUVBounds.InsertFull(uvCoords[i]); |
|
|
|
// 从模型UV映射到纹理坐标
|
|
|
|
|
|
|
|
const float u = (modelUVs[i].x - uvBounds.ptMin.x()) / uvWidth; |
|
|
|
|
|
|
|
const float v = (modelUVs[i].y - uvBounds.ptMin.y()) / uvHeight; |
|
|
|
|
|
|
|
faceUVBounds.InsertFull(Point2f(u, v)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 将UV坐标转换到纹理像素坐标
|
|
|
|
// 转换为像素坐标
|
|
|
|
const int startX = std::max(0, (int)(faceUVBounds.ptMin.x() * textureSize)); |
|
|
|
const int startX = std::max(0, (int)(faceUVBounds.ptMin.x() * textureSize)); |
|
|
|
const int startY = std::max(0, (int)(faceUVBounds.ptMin.y() * textureSize)); |
|
|
|
const int startY = std::max(0, (int)(faceUVBounds.ptMin.y() * textureSize)); |
|
|
|
const int endX = std::min(textureSize - 1, (int)(faceUVBounds.ptMax.x() * textureSize)); |
|
|
|
const int endX = std::min(textureSize - 1, (int)(faceUVBounds.ptMax.x() * textureSize)); |
|
|
|
const int endY = std::min(textureSize - 1, (int)(faceUVBounds.ptMax.y() * textureSize)); |
|
|
|
const int endY = std::min(textureSize - 1, (int)(faceUVBounds.ptMax.y() * textureSize)); |
|
|
|
|
|
|
|
|
|
|
|
// 对面片覆盖的每个纹理像素进行采样
|
|
|
|
if (startX > endX || startY > endY) { |
|
|
|
|
|
|
|
failedFaces++; |
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 对面片覆盖的每个像素进行采样
|
|
|
|
for (int y = startY; y <= endY; ++y) { |
|
|
|
for (int y = startY; y <= endY; ++y) { |
|
|
|
for (int x = startX; x <= endX; ++x) { |
|
|
|
for (int x = startX; x <= endX; ++x) { |
|
|
|
const Point2f texCoord((float)x / textureSize, (float)y / textureSize); |
|
|
|
const Point2f texCoord((float)x / textureSize, (float)y / textureSize); |
|
|
|
|
|
|
|
|
|
|
|
// 1. 检查是否在三角形内
|
|
|
|
// 1. 检查是否在模型UV三角形内
|
|
|
|
Point3f barycentric; |
|
|
|
Point3f barycentric; |
|
|
|
if (PointInTriangle(texCoord, uvCoords[0], uvCoords[1], uvCoords[2], barycentric)) { |
|
|
|
if (!PointInTriangle(texCoord, |
|
|
|
// 计算3D空间中的对应点
|
|
|
|
Point2f((modelUVs[0].x - uvBounds.ptMin.x()) / uvWidth, |
|
|
|
const Vertex worldPoint = |
|
|
|
(modelUVs[0].y - uvBounds.ptMin.y()) / uvHeight), |
|
|
|
vertices[face[0]] * barycentric.x + |
|
|
|
Point2f((modelUVs[1].x - uvBounds.ptMin.x()) / uvWidth, |
|
|
|
vertices[face[1]] * barycentric.y + |
|
|
|
(modelUVs[1].y - uvBounds.ptMin.y()) / uvHeight), |
|
|
|
vertices[face[2]] * barycentric.z; |
|
|
|
Point2f((modelUVs[2].x - uvBounds.ptMin.x()) / uvWidth, |
|
|
|
|
|
|
|
(modelUVs[2].y - uvBounds.ptMin.y()) / uvHeight), barycentric)) { |
|
|
|
// 将3D点投影到源图像
|
|
|
|
continue; |
|
|
|
Point2f imgPoint = ProjectPointWithAutoCorrection(sourceImage.camera, worldPoint, sourceImage); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 验证投影的有效性
|
|
|
|
|
|
|
|
if (!ValidateProjection(worldPoint, sourceImage, imgPoint)) { |
|
|
|
|
|
|
|
continue; // 跳过几何不一致的采样点
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 检查投影是否在图像边界内
|
|
|
|
|
|
|
|
if (imgPoint.x < -100 || imgPoint.x > sourceImage.image.cols + 100 || |
|
|
|
|
|
|
|
imgPoint.y < -100 || imgPoint.y > sourceImage.image.rows + 100) { |
|
|
|
|
|
|
|
DEBUG_EXTRA("异常投影: 图像点(%.1f,%.1f) 超出图像范围(%dx%d)", |
|
|
|
|
|
|
|
imgPoint.x, imgPoint.y, sourceImage.image.cols, sourceImage.image.rows); |
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 检查投影有效性
|
|
|
|
|
|
|
|
if (!sourceImage.image.isInside(imgPoint) || |
|
|
|
|
|
|
|
!sourceImage.camera.IsInFront(worldPoint)) { |
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 从源图像采样颜色
|
|
|
|
|
|
|
|
Pixel8U sampledColor = SampleImageBilinear(sourceImage.image, imgPoint); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 将采样颜色写入纹理图集
|
|
|
|
|
|
|
|
#ifdef _USE_OPENMP |
|
|
|
|
|
|
|
#pragma omp critical |
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
textureAtlas(y, x) = sampledColor; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
// 2. 如果不在三角形内,检查是否在三角形边缘附近
|
|
|
|
|
|
|
|
else { |
|
|
|
// 2. 使用相同的重心坐标在source纹理UV中插值
|
|
|
|
// 计算到三角形边缘的最近距离
|
|
|
|
Point2f sourceTexCoord( |
|
|
|
float minDist = std::numeric_limits<float>::max(); |
|
|
|
sourceUVs[0].x * barycentric.x + |
|
|
|
Point2f closestPoint; |
|
|
|
sourceUVs[1].x * barycentric.y + |
|
|
|
|
|
|
|
sourceUVs[2].x * barycentric.z, |
|
|
|
// 检查三条边
|
|
|
|
sourceUVs[0].y * barycentric.x + |
|
|
|
for (int i = 0; i < 3; ++i) { |
|
|
|
sourceUVs[1].y * barycentric.y + |
|
|
|
const Point2f& p1 = uvCoords[i]; |
|
|
|
sourceUVs[2].y * barycentric.z |
|
|
|
const Point2f& p2 = uvCoords[(i + 1) % 3]; |
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
Point2f proj = ProjectPointToLineSegment(texCoord, p1, p2); |
|
|
|
// 确保UV坐标在有效范围内
|
|
|
|
float dist = cv::norm(texCoord - proj); |
|
|
|
if (sourceTexCoord.x < 0 || sourceTexCoord.x >= sourceTexture.cols || |
|
|
|
|
|
|
|
sourceTexCoord.y < 0 || sourceTexCoord.y >= sourceTexture.rows) { |
|
|
|
if (dist < minDist) { |
|
|
|
continue; |
|
|
|
minDist = dist; |
|
|
|
|
|
|
|
closestPoint = proj; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 如果距离小于阈值,进行边缘填充
|
|
|
|
|
|
|
|
const float edgeThreshold = 1.0f / textureSize; |
|
|
|
|
|
|
|
if (minDist <= edgeThreshold * 2) { |
|
|
|
|
|
|
|
// 在最近点计算重心坐标
|
|
|
|
|
|
|
|
Point3f edgeBarycentric = BarycentricFromPoint(closestPoint, uvCoords[0], uvCoords[1], uvCoords[2]); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (edgeBarycentric.x >= 0 && edgeBarycentric.x <= 1 && |
|
|
|
|
|
|
|
edgeBarycentric.y >= 0 && edgeBarycentric.y <= 1 && |
|
|
|
|
|
|
|
edgeBarycentric.z >= 0 && edgeBarycentric.z <= 1) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const Vertex worldPoint = |
|
|
|
|
|
|
|
vertices[face[0]] * edgeBarycentric.x + |
|
|
|
|
|
|
|
vertices[face[1]] * edgeBarycentric.y + |
|
|
|
|
|
|
|
vertices[face[2]] * edgeBarycentric.z; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Point2f imgPoint = ProjectPointWithAutoCorrection(sourceImage.camera, worldPoint, sourceImage); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (ValidateProjection(worldPoint, sourceImage, imgPoint) && |
|
|
|
|
|
|
|
sourceImage.image.isInside(imgPoint) && |
|
|
|
|
|
|
|
sourceImage.camera.IsInFront(worldPoint)) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Pixel8U sampledColor = SampleImageBilinear(sourceImage.image, imgPoint); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef _USE_OPENMP |
|
|
|
|
|
|
|
#pragma omp critical |
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
textureAtlas(y, x) = sampledColor; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 3. 从source纹理中采样颜色
|
|
|
|
|
|
|
|
const cv::Vec3b color = sourceTexture.at<cv::Vec3b>( |
|
|
|
|
|
|
|
(int)sourceTexCoord.y, (int)sourceTexCoord.x); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 4. 写入目标纹理
|
|
|
|
|
|
|
|
textureAtlas.at<cv::Vec3b>(y, x) = color; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
processedFaces++; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 5. 添加后处理填充函数
|
|
|
|
DEBUG_EXTRA("Texture sampling completed: %d faces processed, %d faces failed", |
|
|
|
// 修正这里:移除多余的 faceLabels 参数
|
|
|
|
processedFaces, failedFaces); |
|
|
|
// FillTextureGaps(textureAtlas, scene.mesh.faceTexcoords, (FIndex)scene.mesh.faces.size(), textureSize, colEmpty);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 6. 应用后处理
|
|
|
|
// 4. 填充纹理空隙
|
|
|
|
|
|
|
|
if (processedFaces > 0) { |
|
|
|
|
|
|
|
// FillTextureGaps(textureAtlas, scene.mesh.faceTexcoords, (FIndex)scene.mesh.faces.size(), textureSize, colEmpty);
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 5. 应用锐化
|
|
|
|
if (fSharpnessWeight > 0) { |
|
|
|
if (fSharpnessWeight > 0) { |
|
|
|
// ApplySharpening(textureAtlas, fSharpnessWeight);
|
|
|
|
// ApplySharpening(textureAtlas, fSharpnessWeight);
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
DEBUG_EXTRA("纹理图集生成完成: %u个面片, 纹理尺寸%dx%d", |
|
|
|
DEBUG_EXTRA("Generated texture atlas: %dx%d from %zu source textures", |
|
|
|
scene.mesh.faces.size(), textureSize, textureSize); |
|
|
|
textureSize, textureSize, sourceTextures.size()); |
|
|
|
|
|
|
|
|
|
|
|
return textures; |
|
|
|
return textures; |
|
|
|
} |
|
|
|
} |
|
|
|
@ -11184,44 +11170,40 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi |
|
|
|
// mesh.CheckUVValid();
|
|
|
|
// mesh.CheckUVValid();
|
|
|
|
|
|
|
|
|
|
|
|
DEBUG_EXTRA("TextureMesh %b, %s", bUseExistingUV, strUVMeshFileName.c_str()); |
|
|
|
DEBUG_EXTRA("TextureMesh %b, %s", bUseExistingUV, strUVMeshFileName.c_str()); |
|
|
|
if (bUseExistingUV && !strUVMeshFileName.empty()) { |
|
|
|
if (bUseExistingUV && !strUVMeshFileName.empty()) { |
|
|
|
// 1. 先加载包含原始UV坐标的网格
|
|
|
|
Mesh::TexCoordArr existingTexcoords; |
|
|
|
VERBOSE("1faceTexcoords.size=%d, faces.size=%d", mesh.faceTexcoords.size(), mesh.faces.size() * 3); |
|
|
|
Mesh::TexIndexArr existingTexindices; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
texture.GenerateTextureForUV(bGlobalSeamLeveling, bLocalSeamLeveling, nTextureSizeMultiple, nRectPackingHeuristic, colEmpty, fSharpnessWeight, maxTextureSize, baseFileName, bOriginFaceview, this, existingTexcoords, existingTexindices); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 保存生成的纹理数据
|
|
|
|
|
|
|
|
Mesh::Image8U3Arr existingTextures = texture.texturesDiffuseTemp; |
|
|
|
|
|
|
|
// Mesh::TexCoordArr existingTexcoords = mesh.faceTexcoords; // 保存临时计算的UV
|
|
|
|
|
|
|
|
// Mesh::TexIndexArr existingTexindices = mesh.faceTexindices; // 保存纹理索引
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VERBOSE("1faceTexcoords.size=%d, faces.size=%d", mesh.faceTexcoords.size(), mesh.faces.size() * 3); |
|
|
|
// 使用预计算UV模式
|
|
|
|
// 使用预计算UV模式
|
|
|
|
if (!mesh.Load(MAKE_PATH_SAFE(strUVMeshFileName), true)) { |
|
|
|
if (!mesh.Load(MAKE_PATH_SAFE(strUVMeshFileName), true)) { |
|
|
|
VERBOSE("error: cannot load mesh file with UV coordinates"); |
|
|
|
VERBOSE("error: cannot load mesh file with UV coordinates"); |
|
|
|
return false; |
|
|
|
return false; // 注意:在成员函数中,返回 false 表示失败
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
VERBOSE("2faceTexcoords.size=%d, faces.size=%d", mesh.faceTexcoords.size(), mesh.faces.size() * 3); |
|
|
|
VERBOSE("2faceTexcoords.size=%d, faces.size=%d", mesh.faceTexcoords.size(), mesh.faces.size() * 3); |
|
|
|
|
|
|
|
// mesh.CheckUVValid();
|
|
|
|
|
|
|
|
//*
|
|
|
|
// 确保网格包含UV坐标
|
|
|
|
// 确保网格包含UV坐标
|
|
|
|
if (mesh.faceTexcoords.empty()) { |
|
|
|
if (mesh.faceTexcoords.empty()) { |
|
|
|
VERBOSE("error: the specified mesh does not contain UV coordinates"); |
|
|
|
VERBOSE("error: the specified mesh does not contain UV coordinates"); |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 2. 使用原有的正常方法,但保持加载的UV坐标不变
|
|
|
|
// 使用新的纹理生成方法
|
|
|
|
MeshTexture texture(*this, nResolutionLevel, nMinResolution); |
|
|
|
MeshTexture texture2(*this, nResolutionLevel, nMinResolution); |
|
|
|
|
|
|
|
if (!texture2.TextureWithExistingUV(views, nIgnoreMaskLabel, fOutlierThreshold, nTextureSizeMultiple, colEmpty, fSharpnessWeight, existingTextures, existingTexcoords, existingTexindices)){ |
|
|
|
// 关键修改:确保纹理类使用当前网格的UV坐标
|
|
|
|
|
|
|
|
texture.scene.mesh.faceTexcoords = mesh.faceTexcoords; // 使用原始UV坐标
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!texture.TextureWithExistingUV(views, nIgnoreMaskLabel, fOutlierThreshold, |
|
|
|
|
|
|
|
nTextureSizeMultiple, colEmpty, fSharpnessWeight)) { |
|
|
|
|
|
|
|
return false; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
//*/
|
|
|
|
// 将生成的纹理赋值回场景网格
|
|
|
|
|
|
|
|
mesh.texturesDiffuse = std::move(texture.scene.mesh.texturesDiffuse); |
|
|
|
|
|
|
|
if (!mesh.texturesDiffuse.empty()) { |
|
|
|
|
|
|
|
mesh.faceTexindices.resize(mesh.faces.size()); |
|
|
|
|
|
|
|
for (size_t i = 0; i < mesh.faces.size(); ++i) { |
|
|
|
|
|
|
|
mesh.faceTexindices[i] = 0; // 所有面使用第一个纹理
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return true; |
|
|
|
return true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|