暗部提亮测试代码

This commit is contained in:
hesuicong committed 2026-10-31 15:56:07 +08:00
1 parent 2f88885ccc
commit d4330afd6f
1 file changed
+164
+164
View File
@@ -17174,6 +17174,170 @@ if (patchID == NO_ID) {
}
}
// ========== 内置 AO 计算与暗部提亮(无需外部图片)==========
if (false)
{
DEBUG_EXTRA("[AO] Computing ambient occlusion directly...");
// 1. 构建深度图(用顶点位置的 UV 空间投影)
cv::Mat depthMap(textureSize, textureSize, CV_32FC1, std::numeric_limits<float>::max());
cv::Mat normalMap(textureSize, textureSize, CV_32FC3, cv::Vec3f(0, 0, 0));
cv::Mat faceIDMap(textureSize, textureSize, CV_32SC1, -1);
// 遍历所有面,在 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) {
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;
}
// 计算 UV 包围盒
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));
// 对每个像素做重心坐标插值
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 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)); // 到原点的距离作为深度
// 记录最小深度(离相机最近的面)
if (depth < depthMap.at<float>(py, px)) {
depthMap.at<float>(py, px) = depth;
normalMap.at<cv::Vec3f>(py, px) = cv::Vec3f(normal.x, normal.y, normal.z);
faceIDMap.at<int>(py, px) = (int)fi;
}
}
}
}
// 2. SSAO 计算
cv::Mat aoMap(textureSize, textureSize, CV_32FC1, cv::Scalar(1.0f)); // 初始全白(无遮挡)
int sampleRadius = 8; // 采样半径(像素)
int numSamples = 24; // 每个像素的采样点数
float bias = 0.005f; // 深度偏差,防止自身遮挡
float intensity = 1.5f; // AO 强度
for (int y = 0; y < textureSize; ++y) {
for (int x = 0; x < textureSize; ++x) {
float centerDepth = depthMap.at<float>(y, x);
if (centerDepth >= std::numeric_limits<float>::max() - 1.0f) continue;
cv::Vec3f centerNormal = normalMap.at<cv::Vec3f>(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 >= textureSize || sy < 0 || sy >= textureSize) continue;
float sampleDepth = depthMap.at<float>(sy, sx);
if (sampleDepth >= std::numeric_limits<float>::max() - 1.0f) continue;
// 判断采样点是否遮挡了中心点
float depthDiff = sampleDepth - centerDepth;
if (depthDiff > bias && depthDiff < sampleRadius * 0.1f) {
// 采样点在中心点前方且距离适中,算作遮挡
occlusion += 1.0f / (1.0f + depthDiff * depthDiff);
}
}
// 归一化 AO 值
float ao = 1.0f - occlusion / (float)numSamples * intensity;
ao = std::max(0.0f, std::min(1.0f, ao));
aoMap.at<float>(y, x) = ao;
}
}
// 3. 高斯模糊 AO 贴图,使其更平滑
cv::GaussianBlur(aoMap, aoMap, cv::Size(0, 0), 3.0);
// 4. 用 AO 贴图做暗部提亮
float strength = 0.35f; // 提亮强度
float threshold = 0.60f; // AO 阈值,低于此值才提亮
float exponent = 1.5f; // 非线性指数
cv::Mat atlasFloat;
atlas.convertTo(atlasFloat, CV_32FC3, 1.0f / 255.0f);
for (int y = 0; y < textureSize; ++y) {
for (int x = 0; x < textureSize; ++x) {
float aoVal = aoMap.at<float>(y, x);
// 只处理 AO 值低于阈值的区域
if (aoVal >= threshold) continue;
// 计算提亮权重
float darkness = (threshold - aoVal) / threshold;
darkness = std::pow(darkness, exponent);
float weight = darkness * strength;
if (weight < 0.01f) continue;
cv::Vec3f color = atlasFloat.at<cv::Vec3f>(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<cv::Vec3f>(y, x) = color;
}
}
atlasFloat.convertTo(atlas, CV_8UC3, 255.0f);
DEBUG_EXTRA("[AO] In-engine AO lightening applied: strength=%.2f, threshold=%.2f",
strength, threshold);
}
return true;
}