aboutsummaryrefslogtreecommitdiff
path: root/src/raytracer
diff options
context:
space:
mode:
Diffstat (limited to 'src/raytracer')
-rw-r--r--src/raytracer/raytracer.cpp150
-rw-r--r--src/raytracer/raytracer.h140
-rw-r--r--src/raytracer/raytracescene.cpp56
-rw-r--r--src/raytracer/raytracescene.h42
4 files changed, 388 insertions, 0 deletions
diff --git a/src/raytracer/raytracer.cpp b/src/raytracer/raytracer.cpp
new file mode 100644
index 0000000..c3466cf
--- /dev/null
+++ b/src/raytracer/raytracer.cpp
@@ -0,0 +1,150 @@
+#include <QList>
+#include <QtConcurrent>
+#include <iostream>
+#include "raytracer.h"
+#include "raytracescene.h"
+
+//struct Ray {
+// glm::vec3 p;
+// glm::vec3 d;
+//};
+
+RayTracer::RayTracer(const Config &config) : m_config(config) {}
+
+void RayTracer::render(RGBA *imageData, const RayTraceScene &scene) {
+ if(m_config.enableParallelism)
+ {
+ renderParallel(imageData, scene);
+ return;
+ }
+
+ // naive rendering
+ 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());
+
+ 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
+
+ // convert to world space
+ glm::vec4 pWorld = camera.getInverseViewMatrix() * eyeCamera;
+ glm::vec4 dWorld = glm::normalize(camera.getInverseViewMatrix() * pixelDirCamera);
+
+ // cast ray!
+ glm::vec4 pixel = getPixelFromRay(pWorld, dWorld, scene);
+ imageData[imageRow * scene.width() + imageCol] = toRGBA(pixel);
+ }
+ }
+}
+
+
+glm::vec4 RayTracer::getPixelFromRay(
+ glm::vec4 pWorld,
+ glm::vec4 dWorld,
+ const RayTraceScene &scene,
+ int depth)
+{
+ if (depth > m_config.maxRecursiveDepth)
+ {
+ return glm::vec4(0.f);
+ }
+
+ // variables from computing the intersection
+ glm::vec4 closestIntersectionObj;
+ glm::vec4 closestIntersectionWorld;
+ RenderShapeData intersectedShape;
+
+ if (m_config.enableAcceleration)
+ {
+ float tWorld = traverseBVH(pWorld, dWorld, intersectedShape, scene.m_bvh);
+ if (tWorld == FINF)
+ {
+ return glm::vec4(0.f);
+ }
+ closestIntersectionWorld = pWorld + tWorld * dWorld;
+ closestIntersectionObj = intersectedShape.inverseCTM * closestIntersectionWorld;
+ }
+ else
+ {
+ float minDist = FINF;
+ // shoot a ray at each shape
+ for (const RenderShapeData &shape : scene.getShapes()) {
+ glm::vec4 pObject = shape.inverseCTM * pWorld;
+ glm::vec4 dObject = glm::normalize(shape.inverseCTM * dWorld);
+
+ glm::vec4 newIntersectionObj = findIntersection(pObject, dObject, shape);
+ if (newIntersectionObj.w == 0) // no hit
+ {
+ continue;
+ }
+
+ auto newIntersectionWorld = shape.ctm * newIntersectionObj;
+ float newDist = glm::distance(newIntersectionWorld, pWorld);
+ if (
+ newDist < minDist // closer intersection
+ && !floatEquals(newDist, 0) // and not a self intersection
+ )
+ {
+ minDist = newDist;
+
+ intersectedShape = shape;
+ closestIntersectionObj = newIntersectionObj;
+ closestIntersectionWorld = newIntersectionWorld;
+ }
+ }
+
+ if (minDist == FINF) // no hit
+ {
+ return glm::vec4(0.f);
+ }
+ }
+
+ glm::vec3 normalObject = getNormal(closestIntersectionObj, intersectedShape, scene);
+ glm::vec3 normalWorld =
+ (
+ glm::inverse(glm::transpose(intersectedShape.ctm))
+ * glm::vec4(normalObject, 0.f)
+ ).xyz();
+
+ return illuminatePixel(closestIntersectionWorld, normalWorld, -dWorld, intersectedShape, scene, depth);
+}
+
+// EXTRA CREDIT -> depth of field
+glm::vec4 RayTracer::secondaryRays(glm::vec4 pWorld, glm::vec4 dWorld, RayTraceScene &scene)
+{
+ auto inv = scene.getCamera().getInverseViewMatrix();
+ float focalLength = scene.getCamera().getFocalLength();
+ float aperture = scene.getCamera().getAperture();
+
+ glm::vec4 illumination(0.f);
+ glm::vec4 focalPoint = pWorld + focalLength * dWorld;
+
+ int TIMES = 500;
+ for (int i = 0; i < TIMES; i++) {
+ // generate a random number from -aperature to aperature
+ float rand1 = ((float) rand() / (float) RAND_MAX) * aperture;
+ rand1 *= (rand() % 2 == 0) ? 1 : -1;
+ // generate another number also inside the aperature lens
+ float rand2 = ((float) rand() / (float) RAND_MAX) * std::sqrt(aperture - rand1*rand1);
+ rand2 *= (rand() % 2 == 0) ? 1 : -1;
+ glm::vec4 randEye = (rand() % 2 == 0) ? glm::vec4(rand1, rand2, 0.f, 1.f) : glm::vec4(rand2, rand1, 0.f, 1.f);
+ // convert this random point to world space
+ glm::vec4 eyeWorld = inv * randEye;
+
+ // make the ray
+ glm::vec4 randomDir = glm::vec4(glm::normalize(focalPoint.xyz() - eyeWorld.xyz()), 0.f);
+
+ illumination += getPixelFromRay(eyeWorld, randomDir, scene, 0);
+ }
+
+ return illumination / (float) TIMES;
+} \ No newline at end of file
diff --git a/src/raytracer/raytracer.h b/src/raytracer/raytracer.h
new file mode 100644
index 0000000..6a16cdf
--- /dev/null
+++ b/src/raytracer/raytracer.h
@@ -0,0 +1,140 @@
+#pragma once
+
+#include <glm/glm.hpp>
+#include "utils/rgba.h"
+#include "utils/sceneparser.h"
+#include "raytracescene.h"
+#include "accelerate/kdtree.h"
+#include "accelerate/bvh.h"
+
+// A forward declaration for the RaytraceScene class
+
+class RayTraceScene;
+
+// A class representing a ray-tracer
+
+const float FINF = std::numeric_limits<float>::infinity();
+static float mediumIor = 1.0f;
+
+struct Config {
+ bool enableShadow = false;
+ bool enableReflection = false;
+ bool enableRefraction = false;
+ bool enableTextureMap = false;
+ bool enableTextureFilter = false;
+ bool enableParallelism = false;
+ bool enableSuperSample = false;
+ bool enableAntiAliasing = false;
+ bool enableAcceleration = false;
+ bool enableDepthOfField = false;
+ int maxRecursiveDepth = 4;
+ bool onlyRenderNormals = false;
+};
+
+class RayTracer
+{
+public:
+ // constructor for the config
+ explicit RayTracer(const Config &config);
+ const Config &m_config;
+
+ // Renders the scene synchronously.
+ // The ray-tracer will render the scene and fill imageData in-place.
+ // @param imageData The pointer to the imageData to be filled.
+ // @param scene The scene to be rendered.
+ void render(RGBA *imageData, const RayTraceScene &scene);
+
+ // shadow
+ bool isShadowed(glm::vec4 lightPosition, float distanceToLight, glm::vec4 directionFromIntersectionToLight,
+ glm::vec4 intersectionWorld, const RayTraceScene &scene);
+
+ // texture
+ glm::vec4 interpolateTexture(
+ glm::vec4 pObject,
+ const RenderShapeData &shape,
+ glm::vec4 illuminationToInterpolate);
+
+ glm::vec3 getNormal(
+ glm::vec4 intersectPointObject,
+ const RenderShapeData &shape,
+ const RayTraceScene &scene);
+
+ // ray tracing
+ glm::vec4 getPixelFromRay(
+ glm::vec4 pWorld,
+ glm::vec4 dWorld,
+ const RayTraceScene &scene,
+ int depth = 0);
+
+ // intersect
+ glm::vec4 findIntersection(
+ glm::vec4 p,
+ glm::vec4 d,
+ const RenderShapeData& shape);
+
+ // utils
+ static RGBA toRGBA(const glm::vec4 &illumination);
+ static bool floatEquals(float a, float b, float epsilon = 0.0001f);
+
+ // refracting, reflecting
+ glm::vec4 refract(
+ glm::vec3 intersectionWorld,
+ glm::vec3 normalWorld,
+ glm::vec3 incidentDir,
+ const RenderShapeData &shape,
+ const RayTraceScene &scene,
+ int depth);
+ glm::vec4 reflect(
+ glm::vec3 intersectionWorld,
+ glm::vec3 normalWorld,
+ glm::vec3 incidentDir,
+ const RenderShapeData &shape,
+ const RayTraceScene &scene,
+ int depth);
+ glm::vec4 illuminatePixel(
+ glm::vec3 intersectionWorld,
+ glm::vec3 normalWorld,
+ glm::vec3 directionToCamera,
+ const RenderShapeData &shape,
+ const RayTraceScene &scene,
+ int depth);
+
+
+ // shading, and helpers for each type of light
+ glm::vec4
+ phong(glm::vec4 lightColor, float attenuation, glm::vec3 directionFromIntersectionToLight,
+ glm::vec3 directionToCamera,
+ glm::vec3 intersectionWorld, glm::vec3 normalWorld, const RenderShapeData &shape, const RayTraceScene &scene);
+
+ glm::vec4
+ illuminationFromPointLight(const SceneLightData &light, glm::vec3 intersectionWorld, glm::vec3 normalWorld,
+ glm::vec3 directionToCamera, const RenderShapeData &shape,
+ const RayTraceScene &scene);
+
+ glm::vec4 illuminationFromSpotLight(const SceneLightData &light, glm::vec3 intersectionWorld, glm::vec3 normalWorld,
+ glm::vec3 directionToCamera, const RenderShapeData &shape,
+ const RayTraceScene &scene);
+
+ glm::vec4
+ illuminationFromDirectionalLight(const SceneLightData &light, glm::vec3 intersectionWorld, glm::vec3 normalWorld,
+ glm::vec3 directionToCamera, const RenderShapeData &shape,
+ const RayTraceScene &scene);
+
+ glm::vec4 illuminationFromAreaLight(const SceneLightData &light, glm::vec3 intersectionWorld, glm::vec3 normalWorld,
+ glm::vec3 directionToCamera, const RenderShapeData &shape,
+ const RayTraceScene &scene);
+
+
+ // acceleration data structures
+ void renderParallel(RGBA *imageData, const RayTraceScene &scene);
+ float traverse(glm::vec4 p, glm::vec4 d, float tStart, float tEnd, RenderShapeData &testShape, KdTree *tree);
+ float traverseBVH(glm::vec4 p, glm::vec4 d, RenderShapeData &testShape, bvh *root);
+
+ // aliasing
+ RGBA superSample(glm::vec4 eyeCamera, glm::vec4 pixelDirCamera, const RayTraceScene &scene);
+ void filterBlur(RGBA *imageData, int width, int height, float blurRadius = 3.f);
+
+ // depth of field
+ glm::vec4 secondaryRays(glm::vec4 pWorld, glm::vec4 dWorld, RayTraceScene &scene);
+};
+
diff --git a/src/raytracer/raytracescene.cpp b/src/raytracer/raytracescene.cpp
new file mode 100644
index 0000000..f70aa83
--- /dev/null
+++ b/src/raytracer/raytracescene.cpp
@@ -0,0 +1,56 @@
+#include <stdexcept>
+#include "raytracescene.h"
+#include "utils/sceneparser.h"
+#include "raytracer.h"
+#include <iostream>
+
+RayTraceScene::RayTraceScene(int width, int height, const RenderData &metaData) :
+ m_camera(* new Camera(metaData.cameraData))
+{
+ // Optional TODO: implement this. Store whatever you feel is necessary.
+ m_width = width;
+ m_height = height;
+ m_sceneGlobalData = metaData.globalData;
+ m_shapes = metaData.shapes;
+ m_lights = metaData.lights;
+
+ // populate the kd tree
+ m_kdTree = nullptr;
+ std::vector<KdShape> shapes;
+ for (const auto& shape : metaData.shapes) {
+ KdShape s{
+ shape,
+ KdTree::transformBoundingRegion(OBJECT_BOUNDS, shape.ctm)
+ };
+ shapes.push_back(s);
+ }
+ m_bvh = new bvh(shapes, 0);
+}
+
+const int& RayTraceScene::width() const {
+ // Optional TODO: implement the getter or make your own design
+ return m_width;
+}
+
+const int& RayTraceScene::height() const {
+ // Optional TODO: implement the getter or make your own design
+ return m_height;
+}
+
+const SceneGlobalData& RayTraceScene::getGlobalData() const {
+ // Optional TODO: implement the getter or make your own design
+ return m_sceneGlobalData;
+}
+
+const std::vector<RenderShapeData> RayTraceScene::getShapes() const {
+ return m_shapes;
+}
+
+const std::vector<SceneLightData> RayTraceScene::getLights() const {
+ return m_lights;
+}
+
+const Camera& RayTraceScene::getCamera() const {
+ // Optional TODO: implement the getter or make your own design
+ return m_camera;
+}
diff --git a/src/raytracer/raytracescene.h b/src/raytracer/raytracescene.h
new file mode 100644
index 0000000..b61bd2f
--- /dev/null
+++ b/src/raytracer/raytracescene.h
@@ -0,0 +1,42 @@
+#pragma once
+
+#include "utils/scenedata.h"
+#include "utils/sceneparser.h"
+#include "camera/camera.h"
+#include "accelerate/kdtree.h"
+#include "accelerate/bvh.h"
+
+// A class representing a scene to be ray-traced
+
+// Feel free to make your own design choices for RayTraceScene, the functions below are all optional / for your convenience.
+// You can either implement and use these getters, or make your own design.
+// If you decide to make your own design, feel free to delete these as TAs won't rely on them to grade your assignments.
+class RayTraceScene
+{
+public:
+ RayTraceScene(int width, int height, const RenderData &metaData);
+
+ // The getter of the width of the scene
+ const int& width() const;
+
+ // The getter of the height of the scene
+ const int& height() const;
+
+ // The getter of the global data of the scene
+ const SceneGlobalData& getGlobalData() const;
+ const std::vector<RenderShapeData> getShapes() const;
+ const std::vector<SceneLightData> getLights() const;
+
+ // The getter of the shared pointer to the camera instance of the scene
+ const Camera& getCamera() const;
+
+ KdTree *m_kdTree;
+ bvh *m_bvh;
+private:
+ int m_width;
+ int m_height;
+ SceneGlobalData m_sceneGlobalData;
+ Camera& m_camera;
+ std::vector<RenderShapeData>m_shapes;
+ std::vector<SceneLightData>m_lights;
+};