diff --git a/libs/MVS/SceneTexture.cpp b/libs/MVS/SceneTexture.cpp index 31f5ade..8813436 100644 --- a/libs/MVS/SceneTexture.cpp +++ b/libs/MVS/SceneTexture.cpp @@ -17174,21 +17174,27 @@ if (patchID == NO_ID) { } } - // ========== 内置 AO 计算与暗部提亮(无需外部图片)========== - if (false) +// ========== 内置 AO 计算与暗部提亮 ========== + // 先释放不再需要的 RC patches 大内存 + { + // 清空 rcPatches 释放内存 + std::vector().swap(rcPatches); + // 清空 m_texelPatchID 释放内存(注意类型是 unsigned int) + std::vector().swap(m_texelPatchID); + DEBUG_EXTRA("[AO] Freed RC patches and texelPatchID memory"); + } + if (true) { DEBUG_EXTRA("[AO] Computing ambient occlusion directly..."); - // 1. 构建深度图(用顶点位置的 UV 空间投影) - cv::Mat depthMap(textureSize, textureSize, CV_32FC1, std::numeric_limits::max()); - cv::Mat normalMap(textureSize, textureSize, CV_32FC3, cv::Vec3f(0, 0, 0)); - cv::Mat faceIDMap(textureSize, textureSize, CV_32SC1, -1); + int aoTextureSize = 1024; + + cv::Mat depthMap(aoTextureSize, aoTextureSize, CV_32FC1, std::numeric_limits::max()); + cv::Mat normalMap(aoTextureSize, aoTextureSize, CV_32FC3, cv::Vec3f(0, 0, 0)); - // 遍历所有面,在 UV 空间 rasterize 深度和法线 for (size_t fi = 0; fi < scene.mesh.faces.size(); ++fi) { const Face& face = scene.mesh.faces[fi]; - // 获取三个顶点的 UV 和 3D 位置 Point2f uv[3]; Point3f pos[3]; for (int j = 0; j < 3; ++j) { @@ -17196,60 +17202,52 @@ if (patchID == NO_ID) { pos[j] = scene.mesh.vertices[face[j]]; } - // 计算法线 Vertex v0 = Vertex(pos[1]) - Vertex(pos[0]); Vertex v1 = Vertex(pos[2]) - Vertex(pos[0]); Vertex normal = v0.cross(v1); float len = sqrtf(normal.dot(normal)); - if (len > 0.0f) { - normal /= len; - } - - // 计算 UV 包围盒 + if (len > 0.0f) normal /= len; + float minU = std::min({uv[0].x, uv[1].x, uv[2].x}); float maxU = std::max({uv[0].x, uv[1].x, uv[2].x}); float minV = std::min({uv[0].y, uv[1].y, uv[2].y}); float maxV = std::max({uv[0].y, uv[1].y, uv[2].y}); - int minPX = std::max(0, (int)(minU * textureSize)); - int maxPX = std::min(textureSize - 1, (int)(maxU * textureSize)); - int minPY = std::max(0, (int)(minV * textureSize)); - int maxPY = std::min(textureSize - 1, (int)(maxV * textureSize)); + int minPX = std::max(0, (int)(minU * aoTextureSize)); + int maxPX = std::min(aoTextureSize - 1, (int)(maxU * aoTextureSize)); + int minPY = std::max(0, (int)(minV * aoTextureSize)); + int maxPY = std::min(aoTextureSize - 1, (int)(maxV * aoTextureSize)); - // 对每个像素做重心坐标插值 for (int py = minPY; py <= maxPY; ++py) { for (int px = minPX; px <= maxPX; ++px) { - float u = (float)px / textureSize; - float v = (float)py / textureSize; + float u = (float)px / aoTextureSize; + float v = (float)py / aoTextureSize; float b0, b1, b2; bool inside = ComputeBarycentric(u, v, uv[0], uv[1], uv[2], b0, b1, b2); if (!inside) continue; - // 插值得到 3D 位置(深度) Point3f P = pos[0] * b0 + pos[1] * b1 + pos[2] * b2; - float depth = sqrt(P.dot(P)); // 到原点的距离作为深度 + float depth = sqrt(P.dot(P)); - // 记录最小深度(离相机最近的面) if (depth < depthMap.at(py, px)) { depthMap.at(py, px) = depth; normalMap.at(py, px) = cv::Vec3f(normal.x, normal.y, normal.z); - faceIDMap.at(py, px) = (int)fi; } } } } - // 2. SSAO 计算 - cv::Mat aoMap(textureSize, textureSize, CV_32FC1, cv::Scalar(1.0f)); // 初始全白(无遮挡) + // SSAO 计算 + cv::Mat aoMapSmall(aoTextureSize, aoTextureSize, CV_32FC1, cv::Scalar(1.0f)); - int sampleRadius = 8; // 采样半径(像素) - int numSamples = 24; // 每个像素的采样点数 - float bias = 0.005f; // 深度偏差,防止自身遮挡 - float intensity = 1.5f; // AO 强度 + int sampleRadius = 8; + int numSamples = 72; // 恢复一些采样数 + float bias = 0.002f; + float intensity = 2.5f; - for (int y = 0; y < textureSize; ++y) { - for (int x = 0; x < textureSize; ++x) { + for (int y = 0; y < aoTextureSize; ++y) { + for (int x = 0; x < aoTextureSize; ++x) { float centerDepth = depthMap.at(y, x); if (centerDepth >= std::numeric_limits::max() - 1.0f) continue; @@ -17258,86 +17256,83 @@ if (patchID == NO_ID) { float occlusion = 0.0f; - // 在半球方向随机采样 for (int s = 0; s < numSamples; ++s) { - // 生成随机采样方向(在法线朝向的半球内) float theta = 2.0f * M_PI * (float)(rand() % 10000) / 10000.0f; float phi = acos((float)(rand() % 10000) / 10000.0f); - // 采样点偏移 float radius = (float)(rand() % 10000) / 10000.0f * sampleRadius; int sx = x + (int)(radius * sin(phi) * cos(theta)); int sy = y + (int)(radius * sin(phi) * sin(theta)); - // 边界检查 - if (sx < 0 || sx >= textureSize || sy < 0 || sy >= textureSize) continue; + if (sx < 0 || sx >= aoTextureSize || sy < 0 || sy >= aoTextureSize) continue; float sampleDepth = depthMap.at(sy, sx); if (sampleDepth >= std::numeric_limits::max() - 1.0f) continue; - // 判断采样点是否遮挡了中心点 float depthDiff = sampleDepth - centerDepth; - if (depthDiff > bias && depthDiff < sampleRadius * 0.1f) { - // 采样点在中心点前方且距离适中,算作遮挡 - occlusion += 1.0f / (1.0f + depthDiff * depthDiff); + if (depthDiff > bias && depthDiff < 0.5f) { + occlusion += 1.0f / (1.0f + depthDiff * depthDiff * 400.0f); } } - // 归一化 AO 值 float ao = 1.0f - occlusion / (float)numSamples * intensity; ao = std::max(0.0f, std::min(1.0f, ao)); - aoMap.at(y, x) = ao; + aoMapSmall.at(y, x) = ao; } } - // 3. 高斯模糊 AO 贴图,使其更平滑 - cv::GaussianBlur(aoMap, aoMap, cv::Size(0, 0), 3.0); + cv::GaussianBlur(aoMapSmall, aoMapSmall, cv::Size(0, 0), 2.0); - // 4. 用 AO 贴图做暗部提亮 - float strength = 0.35f; // 提亮强度 - float threshold = 0.60f; // AO 阈值,低于此值才提亮 - float exponent = 1.5f; // 非线性指数 + // 保存 AO 贴图(1024 版本) + { + cv::Mat aoVis; + aoMapSmall.convertTo(aoVis, CV_8UC1, 255.0); + std::string aoPath = "/home/algo/Documents/openMVS/data/603454/out.603454/ao.jpg"; + cv::imwrite(aoPath, aoVis); + DEBUG_EXTRA("[AO] AO map saved to: %s", aoPath.c_str()); + } + // ★★★ 关键修改:把 AO 上采样回 8192,然后在原图上做提亮 ★★★ + cv::Mat aoMapFull; + cv::resize(aoMapSmall, aoMapFull, cv::Size(textureSize, textureSize), 0, 0, cv::INTER_LINEAR); + + // 把 atlas 转成浮点,在原分辨率上做提亮 cv::Mat atlasFloat; atlas.convertTo(atlasFloat, CV_32FC3, 1.0f / 255.0f); + // 暗部提亮(8192 分辨率) + float strength = 0.28f; + float threshold = 0.58f; + float exponent = 2.0f; + for (int y = 0; y < textureSize; ++y) { for (int x = 0; x < textureSize; ++x) { - float aoVal = aoMap.at(y, x); - - // 只处理 AO 值低于阈值的区域 + float aoVal = aoMapFull.at(y, x); if (aoVal >= threshold) continue; - // 计算提亮权重 float darkness = (threshold - aoVal) / threshold; darkness = std::pow(darkness, exponent); float weight = darkness * strength; - - if (weight < 0.01f) continue; + if (weight < 0.005f) continue; cv::Vec3f color = atlasFloat.at(y, x); - - // 提亮:color = color + weight * (1 - color) color[0] = color[0] + weight * (1.0f - color[0]); color[1] = color[1] + weight * (1.0f - color[1]); color[2] = color[2] + weight * (1.0f - color[2]); - - // 钳制 color[0] = std::min(1.0f, std::max(0.0f, color[0])); color[1] = std::min(1.0f, std::max(0.0f, color[1])); color[2] = std::min(1.0f, std::max(0.0f, color[2])); - atlasFloat.at(y, x) = color; } } + // 转回 8UC3 写回 atlas atlasFloat.convertTo(atlas, CV_8UC3, 255.0f); DEBUG_EXTRA("[AO] In-engine AO lightening applied: strength=%.2f, threshold=%.2f", strength, threshold); } - return true; }