diff options
| author | sotech117 <michael_foiani@brown.edu> | 2023-12-07 16:23:20 -0500 |
|---|---|---|
| committer | sotech117 <michael_foiani@brown.edu> | 2023-12-07 16:23:20 -0500 |
| commit | caa765bff49d54217b75aaf0e7acf4e5392a11e4 (patch) | |
| tree | 9b92914dfb88b99599e8e60e4512e9e9ea9a25db /src/accelerate | |
| parent | a9274459443f1d560d7580a162deb581549980cb (diff) | |
upload base code
Diffstat (limited to 'src/accelerate')
| -rw-r--r-- | src/accelerate/bvh.cpp | 139 | ||||
| -rw-r--r-- | src/accelerate/bvh.h | 20 | ||||
| -rw-r--r-- | src/accelerate/kdtree.cpp | 273 | ||||
| -rw-r--r-- | src/accelerate/kdtree.h | 53 | ||||
| -rw-r--r-- | src/accelerate/myqtconcurrent.cpp | 80 | ||||
| -rw-r--r-- | src/accelerate/myqthreads.cpp | 130 |
6 files changed, 695 insertions, 0 deletions
diff --git a/src/accelerate/bvh.cpp b/src/accelerate/bvh.cpp new file mode 100644 index 0000000..ce104a0 --- /dev/null +++ b/src/accelerate/bvh.cpp @@ -0,0 +1,139 @@ +#include "raytracer/raytracer.h" +#include "bvh.h" + +bvh::bvh( + const std::vector<KdShape>& p_shapes, + int p_dimension) +{ + dimension = p_dimension; + if (p_shapes.empty()) { + return; + } + + // compute the new bouding ragion from the shapes and add shapes to the root + BoundingRegion tmp { + glm::vec4(-FINF, -FINF, -FINF, 1.f), + glm::vec4(FINF, FINF, FINF, 1.f), + glm::vec4(-FINF, -FINF, -FINF, 1.f), + glm::vec4(FINF, FINF, FINF, 1.f), + glm::vec4(-FINF, -FINF, -FINF, 1.f), + glm::vec4(FINF, FINF, FINF, 1.f), + glm::vec4(0.f, 0.f, 0.f, 1.f) + }; + shapes = std::vector<KdShape>(); + for (const auto& shape : p_shapes) { + tmp.xmax = glm::max(tmp.xmax, shape.region.xmax); + tmp.xmin = glm::min(tmp.xmin, shape.region.xmin); + tmp.ymax = glm::max(tmp.ymax, shape.region.ymax); + tmp.ymin = glm::min(tmp.ymin, shape.region.ymin); + tmp.zmax = glm::max(tmp.zmax, shape.region.zmax); + tmp.zmin = glm::min(tmp.zmin, shape.region.zmin); + tmp.center.x = (tmp.xmax.x + tmp.xmin.x) / 2.f; + tmp.center.y = (tmp.ymax.y + tmp.ymin.y) / 2.f; + tmp.center.z = (tmp.zmax.z + tmp.zmin.z) / 2.f; + + shapes.push_back(shape); + } + region = tmp; + + // split the shapes into two groups, if more than two shapes + if (shapes.size() <= 2) { + return; + } + std::vector<KdShape> leftShapes; + std::vector<KdShape> rightShapes; + for (const auto& shape : shapes) { + if (shape.region.center[dimension] < region.center[dimension]) { + leftShapes.push_back(shape); + } + else if (shape.region.center[dimension] > region.center[dimension]) { + rightShapes.push_back(shape); + } else { + if (leftShapes.size() < rightShapes.size()) { + leftShapes.push_back(shape); + } else { + rightShapes.push_back(shape); + } + } + } + + // make the children + leftChild = new bvh(leftShapes, (dimension + 1) % 3); + rightChild = new bvh(rightShapes, (dimension + 1) % 3); +} + +float intersectRegion( + glm::vec4 p, + glm::vec4 d, + BoundingRegion region) +{ + float tXmin = (region.xmin.x - p.x) / d.x; + float tXmax = (region.xmax.x - p.x) / d.x; + float tYmin = (region.ymin.y - p.y) / d.y; + float tYmax = (region.ymax.y - p.y) / d.y; + float tZmin = (region.zmin.z - p.z) / d.z; + float tZmax = (region.zmax.z - p.z) / d.z; + + float tMin = std::max(std::max(std::min(tXmin, tXmax), std::min(tYmin, tYmax)), std::min(tZmin, tZmax)); + float tMax = std::min(std::min(std::max(tXmin, tXmax), std::max(tYmin, tYmax)), std::max(tZmin, tZmax)); + + if (tMin > tMax) { + return FINF; + } + return tMin; +} + +float RayTracer::traverseBVH( + glm::vec4 p, + glm::vec4 d, + RenderShapeData &testShape, + bvh *root) +{ + std::vector<bvh*> stack = std::vector<bvh*>(); + stack.push_back(root); + float minT = FINF; + + while (!stack.empty()) + { + auto current = *stack.back(); + stack.pop_back(); + + if (current.leftChild == nullptr && current.rightChild == nullptr) { + for (const auto &shape: current.shapes) { + glm::vec4 pObject = shape.shape.inverseCTM * p; + glm::vec4 dObject = glm::normalize(shape.shape.inverseCTM * d); + + glm::vec4 intersection = findIntersection(pObject, dObject, shape.shape); + if (intersection.w == 0.f) { + continue; + } + intersection = shape.shape.ctm * intersection; + // check within bounds + float tWorld = (intersection.x - p.x) / d.x; + if (tWorld < minT) + { + minT = tWorld; + testShape = shape.shape; + } + } + } else { + float leftIntersect = intersectRegion(p, d, current.leftChild->region); + float rightIntersect = intersectRegion(p, d, current.rightChild->region); + if (leftIntersect != FINF && rightIntersect != FINF) { + if (leftIntersect < rightIntersect) { + stack.push_back(current.rightChild); + stack.push_back(current.leftChild); + } else { + stack.push_back(current.leftChild); + stack.push_back(current.rightChild); + } + } else if (leftIntersect != FINF) { + stack.push_back(current.leftChild); + } else if (rightIntersect != FINF) { + stack.push_back(current.rightChild); + } + } + } + + return minT; +}
\ No newline at end of file diff --git a/src/accelerate/bvh.h b/src/accelerate/bvh.h new file mode 100644 index 0000000..062f748 --- /dev/null +++ b/src/accelerate/bvh.h @@ -0,0 +1,20 @@ +#include "raytracer/raytracer.h" + +#ifndef PROJECTS_RAY_BVH_H + +class bvh +{ +public: + bvh(const std::vector<KdShape> &shapes, int dimension); + + std::vector<KdShape> shapes; + int dimension; + BoundingRegion region{}; + bvh *leftChild; + bvh *rightChild; +}; + + +#define PROJECTS_RAY_BVH_H + +#endif //PROJECTS_RAY_BVH_H diff --git a/src/accelerate/kdtree.cpp b/src/accelerate/kdtree.cpp new file mode 100644 index 0000000..4156c98 --- /dev/null +++ b/src/accelerate/kdtree.cpp @@ -0,0 +1,273 @@ +#include "kdtree.h" +#include "raytracer/raytracer.h" + +// Constructor +KdTree::KdTree(int pDimension, float pSplitCoord) { + empty = true; + dimension = pDimension; + splitCoord = pSplitCoord; + + shapesWithinBounds = std::vector<KdShape>(); + + leftChild = nullptr; + rightChild = nullptr; +} + +void KdTree::insert(KdShape shape) +{ + // first, add shape to this node + shapesWithinBounds.push_back(shape); + + if (empty) { + empty = false; + return; + } + + int nextDimension = (dimension + 1) % 3; + if (dimension == 0) // x split + { + if ( + shape.region.xmin.x > splitCoord + ) + // bound box is strictly to the right, only add right + { + if (rightChild == nullptr) { + rightChild = new KdTree( + nextDimension, + shape.region.center[nextDimension]); // next dim is y + } + } + else if ( + shape.region.xmin.x < splitCoord + && shape.region.xmax.x > splitCoord + ) + // bounding box overlaps center, need to add to both children + { + if (rightChild == nullptr) { + rightChild = new KdTree( + nextDimension, + shape.region.center[nextDimension]); // next dim is y + } + + if (leftChild == nullptr) { + leftChild = new KdTree( + nextDimension, + shape.region.center[nextDimension]); // next dim is y + } + + } + else if ( + shape.region.xmax.x < splitCoord + ) + // bounding box strictly to the left, only add left + { + if (leftChild == nullptr) { + leftChild = new KdTree( + nextDimension, + shape.region.center[nextDimension]); // next dim is y + } + } + } + + else if (dimension == 1) // y split + { + if (shape.region.ymin.y > splitCoord) { + if (rightChild == nullptr) { + rightChild = new KdTree( + nextDimension, + shape.region.center[nextDimension]); // next dim is z + } + } + else if ( + shape.region.ymin.y < splitCoord + && shape.region.ymax.y > splitCoord + ) { + if (rightChild == nullptr) { + rightChild = new KdTree( + nextDimension, + shape.region.center[nextDimension]); // next dim is z + } + + if (leftChild == nullptr) { + leftChild = new KdTree( + nextDimension, + shape.region.center[nextDimension]); // next dim is z + } + } + else if ( + shape.region.ymax.y < splitCoord + ) { + if (leftChild == nullptr) { + leftChild = new KdTree( + nextDimension, + shape.region.center[nextDimension]); // next dim is z + } + } + } + + else if (dimension == 2) // z split + { + if (shape.region.zmin.z > splitCoord) { + if (rightChild == nullptr) { + rightChild = new KdTree( + nextDimension, + shape.region.center[nextDimension]); // next dim is x + } + } + else if ( + shape.region.zmin.z < splitCoord + && shape.region.zmax.z > splitCoord + ) { + if (rightChild == nullptr) { + rightChild = new KdTree( + nextDimension, + shape.region.center[nextDimension]); // next dim is x + } + + if (leftChild == nullptr) { + leftChild = new KdTree( + nextDimension, + shape.region.center[nextDimension]); // next dim is x + } + } + else if ( + shape.region.zmax.z < splitCoord + ) { + if (leftChild == nullptr) { + leftChild = new KdTree( + nextDimension, + shape.region.center[nextDimension]); // next dim is x + } + } + } + + // now, add shape to children + if (leftChild != nullptr) { + leftChild->insert(shape); + } + if (rightChild != nullptr) { + rightChild->insert(shape); + } +} + +BoundingRegion KdTree::transformBoundingRegion(BoundingRegion region, glm::mat4 transformationMatrix, glm::vec3 basis) +{ + std::vector<glm::vec4> transformedPoints = std::vector<glm::vec4>(); + transformedPoints.push_back(transformationMatrix * region.xmax); + transformedPoints.push_back(transformationMatrix * region.xmin); + transformedPoints.push_back(transformationMatrix * region.ymax); + transformedPoints.push_back(transformationMatrix * region.ymin); + transformedPoints.push_back(transformationMatrix * region.zmax); + transformedPoints.push_back(transformationMatrix * region.zmin); + + BoundingRegion transformedRegion{ + glm::vec4(-FINF), + glm::vec4(FINF), + glm::vec4(-FINF), + glm::vec4(FINF), + glm::vec4(-FINF), + glm::vec4(FINF), + glm::vec4(0.f) + }; // just init values, will be set to be correct + + // these are the new bound points, but they may have been rotated or reflected + // this also ensures axis aligned bounding boxes, given the dots with the basis + for (glm::vec4 point: transformedPoints) { + if (point.x * basis.x > transformedRegion.xmax.x) { + transformedRegion.xmax = point; + } + if (point.x * basis.x < transformedRegion.xmin.x) { + transformedRegion.xmin = point; + } + if (point.y * basis.y > transformedRegion.ymax.y) { + transformedRegion.ymax = point; + } + if (point.y * basis.y < transformedRegion.ymin.y) { + transformedRegion.ymin = point; + } + if (point.z * basis.z > transformedRegion.zmax.z) { + transformedRegion.zmax = point; + } + if (point.z * basis.z < transformedRegion.zmin.z) { + transformedRegion.zmin = point; + } + } + + transformedRegion.center = transformationMatrix * region.center; + return transformedRegion; +} + +// TODO: return the float with the shape +float RayTracer::traverse( + glm::vec4 p, + glm::vec4 d, + float tStart, + float tEnd, + RenderShapeData &testShape, + KdTree *tree) +{ + if (tree == nullptr) { + return FINF; + } + + // leaf node + if ( tree->shapesWithinBounds.size() <= 2 || + tree->leftChild == nullptr || tree->rightChild == nullptr) + { + float minT = FINF; + for (const auto &shape: tree->shapesWithinBounds) { + glm::vec4 pObject = shape.shape.inverseCTM * p; + glm::vec4 dObject = glm::normalize(shape.shape.inverseCTM * d); + + glm::vec4 intersection = findIntersection(pObject, dObject, shape.shape); + if (intersection.w == 0.f) { + continue; + } + intersection = shape.shape.ctm * intersection; + // check within bounds + if ( + intersection.x <= shape.region.xmax.x && intersection.x >= shape.region.xmin.x + && + intersection.y <= shape.region.ymax.y && intersection.y >= shape.region.ymin.y + && + intersection.z <= shape.region.zmax.z && intersection.z >= shape.region.zmin.z + ) + { + float tWorld = (intersection.x - p.x) / d.x; + if (tWorld < minT) { + minT = tWorld; + testShape = shape.shape; + } + } + } + return minT; + } + + // solve for t, only in current 1d-dimension + float t = (tree->splitCoord - p[tree->dimension]) / d[tree->dimension]; + + // There are three cases: + // 1) only intersects with left (front) child (t <= tEnd) + // 2) only intersects with right (back) child (t <= tStart) + // 3) intersects with both children (tStart <= t <= tEnd) + // on last case, we need to traverse both children, + // but gain a significant speedup by traversing the one that is closer first. + + if (t <= tStart && tree->rightChild != nullptr) // case 1) + { + return traverse(p, d, tStart, tEnd, testShape, tree->rightChild); + } + else if (t >= tEnd && tree->leftChild != nullptr) // case 2) + { + return traverse(p, d, tStart, tEnd, testShape, tree->leftChild); + } + else // case 3) + { + float t_hit = traverse(p, d, tStart, t, testShape, tree->leftChild); + if (t_hit < t) + { // this is where we save time! + return t_hit; + } + return traverse(p, d, t, tEnd, testShape, tree->rightChild); + } +}
\ No newline at end of file diff --git a/src/accelerate/kdtree.h b/src/accelerate/kdtree.h new file mode 100644 index 0000000..e33aa59 --- /dev/null +++ b/src/accelerate/kdtree.h @@ -0,0 +1,53 @@ +#ifndef KDTREE_H +#define KDTREE_H +#include "utils/sceneparser.h" +#include <queue> +#include <vector> + +typedef struct { + glm::vec4 xmax; + glm::vec4 xmin; + glm::vec4 ymax; + glm::vec4 ymin; + glm::vec4 zmax; + glm::vec4 zmin; + glm::vec4 center; +} BoundingRegion; + +typedef struct KdShape +{ + RenderShapeData shape; + BoundingRegion region; +} KdShape; + +class KdTree +{ +public: + + KdTree(int pDimension, float pSplitCoord); + + bool empty; + int dimension; + float splitCoord; + std::vector<KdShape> shapesWithinBounds; + void insert(KdShape shape); + + KdTree *leftChild; + KdTree *rightChild; + + // todo: make basis a matrix + static BoundingRegion transformBoundingRegion(BoundingRegion region, glm::mat4 transformationMatrix, glm::vec3 basis=glm::vec3(1.0f, 1.0f, 1.0f)); +}; + +const static BoundingRegion OBJECT_BOUNDS{ + glm::vec4(.5f, 0.f, 0.f, 1.f), + glm::vec4(-.5f, 0.f, 0.f, 1.f), + glm::vec4(0.f, .5f, 0.f, 1.f), + glm::vec4(0.f, -.5f, 0.f, 1.f), + glm::vec4(0.f, 0.f, .5f, 1.f), + glm::vec4(0.f, 0.f, -.5f, 1.f), + glm::vec4(0.f, 0.f, 0.f, 1.f) +}; + + +#endif // KDTREE_H
\ No newline at end of file diff --git a/src/accelerate/myqtconcurrent.cpp b/src/accelerate/myqtconcurrent.cpp new file mode 100644 index 0000000..1dff0e0 --- /dev/null +++ b/src/accelerate/myqtconcurrent.cpp @@ -0,0 +1,80 @@ + +#include <QList> +#include <QtConcurrent> +#include "raytracer/raytracer.h" + +struct pixelRoutineArgs { + glm::vec4 pCamera; + glm::vec4 dCamera; + const RayTraceScene &scene; + RayTracer rt; +}; +static RGBA pixelRoutine(pixelRoutineArgs args); + +void RayTracer::renderParallel(RGBA *imageData, const RayTraceScene &scene) +{ + Camera camera = scene.getCamera(); + float cameraDepth = 1.f; + float viewplaneHeight = 2.f*cameraDepth*std::tan(camera.getHeightAngle() / 2.f); + float viewplaneWidth = cameraDepth*viewplaneHeight*((float)scene.width()/(float)scene.height()); + + QList<pixelRoutineArgs> l{}; + for (int imageRow = 0; imageRow < scene.height(); imageRow++) { + for (int imageCol = 0; imageCol < scene.width(); imageCol++) { + float xCameraSpace = viewplaneWidth * + (-.5f + (imageCol + .5f) / scene.width()); + float yCameraSpace = viewplaneHeight * + (-.5f + (imageRow + .5f) / scene.height()); + + glm::vec4 pixelDirCamera{xCameraSpace, -yCameraSpace, -cameraDepth, 0.f}; //w=0 for dir + glm::vec4 eyeCamera{0.f, 0.f, 0.f, 1.f}; // w=1.f for point + l.append({ + eyeCamera, // eye + pixelDirCamera, // direction + scene, + *this + }); + + } + } + QList<RGBA> pixels = QtConcurrent::blockingMapped(l, pixelRoutine); + QtConcurrent::blockingMap(l, pixelRoutine); + int index = 0; + for (RGBA p : pixels) { + imageData[index++] = p; + } + + if (m_config.enableAntiAliasing) + { + filterBlur(imageData, scene.width(), scene.height()); + } +} + + +RGBA pixelRoutine(pixelRoutineArgs args) +{ + auto eyeCamera = args.pCamera; + auto pixelDirCamera = args.dCamera; + auto scene = args.scene; + auto rt = args.rt; + + // convert camera space to world space + auto inv = scene.getCamera().getInverseViewMatrix(); + glm::vec4 pWorld = inv * eyeCamera; + glm::vec4 dWorld = glm::normalize(inv * pixelDirCamera); + + if (rt.m_config.enableDepthOfField) + { + // if we're doing depth of field, we need to shoot multiple rays, see camera.cpp + return RayTracer::toRGBA(rt.secondaryRays(pWorld, dWorld, scene)); + } + if (rt.m_config.enableSuperSample) + { + // if we're doing super sampling, we need to shoot multiple rays, see raytracer.cpp + return rt.superSample(eyeCamera, pixelDirCamera, scene); + } + + // shoot ray! + RGBA pixel = RayTracer::toRGBA(rt.getPixelFromRay(pWorld, dWorld, scene, 0)); + return pixel; +} diff --git a/src/accelerate/myqthreads.cpp b/src/accelerate/myqthreads.cpp new file mode 100644 index 0000000..ead3aec --- /dev/null +++ b/src/accelerate/myqthreads.cpp @@ -0,0 +1,130 @@ +#include "raytracer/raytracer.h" +#include <QThread> + +/** + * Extra credit -> own implementation of multithreading using QThreads. + * NOT USED for illuminate (not any faster than QT's version), but was used in intersect. + */ + +//struct intersectRoutineArgs { +// RenderShapeData shape; +// glm::vec4 pWorld; +// glm::vec4 dWorld; +//}; +// +//struct intersectData { +// float distance; +// glm::vec4 intersectionWorld; +// glm::vec4 intersectionObj; +// RenderShapeData intersectedShape; +//}; +// +//Q_DECLARE_METATYPE(intersectData); +// +//class IntersectWorker : public QThread +//{ +// Q_OBJECT +// void run() override { +// exec(); +// /* ... here is the expensive or blocking operation ... */ +// glm::vec4 pObject = glm::inverse(a.shape.ctm) * a.pWorld; +// glm::vec4 dObject = glm::normalize(glm::inverse(a.shape.ctm) * a.dWorld); +// +// glm::vec4 intersectionObj = RayTracer::findIntersection(pObject, dObject, a.shape); +// if (intersectionObj.w == 0) // no hit +// { +// const intersectData response{ +// FINF, +// glm::vec4(0.f), +// glm::vec4(0.f), +// a.shape +// }; +// ps.append(response); +// emit data(response); +// } else { +// auto intersectionWorld = a.shape.ctm * intersectionObj; +// float distance = glm::distance(intersectionWorld, a.pWorld); +// +// const intersectData response{ +// distance, +// intersectionWorld, +// intersectionObj, +// a.shape +// }; +// ps.append(response); +// emit data(response); +// } +// emit finished(); +// } +//public: +// intersectRoutineArgs a; +// QList<intersectData> &ps; +// IntersectWorker(intersectRoutineArgs args, QList<intersectData> &p) : ps(p) +// { +// a = args; +// } +// signals: +// void data(const intersectData &s); +// void finished(); +//}; +// +// +//class IntersectController : public QObject +//{ +// Q_OBJECT +//public: +// std::vector<QThread*> qthreads; +// QList<intersectData> intersectPoints; +// IntersectController(const std::vector<RenderShapeData> &shapes, glm::vec4 pWorld, glm::vec4 dWorld) { +// qRegisterMetaType<const intersectData&>("myType"); +// int id = 0; +// for (const RenderShapeData &shape: shapes) { +// const intersectRoutineArgs threadArgs{shape, pWorld, dWorld}; +// IntersectWorker *thread = new IntersectWorker(threadArgs, intersectPoints); +// +// connect(thread, &IntersectWorker::data, this, &IntersectController::addIntersectionPoint); +// connect(thread, &IntersectWorker::finished, thread, &QThread::quit); +// +// connect(thread, &IntersectWorker::finished, thread, &QThread::deleteLater); +// +// qthreads.push_back(thread); +// } +// } +// ~IntersectController() { +// for (QThread* workerThread: qthreads) { +// workerThread->exit(); +// } +// qthreads.clear(); +// intersectPoints.clear(); +// } +// void getClosestIntersection(float &minDist, glm::vec4 &closestIntersectionWorld, glm::vec4 &closestIntersectionObj, RenderShapeData intersectedShape) { +// // start then wait +// for (QThread* thread: qthreads) { +// thread->start(); +// } +// for (QThread* thread: qthreads) { +// thread->quit(); +// thread->wait(); +// } +// +// +// // once all threads are done, find the closest +// for (const intersectData &i : intersectPoints) { +// if (i.distance < minDist) { +// minDist = i.distance; +// +// intersectedShape = i.intersectedShape; +// closestIntersectionObj = i.intersectionObj; +// closestIntersectionWorld = i.intersectionWorld; +// } +// } +//} +//public slots: +// void addIntersectionPoint(const intersectData &s) { +// intersectPoints.append(s); +// } +// signals: +// void operate(intersectRoutineArgs a); +//}; +// +//#include "myqthreads.moc"
\ No newline at end of file |
