Graphics 如何根据高度图计算可见区域?

Graphics 如何根据高度图计算可见区域?,graphics,geometry,Graphics,Geometry,我有一张高度图。我想有效地计算在任何给定的位置和高度上,从眼睛可以看到其中的哪些瓷砖 表明高度贴图优于将地形转换为某种网格,但它们使用Bresenhams对网格进行采样 如果我采用这种方法,我必须为地图上的每一块瓷砖画一条布列森厄姆视线线。我突然想到,如果你在远离眼睛的地方向外填充,应该可以重复使用大部分计算并在一次过程中计算高度图——也许是扫描线填充的方法 但我无法理解这种逻辑。逻辑是什么 这是一张高度图,上面画着一个从特定的有利位置(绿色立方体)(“分水岭”中的“视野”)可以看到的区域:

我有一张高度图。我想有效地计算在任何给定的位置和高度上,从眼睛可以看到其中的哪些瓷砖

表明高度贴图优于将地形转换为某种网格,但它们使用Bresenhams对网格进行采样

如果我采用这种方法,我必须为地图上的每一块瓷砖画一条布列森厄姆视线线。我突然想到,如果你在远离眼睛的地方向外填充,应该可以重复使用大部分计算并在一次过程中计算高度图——也许是扫描线填充的方法

但我无法理解这种逻辑。逻辑是什么

这是一张高度图,上面画着一个从特定的有利位置(绿色立方体)(“分水岭”中的“视野”)可以看到的区域:

这是我提出的O(n)扫描图;我似乎与下面富兰克林和雷的方法的答案中给出的答案相同,只是在这种情况下,我从眼睛向外走,而不是沿着边界朝中心走;在我看来,我的方法将具有更好的缓存行为—即更快—并且使用更少的内存,因为它不必跟踪每个磁贴的矢量,只需记住扫描线的价值:

typedef std::vector<float> visbuf_t;

inline void map::_visibility_scan(const visbuf_t& in,visbuf_t& out,const vec_t& eye,int start_x,int stop_x,int y,int prev_y) {
    const int xdir = (start_x < stop_x)? 1: -1;
    for(int x=start_x; x!=stop_x; x+=xdir) {
        const int x_diff = abs(eye.x-x), y_diff = abs(eye.z-y);
        const bool horiz = (x_diff >= y_diff);
        const int x_step = horiz? 1: x_diff/y_diff;
        const int in_x = x-x_step*xdir; // where in the in buffer would we get the inner value?
        const float outer_d = vec2_t(x,y).distance(vec2_t(eye.x,eye.z));
        const float inner_d = vec2_t(in_x,horiz? y: prev_y).distance(vec2_t(eye.x,eye.z));
        const float inner = (horiz? out: in).at(in_x)*(outer_d/inner_d); // get the inner value, scaling by distance
        const float outer = height_at(x,y)-eye.y; // height we are at right now in the map, eye-relative
        if(inner <= outer) {
            out.at(x) = outer;
            vis.at(y*width+x) = VISIBLE;
        } else {
            out.at(x) = inner;
            vis.at(y*width+x) = NOT_VISIBLE;
        }
    }
}

void map::visibility_add(const vec_t& eye) {
    const float BASE = -10000; // represents a downward vector that would always be visible
    visbuf_t scan_0, scan_out, scan_in;
    scan_0.resize(width);
    vis[eye.z*width+eye.x-1] = vis[eye.z*width+eye.x] = vis[eye.z*width+eye.x+1] = VISIBLE;
    scan_0.at(eye.x) = BASE;
    scan_0.at(eye.x-1) = BASE;
    scan_0.at(eye.x+1) = BASE;
    _visibility_scan(scan_0,scan_0,eye,eye.x+2,width,eye.z,eye.z);
    _visibility_scan(scan_0,scan_0,eye,eye.x-2,-1,eye.z,eye.z);
    scan_out = scan_0;
    for(int y=eye.z+1; y<height; y++) {
        scan_in = scan_out;
        _visibility_scan(scan_in,scan_out,eye,eye.x,-1,y,y-1);
        _visibility_scan(scan_in,scan_out,eye,eye.x,width,y,y-1);
    }
    scan_out = scan_0;
    for(int y=eye.z-1; y>=0; y--) {
        scan_in = scan_out;
        _visibility_scan(scan_in,scan_out,eye,eye.x,-1,y,y+1);
        _visibility_scan(scan_in,scan_out,eye,eye.x,width,y,y+1);
    }
}
typedef std::vector visbuf\t;
内联空洞图::_可见性_扫描(常量visbuf_t&in、visbuf_t&out、常量vec_t&eye、int start_x、int stop_x、int y、int prev_y){
常量int xdir=(开始<停止)?1:-1;
对于(int x=start\u x;x!=stop\u x;x+=xdir){
常数int x_diff=abs(eye.x-x),y_diff=abs(eye.z-y);
常量布尔水平=(x_diff>=y_diff);
const int x_step=horiz?1:x_diff/y_diff;
const int in_x=x-x_step*xdir;//在in缓冲区的何处可以获得内部值?
常量float outer_d=vec2_t(x,y).distance(vec2_t(eye.x,eye.z));
const float inner_d=vec2_t(in_x,horiz?y:prev_y)。距离(vec2_t(eye.x,eye.z));
const float-inner=(horiz?out:in).at(in_x)*(outer_d/inner_d);//获取内部值,按距离缩放
const float outer=height_at(x,y)-eye.y;//我们现在在地图中的高度,与眼睛相对

如果(内部你想要的被称为扫描算法。基本上你向每个周界单元投射光线(Bresenham的),但是在你前进的过程中跟踪地平线,并将你经过的任何单元标记为可见或不可见(如果可见,更新光线的地平线)。这让你从天真方法的O(n^3)开始(单独测试nxn DEM的每个单元)到O(n^2)


本节第5.1节对算法进行了更详细的描述(如果您希望使用非常巨大的高度图,您可能会发现出于其他原因,这一点也很有趣)。

不知道如何获得O(n):有O(n)个周长单元,您必须执行O(n)操作以到达每个周界单元。请注意,一些论文讨论了N元素DEM(大小为sqrt(N)xsqrt(N)),在这种情况下,原始算法是O(N^(3/2)),扫描算法是O(N)。网格中有N个图块。对于扫描,每个图块都有两次访问-这是常数因子,因此是O(N)。对于breshenhams,每个图块都是O(N)-我们必须访问多达n个瓷砖才能走这条线(想象一个一端有眼睛的1x100网格),这样就接近O(n^2)(尽管有早期退出等,预期运行时间会更短)。