fix(vtk): 四叉树加视锥剔除(只细分视野内瓦片,根治预算被屏幕外块耗尽致粗块/拉近无图/地形丢失)+焦点自校正平面方向

This commit is contained in:
gaozheng 2026-06-17 14:18:04 +08:00
parent ca847f5a77
commit 0ac2765fd7
2 changed files with 23 additions and 1 deletions

View File

@ -180,6 +180,17 @@ void TileBasemap::refineTile(int z, int x, int y, std::set<long long>& out, int&
const geopro::render::LonLatBox b = geopro::render::tileBounds(z, x, y); const geopro::render::LonLatBox b = geopro::render::tileBounds(z, x, y);
const auto sw = frame_->toLocal(b.south, b.west); const auto sw = frame_->toLocal(b.south, b.west);
const auto ne = frame_->toLocal(b.north, b.east); const auto ne = frame_->toLocal(b.north, b.east);
// 视锥剔除:瓦片 AABB 全在某视锥面外侧 → 不在视野内,直接丢弃(否则会在屏幕外乱细分耗尽预算)。
const double zmin = -1000.0, zmax = 1000.0; // 地形起伏远小于瓦片尺度,给宽松 z 带
for (int p = 0; p < 6; ++p) {
const double* pl = &frustum_[p * 4]; // 内法向:内侧 a·x+b·y+c·z+d ≥ 0
const double vx = pl[0] >= 0 ? ne.x : sw.x; // 取最朝法向的角点(p-vertex)
const double vy = pl[1] >= 0 ? ne.y : sw.y;
const double vz = pl[2] >= 0 ? zmax : zmin;
if (pl[0] * vx + pl[1] * vy + pl[2] * vz + pl[3] < 0.0) return; // 全在外 → 剔除
}
const double cx = (sw.x + ne.x) * 0.5, cy = (sw.y + ne.y) * 0.5; // 瓦片中心(局部米) const double cx = (sw.x + ne.x) * 0.5, cy = (sw.y + ne.y) * 0.5; // 瓦片中心(局部米)
const double g = std::max(std::abs(ne.x - sw.x), std::abs(ne.y - sw.y)); // 瓦片地面尺寸(米) const double g = std::max(std::abs(ne.x - sw.x), std::abs(ne.y - sw.y)); // 瓦片地面尺寸(米)
@ -219,6 +230,16 @@ void TileBasemap::refresh() {
projParallel_ = cam->GetParallelProjection(); projParallel_ = cam->GetParallelProjection();
projK_ = projParallel_ ? H / (2.0 * std::max(1.0, cam->GetParallelScale())) projK_ = projParallel_ ? H / (2.0 * std::max(1.0, cam->GetParallelScale()))
: H / (2.0 * std::tan(cam->GetViewAngle() * 0.5 * kPi / 180.0)); : H / (2.0 * std::tan(cam->GetViewAngle() * 0.5 * kPi / 180.0));
const double aspect = (sz && sz[1] > 0) ? double(sz[0]) / double(sz[1]) : 1.0;
cam->GetFrustumPlanes(aspect, frustum_); // 6 视锥面(供 refineTile 剔除屏幕外瓦片)
// 用焦点(必在视锥内)统一各面方向为"内侧≥0",规避 VTK 法向内/外约定差异(否则可能全剔成黑屏)。
double fp[3];
cam->GetFocalPoint(fp);
for (int p = 0; p < 6; ++p) {
double* pl = &frustum_[p * 4];
if (pl[0] * fp[0] + pl[1] * fp[1] + pl[2] * fp[2] + pl[3] < 0.0)
for (int k = 0; k < 4; ++k) pl[k] = -pl[k];
}
// 四叉树:从数据中心一圈粗根块出发,按屏幕误差细分 → 近细远粗、铺满视野,无单层级盲区。 // 四叉树:从数据中心一圈粗根块出发,按屏幕误差细分 → 近细远粗、铺满视野,无单层级盲区。
desired_.clear(); desired_.clear();

View File

@ -71,10 +71,11 @@ private:
std::set<long long> inFlight_; // 在途瓦片(续到起伏/平面最终落地) std::set<long long> inFlight_; // 在途瓦片(续到起伏/平面最终落地)
std::map<long long, QImage> demCache_; // DEM 块缓存(key=DEMz/x/y),跨隐藏/重选复用 std::map<long long, QImage> demCache_; // DEM 块缓存(key=DEMz/x/y),跨隐藏/重选复用
std::map<long long, vtkSmartPointer<vtkTexture>> texCache_; // 影像纹理缓存,重选/缩放回看免重拉 std::map<long long, vtkSmartPointer<vtkTexture>> texCache_; // 影像纹理缓存,重选/缩放回看免重拉
// 四叉树当前帧相机参数(refresh 写, refineTile 读):相机位置 + 投影系数 // 四叉树当前帧相机参数(refresh 写, refineTile 读):相机位置 + 投影系数 + 视锥 6 面
double camX_ = 0, camY_ = 0, camZ_ = 0; double camX_ = 0, camY_ = 0, camZ_ = 0;
double projK_ = 1.0; double projK_ = 1.0;
bool projParallel_ = false; bool projParallel_ = false;
double frustum_[24] = {0}; // 6 个视锥平面(内法向)AABB 全在某面外则剔除
struct PendingGet { QString url; std::function<void(QNetworkReply*)> cb; }; struct PendingGet { QString url; std::function<void(QNetworkReply*)> cb; };
std::deque<PendingGet> netQueue_; // 限并发请求队列(防瓦片暴发饱和卡死) std::deque<PendingGet> netQueue_; // 限并发请求队列(防瓦片暴发饱和卡死)
int netInFlight_ = 0; int netInFlight_ = 0;