diff --git a/libs/MVS/SceneTexture.cpp b/libs/MVS/SceneTexture.cpp index 8813436..558629a 100644 --- a/libs/MVS/SceneTexture.cpp +++ b/libs/MVS/SceneTexture.cpp @@ -17174,166 +17174,64 @@ if (patchID == NO_ID) { } } -// ========== 内置 AO 计算与暗部提亮 ========== + // ========== 内置 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..."); - - 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)); - - for (size_t fi = 0; fi < scene.mesh.faces.size(); ++fi) { - const Face& face = scene.mesh.faces[fi]; - - Point2f uv[3]; - Point3f pos[3]; - for (int j = 0; j < 3; ++j) { - uv[j] = scene.mesh.faceTexcoords[fi * 3 + j]; - 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; - - 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 * 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 / 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; - - Point3f P = pos[0] * b0 + pos[1] * b1 + pos[2] * b2; - 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); - } - } - } - } - - // SSAO 计算 - cv::Mat aoMapSmall(aoTextureSize, aoTextureSize, CV_32FC1, cv::Scalar(1.0f)); - - int sampleRadius = 8; - int numSamples = 72; // 恢复一些采样数 - float bias = 0.002f; - float intensity = 2.5f; - - 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; - - cv::Vec3f centerNormal = normalMap.at(y, x); - if (centerNormal[0] == 0 && centerNormal[1] == 0 && centerNormal[2] == 0) continue; - - 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 >= 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 < 0.5f) { - occlusion += 1.0f / (1.0f + depthDiff * depthDiff * 400.0f); - } - } - - float ao = 1.0f - occlusion / (float)numSamples * intensity; - ao = std::max(0.0f, std::min(1.0f, ao)); - aoMapSmall.at(y, x) = ao; - } - } - - cv::GaussianBlur(aoMapSmall, aoMapSmall, cv::Size(0, 0), 2.0); - - // 保存 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 = 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.005f) continue; - - cv::Vec3f color = atlasFloat.at(y, x); - 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); + std::vector().swap(rcPatches); + std::vector().swap(m_texelPatchID); + DEBUG_EXTRA("[AO] Freed RC patches and texelPatchID memory"); } - return true; + if (false) + { + DEBUG_EXTRA("[AO] Loading pre-baked AO from Blender..."); + + std::string aoPath = "/home/algo/Documents/openMVS/data/603454/out.603454/aon.jpg"; + cv::Mat aoMapSmall = cv::imread(aoPath, cv::IMREAD_GRAYSCALE); + if (aoMapSmall.empty()) { + DEBUG_EXTRA("[AO] Failed to load AO map from %s, skipping", aoPath.c_str()); + } else { + cv::resize(aoMapSmall, aoMapSmall, cv::Size(1024, 1024), 0, 0, cv::INTER_LINEAR); + aoMapSmall.convertTo(aoMapSmall, CV_32FC1, 1.0f / 255.0f); + + cv::Mat aoMapFull; + cv::resize(aoMapSmall, aoMapFull, cv::Size(textureSize, textureSize), 0, 0, cv::INTER_LINEAR); + + cv::Mat atlasFloat; + atlas.convertTo(atlasFloat, CV_32FC3, 1.0f / 255.0f); + + float strength = 0.38f; + float aoDarkThresh = 0.62f; + float exponent = 0.92f; + + for (int y = 0; y < textureSize; ++y) { + for (int x = 0; x < textureSize; ++x) { + float aoVal = aoMapFull.at(y, x); + if (aoVal >= aoDarkThresh) continue; + + cv::Vec3f color = atlasFloat.at(y, x); + + float lum = 0.299f * color[2] + 0.587f * color[1] + 0.114f * color[0]; + if (lum < 0.04f) continue; // 只保护最暗的材质 + + // ★★★ 线性映射,不加 cap ★★★ + float factor = (aoDarkThresh - aoVal) / aoDarkThresh; + factor = std::pow(factor, exponent); + float add = factor * strength; + + color[0] = std::min(1.0f, color[0] + add); + color[1] = std::min(1.0f, color[1] + add); + color[2] = std::min(1.0f, color[2] + add); + atlasFloat.at(y, x) = color; + } + } + + atlasFloat.convertTo(atlas, CV_8UC3, 255.0f); + DEBUG_EXTRA("[AO] AO-based lightening applied using Blender AO map"); + } + } + + return true; } bool MeshTexture::BuildSeamVertsFromEdges()