worked on 2D/3D raytracing

adjusted BVH
improved 2D/3D BVH
new bounding volumes
new test cases
renamed some test-cases for grouping reasons
made GPC header-only using slight adjustments
This commit is contained in:
2017-09-13 08:08:00 +02:00
parent c19d18a3a6
commit 686151b511
38 changed files with 1257 additions and 253 deletions

View File

@@ -4,29 +4,36 @@
#include <vector>
#include <functional>
#include "../Ray2.h"
#include "../Ray3.h"
#include "BoundingVolume.h"
#include "BoundingVolumeAABB.h"
#include "BoundingVolumeSphere.h"
#include "BoundingVolumeAABB2.h"
#include "BoundingVolumeCircle2.h"
#include "BoundingVolumeAABB3.h"
#include "BoundingVolumeSphere3.h"
template <typename Element, typename Volume, typename Wrapper> class BVH {
template <typename Element, typename Ray, typename Point, typename Volume, typename Wrapper> class BVH {
protected:
/** one node within the tree */
struct BVHNode {
bool isLeaf = true;
bool isLeaf;
bool check;
Volume boundingVolume;
std::vector<BVHNode*> childNodes;
BVHNode(bool isLeaf = false) : isLeaf(isLeaf) {;}
BVHNode(bool isLeaf = false, bool check = true) : isLeaf(isLeaf), check(check) {;}
};
/** one leaf within the tree */
struct BVHLeaf : public BVHNode {
Element element;
BVHLeaf(const Element& e) : BVHNode(true), element(e) {;}
BVHLeaf(const Element& e, const bool check) : BVHNode(true, check), element(e) {;}
};
/** the tree's root */
@@ -40,10 +47,10 @@ public:
}
/** add a new volume to the tree */
void add(const Element& element) {
void add(const Element& element, const bool leafCheck = true) {
// create a new leaf for this element
BVHLeaf* leaf = new BVHLeaf(element);
BVHLeaf* leaf = new BVHLeaf(element, leafCheck);
// get the element's boundin volume
leaf->boundingVolume = getBoundingVolume(element);
@@ -63,17 +70,17 @@ public:
return max;
}
void getHits(const Ray3 ray, std::function<void(const Element&)> func) const {
//int tests = 0; int leafs = 0;
void getHits(const Ray& ray, const std::function<void(const Element&)>& func) const {
getHits(ray, &root, func);
//std::cout << tests << " " << leafs << std::endl;
}
void getHits(const Ray3 ray, const BVHNode* node, std::function<void(const Element&)> func) const {
// this one has to be as fast as possible!
static void getHits(const Ray& ray, const BVHNode* node, const std::function<void(const Element&)>& func) {
for (const BVHNode* sub : node->childNodes) {
if (sub->boundingVolume.intersects(ray)) {
if (!sub->check || sub->boundingVolume.intersects(ray)) {
if (sub->isLeaf) {
BVHLeaf* leaf = (BVHLeaf*)(sub); // TODO: cast
const BVHLeaf* leaf = static_cast<const BVHLeaf*>(sub);
func(leaf->element);
} else {
getHits(ray, sub, func);
@@ -82,8 +89,50 @@ public:
}
}
/** get the tree's depth */
int getDepth() const {
return getDepth(&root, 1);
}
private:
/** call the given function for each leaf within the given subtree */
void forEachLeaf(const BVHNode* n, std::function<void(const BVHNode*)> func) const {
if (n->isLeaf) {
func(n);
} else {
for (BVHNode* child : n->childNodes) {
forEachLeaf(child, func);
}
}
}
/** determine/approximate a new bounding volume around n1+n2 */
Volume getVolAround(const BVHNode* n1, const BVHNode* n2) const {
//return getVolAroundExact(n1, n2);
return getVolAroundAPX(n1, n2);
}
/** determine the bounding-volume around n1 and n2 by (slowly) calculating a new, exact volume based on all leaf-elements */
Volume getVolAroundExact(const BVHNode* n1, const BVHNode* n2) const {
std::vector<Point> verts;
auto onLeaf = [&] (const BVHNode* n) {
BVHLeaf* leaf = (BVHLeaf*) n;
std::vector<Point> subVerts = Wrapper::getVertices(leaf->element);
verts.insert(verts.end(), subVerts.begin(), subVerts.end());
};
forEachLeaf(n1, onLeaf);
forEachLeaf(n2, onLeaf);
return Volume::fromVertices(verts);
}
/** approximate the bounding-volume around n1 and n2 by (quickly) joining their current volumes. the result might be unnecessarily large */
Volume getVolAroundAPX(const BVHNode* n1, const BVHNode* n2) const {
return Volume::join(n1->boundingVolume, n2->boundingVolume);
}
bool combineBest() {
// nothing to do?
@@ -104,7 +153,7 @@ private:
BVHNode* n1 = root.childNodes[i];
BVHNode* n2 = root.childNodes[j];
const Volume newVol = Volume::join(n1->boundingVolume, n2->boundingVolume);
const Volume newVol = getVolAround(n1,n2);
const float newVolSize = newVol.getVolumeSize();
if (newVolSize < best.volSize) {
best.vol = newVol;
@@ -226,13 +275,32 @@ private:
}
int getDepth(const BVHNode* node, const int cur) const {
if (node->isLeaf) {
return cur;
} else {
int res = cur;
for (const BVHNode* sub : node->childNodes) {
const int subDepth = getDepth(sub, cur+1);
if (subDepth > res) {res = subDepth;}
}
return res;
}
}
/** get a bounding-volume for the given element */
Volume getBoundingVolume(const Element& element) {
const std::vector<Point3> verts = Wrapper::getVertices(element);
const std::vector<Point> verts = Wrapper::getVertices(element);
return Volume::fromVertices(verts);
}
};
template <typename Element, typename Volume, typename Wrapper> class BVH3 : public BVH<Element, Ray3, Point3, Volume, Wrapper> {
};
template <typename Element, typename Volume, typename Wrapper> class BVH2 : public BVH<Element, Ray2, Point2, Volume, Wrapper> {
};

View File

@@ -2,39 +2,52 @@
#define BVHDEBUG_H
#include "BVH.h"
#include <KLib/misc/gnuplot/Gnuplot.h>
#include <KLib/misc/gnuplot/GnuplotSplot.h>
#include <KLib/misc/gnuplot/GnuplotSplotElementPoints.h>
#include <KLib/misc/gnuplot/GnuplotSplotElementColorPoints.h>
#include <KLib/misc/gnuplot/GnuplotSplotElementLines.h>
#include <KLib/misc/gnuplot/Gnuplot.h>
#include <KLib/misc/gnuplot/GnuplotPlot.h>
#include <KLib/misc/gnuplot/GnuplotPlotElementPoints.h>
#include <KLib/misc/gnuplot/GnuplotPlotElementColorLines.h>
#include <KLib/misc/gnuplot/GnuplotPlotElementLines.h>
#include "../BBox3.h"
#include <random>
/** adds some debug helpers to the BVH */
template <typename Element, typename Volume, typename Wrapper> class BVHDebug : public BVH<Element, Volume, Wrapper> {
///** adds some debug helpers to the BVH */
//template <typename Element, typename Ray, typename Point, typename Volume, typename Wrapper> class BVHDebug : public BVH<Element, Ray, Point, Volume, Wrapper> {
//};
template <typename Element, typename Volume, typename Wrapper> class BVH3Debug : public BVH<Element, Ray3, Point3, Volume, Wrapper> {
using BVHNode = typename BVH<Element, Ray3, Point3, Volume, Wrapper>::BVHNode;
using BVHLeaf = typename BVH<Element, Ray3, Point3, Volume, Wrapper>::BVHLeaf;
using BVHNode = typename BVH<Element, Volume, Wrapper>::BVHNode;
using BVHLeaf = typename BVH<Element, Volume, Wrapper>::BVHLeaf;
public:
// std::vecto<std::string> colors {
// "#888888", "#888800", "#008888", "#880088", "#ee0000", "#00ee00", "#0000ee"
// };
void show(int maxPts = 1500, bool showLeafs = true) {
std::stringstream out;
static K::Gnuplot gp;
K::GnuplotSplot plot;
K::GnuplotSplotElementColorPoints pVol; plot.add(&pVol); //pVol.setColor(K::GnuplotColor::fromRGB(128,128,128));
K::GnuplotSplotElementPoints pElemPoints; plot.add(&pElemPoints); pElemPoints.setColor(K::GnuplotColor::fromRGB(0,0,255));
K::GnuplotSplotElementLines pElemLines; plot.add(&pElemLines); pElemLines.getStroke().setColor(K::GnuplotColor::fromRGB(0,0,255));
const int depth = recurse(maxPts, showLeafs, 0, &this->root, pVol, pElemPoints, pElemLines);
K::GnuplotSplotElementColorPoints pVol; plot.add(&pVol); //pVol.setColor(K::GnuplotColor::fromRGB(128,128,128));
K::GnuplotSplotElementPoints pElemPoints; plot.add(&pElemPoints); pElemPoints.setColor(K::GnuplotColor::fromRGB(0,0,255));
K::GnuplotSplotElementLines pElemLines; plot.add(&pElemLines); pElemLines.getStroke().setColor(K::GnuplotColor::fromRGB(0,0,255));
plot.getAxisCB().setRange(0, depth);
const int maxDepth = this->getDepth();
recurse(maxPts, showLeafs, 0, &this->root, pVol, pElemPoints, pElemLines);
plot.getAxisCB().setRange(0, maxDepth);
gp << "set view equal xyz\n";
gp.draw(plot);
@@ -44,6 +57,31 @@ public:
private:
Point3 getRandomPoint(BoundingVolumeSphere3 sphere) {
static std::minstd_rand gen;
std::uniform_real_distribution<float> dist(-1, +1);
Point3 dir = Point3(dist(gen), dist(gen), dist(gen)).normalized() * sphere.radius;
return sphere.center + dir;
}
void addLines(const Element& elem, K::GnuplotSplotElementLines& elemLines) {
std::vector<Point3> pts = Wrapper::getDebugLines(elem);
for (size_t i = 0; i< pts.size(); i+=2) {
const Point3 p1 = pts[i+0];
const Point3 p2 = pts[i+1];
K::GnuplotPoint3 gp1(p1.x, p1.y, p1.z);
K::GnuplotPoint3 gp2(p2.x, p2.y, p2.z);
elemLines.addSegment(gp1, gp2);
}
}
int recurse(int maxPts, bool showLeafs, int curDepth, const BVHNode* node, K::GnuplotSplotElementColorPoints& vol, K::GnuplotSplotElementPoints& pElemPoints, K::GnuplotSplotElementLines& elemLines) {
int resDepth = curDepth;
@@ -76,24 +114,53 @@ private:
}
Point3 getRandomPoint(BoundingVolumeSphere sphere) {
static std::minstd_rand gen;
std::uniform_real_distribution<float> dist(-1, +1);
Point3 dir = Point3(dist(gen), dist(gen), dist(gen)).normalized() * sphere.radius;
return sphere.center + dir;
};
template <typename Element, typename Volume, typename Wrapper> class BVH2Debug : public BVH<Element, Ray2, Point2, Volume, Wrapper> {
using BVHNode = typename BVH<Element, Ray2, Point2, Volume, Wrapper>::BVHNode;
using BVHLeaf = typename BVH<Element, Ray2, Point2, Volume, Wrapper>::BVHLeaf;
public:
void show(int maxPts = 1500, bool showLeafs = true) {
std::stringstream out;
static K::Gnuplot gp;
K::GnuplotPlot plot;
K::GnuplotPlotElementColorLines pVolLines; plot.add(&pVolLines);
K::GnuplotPlotElementPoints pElemPoints; plot.add(&pElemPoints); pElemPoints.setColor(K::GnuplotColor::fromRGB(0,0,255));
K::GnuplotPlotElementLines pElemLines; plot.add(&pElemLines); pElemLines.getStroke().setColor(K::GnuplotColor::fromRGB(0,0,255));
const int maxDepth = this->getDepth();
recurse(maxDepth, showLeafs, 0, &this->root, plot, pVolLines, pElemPoints, pElemLines);
plot.getObjects().reOrderByZIndex();
plot.getAxisCB().setRange(0, maxDepth);
gp << "set size ratio -1\n";
gp.draw(plot);
gp.flush();
}
void addLines(const Element& elem, K::GnuplotSplotElementLines& elemLines) {
private:
std::vector<Point3> pts = Wrapper::getDebugLines(elem);
void addLines(const Element& elem, K::GnuplotPlotElementLines& elemLines) {
std::vector<Point2> pts = Wrapper::getDebugLines(elem);
for (size_t i = 0; i< pts.size(); i+=2) {
const Point3 p1 = pts[i+0];
const Point3 p2 = pts[i+1];
const Point2 p1 = pts[i+0];
const Point2 p2 = pts[i+1];
K::GnuplotPoint3 gp1(p1.x, p1.y, p1.z);
K::GnuplotPoint3 gp2(p2.x, p2.y, p2.z);
K::GnuplotPoint2 gp1(p1.x, p1.y);
K::GnuplotPoint2 gp2(p2.x, p2.y);
elemLines.addSegment(gp1, gp2);
@@ -101,6 +168,75 @@ private:
}
std::vector<std::string> colors = {
"#888800", "#444400", "#008888", "#004444", "#880088", "#440044", "#ee0000", "#880000", "#00ee00", "#008800", "#0000ee", "#000088",
"#888800", "#444400", "#008888", "#004444", "#880088", "#440044", "#ee0000", "#880000", "#00ee00", "#008800", "#0000ee", "#000088",
"#888800", "#444400", "#008888", "#004444", "#880088", "#440044", "#ee0000", "#880000", "#00ee00", "#008800", "#0000ee", "#000088"
};
void showVolume(const BoundingVolumeCircle2& circle, int maxDepth, int curDepth, K::GnuplotPlot& plot, K::GnuplotPlotElementColorLines& pVolLines) {
K::GnuplotObjectPolygon* poly = new K::GnuplotObjectPolygon();
for (int i = 0; i < 20; ++i) {
const float f = M_PI*2 * i / 19;
const Point2 p = circle.getPointAt(f);
poly->add(K::GnuplotCoordinate2(p.x, p.y, K::GnuplotCoordinateSystem::FIRST));
poly->getFill().setColor(K::GnuplotColor::fromHexStr(colors[maxDepth-curDepth]));
poly->getFill().setStyle(K::GnuplotFillStyle::SOLID);
poly->setZIndex(curDepth);
}
plot.getObjects().add(poly);
}
void showVolume(const BoundingVolumeAABB2& _aabb, int maxDepth, int curDepth, K::GnuplotPlot& plot, K::GnuplotPlotElementColorLines& pVolLines) {
BBox2 bbox2 = _aabb;
bbox2.grow( (10-curDepth) / 100.0f );
// pVolLines.add(K::GnuplotPoint2(bbox2.getMin().x, bbox2.getMin().y), curDepth);
// pVolLines.add(K::GnuplotPoint2(bbox2.getMax().x, bbox2.getMin().y), curDepth);
// pVolLines.add(K::GnuplotPoint2(bbox2.getMax().x, bbox2.getMax().y), curDepth);
// pVolLines.add(K::GnuplotPoint2(bbox2.getMin().x, bbox2.getMax().y), curDepth);
// pVolLines.add(K::GnuplotPoint2(bbox2.getMin().x, bbox2.getMin().y), curDepth);
// pVolLines.splitFace();
K::GnuplotObjectPolygon* poly = new K::GnuplotObjectPolygon();
poly->getStroke().setColor(K::GnuplotColor::fromHexStr(colors[maxDepth-curDepth]));
//poly->getFill().setColor(K::GnuplotColor::fromHexStr(colors[maxDepth-curDepth]));
//poly->getFill().setStyle(K::GnuplotFillStyle::SOLID);
poly->add(K::GnuplotCoordinate2(bbox2.getMin().x, bbox2.getMin().y, K::GnuplotCoordinateSystem::FIRST));
poly->add(K::GnuplotCoordinate2(bbox2.getMax().x, bbox2.getMin().y, K::GnuplotCoordinateSystem::FIRST));
poly->add(K::GnuplotCoordinate2(bbox2.getMax().x, bbox2.getMax().y, K::GnuplotCoordinateSystem::FIRST));
poly->add(K::GnuplotCoordinate2(bbox2.getMin().x, bbox2.getMax().y, K::GnuplotCoordinateSystem::FIRST));
poly->close();
poly->setZIndex(curDepth);
plot.getObjects().add(poly);
}
int recurse(int maxDepth, bool showLeafs, int curDepth, const BVHNode* node, K::GnuplotPlot& plot, K::GnuplotPlotElementColorLines& pVolLines, K::GnuplotPlotElementPoints& pElemPoints, K::GnuplotPlotElementLines& elemLines) {
int resDepth = curDepth;
for (BVHNode* sub : node->childNodes) {
resDepth = recurse(maxDepth, showLeafs, curDepth+1, sub, plot, pVolLines, pElemPoints, elemLines);
}
if (!node->isLeaf || showLeafs) {
if (node != &this->root) {
//const int numPts = maxPts / (curDepth+1);
showVolume(node->boundingVolume, maxDepth, curDepth, plot, pVolLines);
}
}
if (node->isLeaf) {
BVHLeaf* leaf = (BVHLeaf*) node;
std::vector<Point2> verts = Wrapper::getVertices(leaf->element);
for (const Point2 p : verts) {
pElemPoints.add(K::GnuplotPoint2(p.x, p.y));
}
addLines(leaf->element, elemLines);
}
return resDepth;
}
};
#endif // BVHDEBUG_H

View File

@@ -0,0 +1,36 @@
#ifndef BOUNDINGVOLUMEAABB2_H
#define BOUNDINGVOLUMEAABB2_H
#include <vector>
#include "BoundingVolume.h"
#include "../BBox2.h"
class BoundingVolumeAABB2 : public BBox2 {
public:
BoundingVolumeAABB2() {;}
BoundingVolumeAABB2(const BBox2& bb) : BBox2(bb) {;}
float getVolumeSize() const {
const float dx = getMax().x - getMin().x;
const float dy = getMax().y - getMin().y;
return (dx*dy);
}
/** construct a volume around the given point-set */
static BoundingVolumeAABB2 fromVertices(const std::vector<Point2>& verts) {
BoundingVolumeAABB2 bvs;
for (const Point2 p : verts) {bvs.add(p);}
return bvs;
}
static BoundingVolumeAABB2 join(const BoundingVolumeAABB2 a, const BoundingVolumeAABB2 b) {
return BoundingVolumeAABB2(BBox2::join(a, b));
}
};
#endif // BOUNDINGVOLUMEAABB2_H

View File

@@ -1,4 +0,0 @@
#ifndef BOUNDINGVOLUMEBOX_H
#define BOUNDINGVOLUMEBOX_H
#endif // BOUNDINGVOLUMEBOX_H

View File

@@ -0,0 +1,38 @@
#ifndef BOUDINGVOLUMECIRCLE2_H
#define BOUDINGVOLUMECIRCLE2_H
#include <vector>
#include "BoundingVolume.h"
#include "../Circle2.h"
class BoundingVolumeCircle2 : public Circle2 {
public:
BoundingVolumeCircle2() {;}
BoundingVolumeCircle2(const Circle2& c) : Circle2(c) {;}
float getVolumeSize() const {
return M_PI * (radius*radius);
}
bool intersects(const Ray2 ray) const {
return Circle2::intersects(ray);
}
/** construct a volume around the given point-set */
static BoundingVolumeCircle2 fromVertices(const std::vector<Point2>& verts) {
BoundingVolumeCircle2 bvs;
bvs.adjustToPointSet(verts);
return bvs;
}
static BoundingVolumeCircle2 join(const BoundingVolumeCircle2 a, const BoundingVolumeCircle2 b) {
return BoundingVolumeCircle2(Circle2::join(a, b));
}
};
#endif // BOUDINGVOLUMECIRCLE2_H

View File

@@ -1,4 +0,0 @@
#ifndef BOUNDINGVOLUMEHIERARCHY_H
#define BOUNDINGVOLUMEHIERARCHY_H
#endif // BOUNDINGVOLUMEHIERARCHY_H

View File

@@ -5,13 +5,13 @@
#include "../Sphere3.h"
#include "../Point3.h"
class BoundingVolumeSphere : public BoundingVolume, public Sphere3 {
class BoundingVolumeSphere3 : public BoundingVolume, public Sphere3 {
public:
BoundingVolumeSphere() {;}
BoundingVolumeSphere3() {;}
BoundingVolumeSphere(const Sphere3& s) : Sphere3(s) {;}
BoundingVolumeSphere3(const Sphere3& s) : Sphere3(s) {;}
float getVolumeSize() const {
return (4.0f / 3.0f) * M_PI * (radius*radius*radius);
@@ -27,25 +27,25 @@ public:
/** does the volume intersect with the given volume? */
bool intersects(const BoundingVolume& other) const {
const BoundingVolumeSphere& sphere = (const BoundingVolumeSphere&) other;
const BoundingVolumeSphere3& sphere = (const BoundingVolumeSphere3&) other;
return Sphere3::intersects(sphere);
}
/** does the volume contain the given volume? */
bool contains(const BoundingVolume& other) const {
const BoundingVolumeSphere& sphere = (const BoundingVolumeSphere&) other;
const BoundingVolumeSphere3& sphere = (const BoundingVolumeSphere3&) other;
return Sphere3::contains(sphere);
}
/** construct a volume around the given point-set */
static BoundingVolumeSphere fromVertices(const std::vector<Point3>& verts) {
BoundingVolumeSphere bvs;
static BoundingVolumeSphere3 fromVertices(const std::vector<Point3>& verts) {
BoundingVolumeSphere3 bvs;
bvs.adjustToPointSet(verts);
return bvs;
}
static BoundingVolumeSphere join(const BoundingVolumeSphere a, const BoundingVolumeSphere b) {
return BoundingVolumeSphere(Sphere3::join(a, b));
static BoundingVolumeSphere3 join(const BoundingVolumeSphere3 a, const BoundingVolumeSphere3 b) {
return BoundingVolumeSphere3(Sphere3::join(a, b));
}
};