wip(render): 深度方向修正(取负) + 离屏渲染验证工具(render_verify->PNG)

诚实记录: 经离屏PNG肉眼核对, 帘面渲染仍不正确(扭曲飘带/俯视空白), '2D=俯视帘面'模型不成立(竖直帘面俯视仅一条发丝线), 需独立的地图线actor。色阶偏蓝(线性LUT)。详见后续 STATUS。
之前几版'已验证'仅进程级冒烟, 未看像素 -> 漏掉剖面倒置等问题。
This commit is contained in:
gaozheng 2026-06-07 23:08:15 +08:00
parent 7713271557
commit 96fac3313b
4 changed files with 103 additions and 10 deletions

View File

@ -56,7 +56,8 @@ vtkSmartPointer<vtkActor> buildCurtain(const geopro::core::Grid& g,
py = 0.0; py = 0.0;
} }
const vtkIdType id = static_cast<vtkIdType>(j) * nx + i; const vtkIdType id = static_cast<vtkIdType>(j) * nx + i;
points->SetPoint(id, px, py, g.y[j]); // z=深度/高程VTK Z 向上 // g.y 是深度(越大越深)VTK Z 向上 → 取负,使深部在下、浅部在上(剖面不倒置)。
points->SetPoint(id, px, py, -g.y[j]);
sc->SetValue(id, g.valueAt(i, j)); sc->SetValue(id, g.valueAt(i, j));
} }
} }

View File

