【问题标题】:Fast Ray and Polygon Intersection快射线与多边形相交
【发布时间】:2015-09-21 01:44:00
【问题描述】:

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

所以我为向量、点、边和多边形编写了一个类,并调整了交集算法,使其适用于我的代码。

然后我对其进行了测试,一切正常,但是当我在 for 循环中运行 Intersection 算法以模拟大量处理的边缘(从 100 开始,直到 1000)时,fps 急剧下降,只有 100 条边缘“ 300fps(之前是 3000 帧),而 300 帧我认为它跌破了 60 帧。这对我来说似乎是一个很大的下降,因为我想为我的光源重用这段代码,然后我想我会很快想出超过 300 个边缘的处理方式,它应该在功能不太强大的处理器上运行得很快(我有一个至强 e1230v3)。

我发现只调用 EdgeIntersection 程序运行速度要快很多倍,但我肯定需要循环遍历多边形中的边缘,所以这是没有选择的。

我的源代码:

Vector.h/.cpp:具有两个浮点数(X,Y)、getter&setter、旋转的基本 Vector 类

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

Edge.h/.cpp 基本 Edge 类,带有 start/end-Verticies、getter&setter 和旋转功能(使用 Vector.rotate())

多边形.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);
};
}

雷.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,然后调用 helper-Function callCI()。 我知道当我必须级联我的 getter 和 setter 时,我的代码会变得非常混乱,我必须解决这个问题,但对于其余的,我希望一切都是可以理解的,如果不能随意提问。顺便提一下,我用 Vertex-Arrays 测试绘制我的对象,但我不需要相交过程的图形输出,我只需要可见多边形。

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

祝您有美好的一天,感谢您的回答 :) 保罗

编辑:首先对我的多边形进行三角剖分然后进行 Ray-Triangle 相交测试会更快吗?

【问题讨论】:

  • 那么最好的方法是将每个多边形的边界框四等分,然后在必要时细分它?但我想我必须细分整个区域,因为光线也可能与不同的多边形相交
  • 编辑:性能的主要消耗似乎是我必须遍历多边形的最接近的交叉函数......

标签: c++ polygon sfml intersection raytracing


【解决方案1】:

我无法谈论算法(这可能是您需要的),但有一些关于加快您所拥有的东西的直接想法。

首先,您可以定义 all 您的 getterssetters inline(将它们放在标题中的类中,而不是单独的源文件),以便编译器可以优化函数调用。

那么这些更改可能会为您购买一些框架:

// 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() 较慢,因为它会进行边界检查.

【讨论】:

  • 你的代码让它变得更好(+50 fps),但它仍然不是很好:(
猜你喜欢
  • 1970-01-01
  • 2014-07-28
  • 1970-01-01
  • 2011-08-30
  • 1970-01-01
  • 1970-01-01
  • 2013-01-19
  • 2014-03-21
  • 1970-01-01
相关资源
最近更新 更多