diff options
Diffstat (limited to 'src/raytracer')
-rw-r--r-- | src/raytracer/raytracer.cpp | 150 | ||||
-rw-r--r-- | src/raytracer/raytracer.h | 140 | ||||
-rw-r--r-- | src/raytracer/raytracescene.cpp | 56 | ||||
-rw-r--r-- | src/raytracer/raytracescene.h | 42 |
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; +}; |