@ -3,11 +3,12 @@
#include <vtkBandedPolyDataContourFilter.h> #include <vtkBandedPolyDataContourFilter.h>
#include <vtkDataSetSurfaceFilter.h> #include <vtkDataSetSurfaceFilter.h>
#include <vtkDoubleArray.h> #include <vtkDoubleArray.h>
#include <vtkImageData.h>
#include <vtkNew.h> #include <vtkNew.h>
#include <vtkPointData.h> #include <vtkPointData.h>
#include <vtkPoints.h>
#include <vtkPolyDataMapper.h> #include <vtkPolyDataMapper.h>
#include <vtkProperty.h> #include <vtkProperty.h>
#include <vtkStructuredGrid.h>
#include "ColorLutBuilder.hpp" #include "ColorLutBuilder.hpp"
@ -31,18 +32,23 @@ GridActors buildGridContour(const geopro::core::Grid& g, const geopro::core::Col
return GridActors{}; return GridActors{};
} }
vtkNew<vtkImageData> img; // 用显式坐标的 vtkStructuredGrid不假设等距;y 取负使深部在下、剖面不倒置)。
img->SetDimensions(nx, ny, 1); vtkNew<vtkStructuredGrid> sgrid;
img->SetOrigin(g.x[0], g.y[0], 0.0); sgrid->SetDimensions(nx, ny, 1);
img->SetSpacing(g.x[1] - g.x[0], g.y[1] - g.y[0], 1.0); vtkNew<vtkPoints> points;
points->SetNumberOfPoints(static_cast<vtkIdType>(nx) * ny);
vtkNew<vtkDoubleArray> sc; vtkNew<vtkDoubleArray> sc;
sc->SetName("v"); sc->SetName("v");
sc->SetNumberOfTuples(static_cast<vtkIdType>(nx) * ny); sc->SetNumberOfTuples(static_cast<vtkIdType>(nx) * ny);
for (int j = 0; j < ny; ++j) for (int j = 0; j < ny; ++j)
for (int i = 0; i < nx; ++i) for (int i = 0; i < nx; ++i) {
sc->SetValue(static_cast<vtkIdType>(j) * nx + i, g.valueAt(i, j)); // i 最快 const vtkIdType id = static_cast<vtkIdType>(j) * nx + i; // i 最快
img->GetPointData()->SetScalars(sc); points->SetPoint(id, g.x[i], -g.y[j], 0.0); // y=深度→取负(向下)
sc->SetValue(id, g.valueAt(i, j));
}
sgrid->SetPoints(points);
sgrid->GetPointData()->SetScalars(sc);
// vmin/vmax 来自 Grid若退化==)则用数据极值兜底,避免 LUT/contour 退化。 // vmin/vmax 来自 Grid若退化==)则用数据极值兜底,避免 LUT/contour 退化。
double vmin = g.vmin, vmax = g.vmax; double vmin = g.vmin, vmax = g.vmax;
@ -60,7 +66,7 @@ GridActors buildGridContour(const geopro::core::Grid& g, const geopro::core::Col
auto lut = buildLut(cs, vmin, vmax, kLutLevels); auto lut = buildLut(cs, vmin, vmax, kLutLevels);
vtkNew<vtkDataSetSurfaceFilter> surf; vtkNew<vtkDataSetSurfaceFilter> surf;
surf->SetInputData(img); surf->SetInputData(sgrid);
vtkNew<vtkBandedPolyDataContourFilter> banded; vtkNew<vtkBandedPolyDataContourFilter> banded;
banded->SetInputConnection(surf->GetOutputPort()); banded->SetInputConnection(surf->GetOutputPort());

View File

@ -13,3 +13,9 @@ find_package(VTK REQUIRED COMPONENTS
add_executable(grid_contour_spike grid_contour_spike.cpp) add_executable(grid_contour_spike grid_contour_spike.cpp)
target_link_libraries(grid_contour_spike PRIVATE ${VTK_LIBRARIES} nlohmann_json::nlohmann_json) target_link_libraries(grid_contour_spike PRIVATE ${VTK_LIBRARIES} nlohmann_json::nlohmann_json)
vtk_module_autoinit(TARGETS grid_contour_spike MODULES ${VTK_LIBRARIES}) vtk_module_autoinit(TARGETS grid_contour_spike MODULES ${VTK_LIBRARIES})
# / PNG 2D/3D
add_executable(render_verify render_verify.cpp)
target_link_libraries(render_verify PRIVATE
geopro_render geopro_data geopro_core ${VTK_LIBRARIES})
vtk_module_autoinit(TARGETS render_verify MODULES ${VTK_LIBRARIES})

View File

@ -0,0 +1,80 @@
// 离屏渲染验证:把真实数据渲成 PNG肉眼核对方向/2D-3D 差异(不靠"进程活着"自欺)。
// 产出(D:/dev/spike_data/):
// verify_section.png —— 平面反演剖面(#18, 数据详情),应浅部在上、深部在下
// verify_curtain_top.png —— 帘面俯视(二维),应看到一条沿测线的带(像地图线)
// verify_curtain_3d.png —— 帘面透视(三维),应看到立着的彩色断面墙
#include <fstream>
#include <sstream>
#include <string>
#include <numeric>
#include <vtkActor.h>
#include <vtkNew.h>
#include <vtkPNGWriter.h>
#include <vtkRenderWindow.h>
#include <vtkRenderer.h>
#include <vtkWindowToImageFilter.h>
#include "parse/SampleParsers.hpp"
#include "geo/GeoLocalFrame.hpp"
#include "actors/GridContourActor.hpp"
#include "actors/CurtainActor.hpp"
#include "ColorLutBuilder.hpp"
#include "CameraPreset.hpp"
static std::string slurp(const char* p) {
std::ifstream f(p);
std::stringstream s; s << f.rdbuf(); return s.str();
}
static void renderToPng(vtkRenderer* ren, const char* path, int w, int h) {
vtkNew<vtkRenderWindow> rw;
rw->SetOffScreenRendering(1);
rw->AddRenderer(ren);
rw->SetSize(w, h);
rw->Render();
vtkNew<vtkWindowToImageFilter> w2i; w2i->SetInput(rw);
vtkNew<vtkPNGWriter> png; png->SetFileName(path);
png->SetInputConnection(w2i->GetOutputPort()); png->Write();
}
int main() {
using namespace geopro;
const std::string dir = "D:/dev/spike_data/";
core::Grid g = data::parseGrid(slurp((dir + "grid.json").c_str()));
core::ColorScale cs = data::parseColorScale(slurp((dir + "colorbar.json").c_str()));
// 1) 平面剖面 (#18) — 俯视看 XY 平面(face-on)
{
auto a = render::buildGridContour(g, cs);
vtkNew<vtkRenderer> ren; ren->SetBackground(1, 1, 1);
if (a.bands) ren->AddActor(a.bands);
if (a.edges) ren->AddActor(a.edges);
render::applyTop2D(ren);
renderToPng(ren, (dir + "verify_section.png").c_str(), 1100, 320);
}
// GeoLocalFrame原点取 lat/lon 均值
double lat0 = std::accumulate(g.lat.begin(), g.lat.end(), 0.0) / (g.lat.empty() ? 1 : g.lat.size());
double lon0 = std::accumulate(g.lon.begin(), g.lon.end(), 0.0) / (g.lon.empty() ? 1 : g.lon.size());
core::GeoLocalFrame frame(lat0, lon0);
// 2) 帘面俯视(二维)
{
auto a = render::buildCurtain(g, cs, frame);
vtkNew<vtkRenderer> ren; ren->SetBackground(1, 1, 1);
ren->AddActor(a);
render::applyTop2D(ren);
renderToPng(ren, (dir + "verify_curtain_top.png").c_str(), 700, 500);
}
// 3) 帘面透视(三维)
{
auto a = render::buildCurtain(g, cs, frame);
vtkNew<vtkRenderer> ren; ren->SetBackground(1, 1, 1);
ren->AddActor(a);
render::applyFree3D(ren);
renderToPng(ren, (dir + "verify_curtain_3d.png").c_str(), 700, 500);
}
std::printf("RENDER_VERIFY_DONE grid=%dx%d lat0=%.5f lon0=%.5f\n", g.nx(), g.ny(), lat0, lon0);
return 0;
}