快速光线和多边形相交

Fast Ray and Polygon Intersection

本文关键字:多边形      更新时间:2023-10-16

我正在编写我自己的小游戏,它应该有这里描述的可见性效果。我的世界由多边形组成,每个多边形都有一个边列表(按CW排序(。我现在想要(如文章中所述(将光线投射到多边形的边缘,找到交点并检索定义可见区域的多边形。

因此,我为Vectors、Points、Edges和Polygons编写了一个类,并调整了交集算法,使其能够与我的代码一起使用。

然后我测试了它,一切都很好,但当我在for循环中运行交集算法来模拟处理的大量边缘(从100开始,直到1000(时,fps急剧下降,100个边缘"仅"300fps(之前是3000(,我认为300个边缘下降到60以下。这对我来说似乎是一个很大的下降,因为我想在我的Lightsource中重用这段代码,然后我想我会很快想出处理300多个Edges的方法,它应该在功能较弱的处理器上快速运行(我得到了一个xeon e1230v3(。

我发现,只调用EdgeIntersection程序运行速度会快很多倍,但我肯定需要循环通过多边形中的边,所以这是不可能的。

我的源代码:

Vector.h/.cpp:具有两个浮点(X,Y(、getters&旋转设置器

Vertex.h/.cpp:具有位置向量的基点类,getters&setters和一个布尔值,用于指示它是否为交集顶点

Edge.h/.cpp具有起始/结束顶点、getters&setters和旋转函数(使用Vector.rerotate(((

多边形.h:

#pragma once
#include <vector>
#include "Edge.h"
namespace geo
{
class Polygon
{
private:
    std::vector<Edge> edges;
public:
    Polygon();
    Polygon(std::vector<Edge> edges);
    ~Polygon();
    std::vector<Edge> getEdges();
    Edge getEdge(int index);
    int getEdgeCount();
    void setEdges(std::vector<Edge> edges);
    void setEdge(Edge e, int index);
    void addEdge(Edge e);
    void removeEdge(int index);
};
}

Ray.h:

#pragma once
#include "Vertex.h"
class Ray
{
private:
    geo::Vertex origin;
    geo::Vector dir;
public:
    Ray();
    Ray(geo::Vertex origin, geo::Vector dir);
    ~Ray();
    geo::Vertex getOrigin();
    geo::Vector getDirection();
    void setOrigin(geo::Vertex origin);
    void setDirection(geo::Vector dir);
};

LightModule.h:

#pragma once
#include "Polygon.h"
#include "Ray.h"
class LightModule
{
private:
//List of blocking Polygons
std::vector<geo::Polygon>* blockingPolygons;
std::vector<Ray> rays;
geo::Polygon bounds;
geo::Polygon visible;
/*geo::Polygon blocked;*/
//HitDetection Class later
geo::Vertex getIntersection(Ray r, geo::Edge* e);
geo::Vertex getClosestIntersection(Ray r, geo::Polygon *p);
public:
LightModule();
LightModule(std::vector<geo::Polygon>* blockingPolygons);
~LightModule();
//Set the Blocking Polygons
void setBlockingPolygons(std::vector<geo::Polygon>* blockingPolygons);
geo::Vertex callCI(Ray r, geo::Polygon* p);
geo::Vertex callI(Ray r, geo::Edge* e);
//Cast Rays towards Vertecies and store them in rays
void updateRays();
//Update Visibility Polygon
void updateVisible();
//Return Visibility Polygon
geo::Polygon* getVisible();
};

LightMModule.cpp:

#include "LightModule.h"

LightModule::LightModule()
{
rays.clear();
}
LightModule::LightModule(std::vector<geo::Polygon>* blockingPolygons)
{
this->blockingPolygons = blockingPolygons;
rays.clear();
}
LightModule::~LightModule()
{
}
void LightModule::setBlockingPolygons(std::vector<geo::Polygon>* blockingPolygons)
{
this->blockingPolygons = blockingPolygons;
}
//Test-cast a Ray (will follow mouse in the Test)
void LightModule::updateRays()
{
Ray r(geo::Vertex(geo::Vector(200, 100)), geo::Vector(-100, 0));
rays.push_back(r);
}
void LightModule::updateVisible()
{
}
//Both for Testing will later be part of a seperate class
geo::Vertex LightModule::callCI(Ray r, geo::Polygon *p)
{
return this->getClosestIntersection(r, p);
}
geo::Vertex LightModule::callI(Ray r, geo::Edge* e)
{
return this->getIntersection(r, e);
}

//TEST
geo::Vertex LightModule::getIntersection(Ray r, geo::Edge* e)
{
geo::Vertex v;
v.setIntersectVert(false);
float r_px = r.getOrigin().getPosition().getX();
float r_py = r.getOrigin().getPosition().getY();
float r_dx = r.getDirection().getX();
float r_dy = r.getDirection().getY();
float s_px = e->getOrigin().getPosition().getX();
float s_py = e->getOrigin().getPosition().getY();
float s_dx = e->getDirection().getX();
float s_dy = e->getDirection().getY();
float r_mag = sqrt(r_dx*r_dx + r_dy*r_dy);
float s_mag = sqrt(s_dx*s_dx + s_dy*s_dy);
if (r_dx / r_mag == s_dx / s_mag && r_dy / r_mag == s_dy / s_mag)
{
    return v;
}
float T2 = (r_dx*(s_py - r_py) + r_dy*(r_px - s_px)) / (s_dx*r_dy - s_dy*r_dx);
float T1 = (s_px + s_dx*T2 - r_px) / r_dx;
if (T1 < 0 /*|| T1 > 1 For Lines*/)
{
    return v;
}
if (T2 < 0 || T2 > 1)
{
    return v;
}
v.setIntersectVert(true);
v.setPosition(geo::Vector(r_px + r_dx*T1, r_py + r_dy*T1));
return v;
}
geo::Vertex LightModule::getClosestIntersection(Ray r, geo::Polygon *p)
{
geo::Vertex v;
v.setIntersectVert(false);
geo::Vertex v_nearest(geo::Vector(0, 0));
v_nearest.setIntersectVert(false);
geo::Vector h1;
geo::Vector h2;
for (int i = 0; i < p->getEdges().size(); i++)
{
    v = this->getIntersection(r, &p->getEdges().at(i));
    h1.setX(v.getPosition().getX() - r.getOrigin().getPosition().getX());
    h1.setY(v.getPosition().getY() - r.getOrigin().getPosition().getY());
    h2.setX(v_nearest.getPosition().getX() - r.getOrigin().getPosition().getX());
    h2.setY(v_nearest.getPosition().getY() -                     r.getOrigin().getPosition().getY());
    if (i < 1)
        v_nearest = v;
    else if (v.isIntersectVert() == true && h1.getLength() < h2.getLength())
    {
        v_nearest = v;
    }
}
return v_nearest;
}

对于测试,我创建了一个多边形一个LightModule,并调用updateRays,然后调用辅助函数callCI((。我知道当我必须级联我的getter和setter时,我的代码会变得非常混乱,我必须解决这个问题,但对于其他人来说,我希望一切都可以理解,如果不能随意询问的话。刚刚提到的是,我用顶点阵列测试绘制我的对象,但我不需要相交过程的图形输出,我只需要可见的多边形。

再次指出:我需要一种更快的方法来找到射线和多边形之间的交点,因为我不知道我的代码中是否做错了什么,所以我把它都发布在这里,这样有人可能会帮助我提高代码的效率,或者向我展示一种不同的方法来解决我的问题。

祝你今天愉快,谢谢你的回答:(Paul

编辑:首先对多边形进行三角测量,然后进行射线三角相交测试,会有意义地更快吗?

我不能谈论算法(这可能是你所需要的(,但我会立即思考如何加快你的速度。

首先,您可以定义所有您的gettersetterinline(将它们放在头中的类中,而不是单独的源文件中(,这样编译器就可以优化函数调用。

然后这些变化可能会为你购买一些帧:

// make sure your getters and setters are inline so the compiler
// can optimize them away
geo::Vertex LightModule::getClosestIntersection(Ray r, geo::Polygon* p)
{
    geo::Vertex v;
    v.setIntersectVert(false);
    geo::Vector h1;
    geo::Vector h2;
    // cache these
    Vector ray_position = r.getOrigin().getPosition();
    geo::Vertex v_nearest(geo::Vector(0, 0));
    v_nearest.setIntersectVert(false);
    // cache size (don't dereference each time)
    size_t size = p->getEdges().size();
    // avoid acces violation
    if(!size)
        return v_nearest;
    // preset item 0
    v_nearest = this->getIntersection(r, &p->getEdges()[0]);
    // start from 1 not 0
    for(int i = 1; i < size; i++)
    {
        // don't use at() its slower
        // v = this->getIntersection(r, &p->getEdges().at(i));
        v = this->getIntersection(r, &p->getEdges()[i]);
        // used cached ray position rather than call functions
        h1.setX(v.getPosition().getX() - ray_position.getX());
        h1.setY(v.getPosition().getY() - ray_position.getY());
        h2.setX(v_nearest.getPosition().getX() - ray_position.getX());
        h2.setY(v_nearest.getPosition().getY() - ray_position.getY());
        // this if not needed because presetting item 0
        //if(i < 1)
        //  v_nearest = v;
        if(v.isIntersectVert() == true && h1.getLength() < h2.getLength())
        {
            v_nearest = v;
        }
    }
    return v_nearest;
}

我通过在循环之前计算0项并从1开始循环,删除了其中一个if语句,其余的只是缓存一个常用的值,并避免at(),因为它进行绑定检查,所以速度较慢。