Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // description: A business as usual raytracer that specializes in tracing spheres
- // references: Peter Shirley's raytracing series
- // compile: clang++ -std=c++14 -Wall -Oz Spherical.cpp -o Spherical
- // compile(debug): clang++ -DDEBUG_MODE -std=c++14 -g2 -O0 Spherical.cpp -o Spherical
- // run: ./Spherical [seed, default=0]
- #include <vector>
- #include <cstdlib> // atoi, time, exit status
- #include <limit> // epsilon
- #include <iostream>
- #include <functional> // closures
- #include <random> // uniform_real_dist<T>
- #include <glm/glm.hpp>
- using namespace std;
- using namespace glm;
- class RandomNumberGenerator {
- public:
- explicit RandomNumberGenerator(unsigned int seed = 0)
- : mSeed(seed) {
- }
- unsigned int getSeed() const {
- return mSeed;
- }
- void setSeed(unsigned int seed) {
- mSeed = seed;
- }
- float getRandomFloat(int low, int high) {
- mt19937 mt (mSeed);
- random_device rd (mt);
- uniform_real_distribution<float> dist (low, high);
- return dist(rd);
- }
- int getRandomInt(int low, int high) {
- mt19937 mt (mSeed);
- random_device rd (mt);
- uniform_real_distribution<int> dist (low, high);
- return dist(rd);
- }
- private:
- unsigned int mSeed;
- }; // class RandomNumberGenerator
- typedef RandomNumberGenerator Rng;
- class Ray {
- vec3 origin, direction;
- explicit Ray(const vec3 &o, const vec3 &d)
- : origin(o), direction(d) {
- }
- }; // class Ray
- class Camera {
- vec3 eye, front, up;
- explicit Camera(const glm::vec3 &eye,
- const glm::vec3 &front,
- const glm::vec3 &up)
- : eye(eye), front(front), up(up) {
- }
- void computeCameraCoordinates() {
- glm::vec3 front (glm::normalize(camera->getTarget() - camera->getEye()));
- glm::vec3 right (glm::normalize(glm::cross(front, camera->getUp())));
- glm::vec3 up (glm::cross(front, right));
- float tanHalfFov = glm::tan(glm::radians(camera->getFieldOfView() * 0.5f));
- float aspectRatio = static_cast<float>(resWidth) / static_cast<float>(resHeight);
- }
- }; // class Camera
- class Sphere {
- vec3 position;
- float radius;
- vec3 ambient, diffuse, specular;
- float shiny, reflectivity, indexOfRefraction;
- bool isLightSource;
- }; // class Sphere
- class Scene {
- public:
- static constexpr unsigned int RecursiveTraceLimit = 10;
- static constexpr unsigned int FrameWidth = 1980, FrameHeight = 1020;
- explicit Scene(unsigned int seed)
- : rng(seed)
- , pixels(FrameWidth * FrameHeight) {
- }
- void buildScene() {
- const std::size_t TotalSpheres = 400;
- for (unsigned int index = 0; index != TOTAL_SPHERES; ++index) {
- glm::vec3 ambient (rng.getRandomFloat(0.09f, 1.0f),
- rng.getRandomFloat(0.09f, 1.0f), rng.getRandomFloat(0.09f, 1.0f));
- glm::vec3 diffuse (Utils::getRandomFloat(0.1f, 0.90f),
- Utils::getRandomFloat(0.09f, 0.9f), Utils::getRandomFloat(0.09f, 0.9f));
- glm::vec3 specular (Utils::getRandomFloat(0.5f, 1.0f),
- Utils::getRandomFloat(0.5f, 1.0f), Utils::getRandomFloat(0.5f, 1.0f));
- float shiny = rng.getRandomFloat(10.0f, 300.0f);
- float refl = rng.getRandomFloat(0.05f, 1.0f);
- Material material (ambient, diffuse, specular,
- rng.getRandomFloat(10.0f, 300.0f),
- rng.getRandomFloat(0.05f, 1.0f),
- rng.getRandomFloat(0.05f, 1.0f));
- float angle = static_cast<float>(index) / static_cast<float>(TOTAL_SPHERES) * 360.0f;
- float displacement = Utils::getRandomFloat(-offset, offset);
- float xpos = glm::sin(angle) * imgCircleRadius + displacement;
- displacement = rng::getRandomFloat(-offset, offset);
- float y = std::abs(displacement) * 7.5f; // y value has smaller displacement
- displacement = rng::getRandomFloat(-offset, offset);
- float z = glm::cos(angle) * imgCircleRadius + displacement;
- glm::vec3 center = glm::vec3(xpos, y, z);
- float radius = rng.getRandomFloat(5.0f, 12.0f);
- spheres.emplace_back(center, radius, ambient, diffuse, specular, shiny, refl);
- }
- int pixels = 0;
- for (unsigned int i = 0; i != resHeight; ++i)
- {
- for (unsigned int j = 0; j != resWidth; ++j, ++pixels)
- {
- pixelColor = glm::vec3(0.0f);
- if (samples > 0)
- {
- for (int p = 0; p != samples; ++p)
- {
- for (int q = 0; q != samples; ++q)
- {
- float normalizedY = (static_cast<float>(i + (p + 0.5f) / samples) / static_cast<float>(resHeight - 1) * 2.0f - 1.0f) * tanHalfFov;
- float normalizedX = (static_cast<float>(j + (q + 0.5f) / samples) / static_cast<float>(resWidth - 1) * 2.0f - 1.0f) * aspectRatio * tanHalfFov;
- glm::vec3 imagePoint = camera->getEye() + front + (normalizedX * right) + (normalizedY * up);
- Ray ray (camera->getEye(), glm::normalize(imagePoint - camera->getEye()));
- pixelColor += mRayTracer.trace(mScene, ray, mScene.getRayBounces());
- }
- }
- pixelColor /= static_cast<float>(samples * samples);
- }
- else
- {
- float normalizedY = (static_cast<float>(i) / static_cast<float>(resHeight - 1) * 2.0f - 1.0f) * tanHalfFov;
- float normalizedX = (static_cast<float>(j) / static_cast<float>(resWidth - 1) * 2.0f - 1.0f) * aspectRatio * tanHalfFov;
- glm::vec3 imagePoint = camera->getEye() + front + (normalizedX * right) + (normalizedY * up);
- Ray ray (camera->getEye(), glm::normalize(imagePoint - camera->getEye()));
- pixelColor += trace(mScene, ray, mScene.getRayBounces());
- }
- mFrameBuffer.at(pixels) = pixelColor;
- }
- }
- }
- void trace(unsigned int traceCount = 0) {
- static constexpr float Epsilon = std::numeric_limits<float>::epsilon();
- static auto phongShading(float shadow) {
- glm::vec3 ambient, diffuse, specular;
- return ambient + shadow * (diffuse + specular);
- }
- // using namespace glm;
- vec3 finalColor (0);
- glm::vec3 finalColor (0.0f);
- static constexpr float colorFrac = 0.9999f; // fraction to add to final color
- for (int i = 0; i != maxRayBounces; ++i)
- {
- auto& camera = scene.getCamera();
- auto& primitives = scene.getPrimitives();
- auto& lights = scene.getLights();
- IPrimitive* hitPrimitive = nullptr;
- float tClosest = camera->getFarPlane();
- float tHit = 0.0f;
- // iterate through the objects and look for closest intersection
- for (auto& primitive : primitives)
- {
- if (primitive->intersect(ray, tHit) && (tHit > scEpsilon) && (tHit < tClosest))
- {
- tClosest = tHit;
- hitPrimitive = primitive.get();
- }
- }
- if (!hitPrimitive)
- {
- finalColor += colorFrac * scene.getBackgroundColor();
- break;
- }
- // otherwise there is a hit, so find the color @ the intersection point
- glm::vec3 intPoint (ray.origin + (ray.direction * tClosest));
- glm::vec3 intNormal = hitPrimitive->getNormal(intPoint);
- // make sure we didn't intersect from inside the hit primitive
- if (glm::dot(ray.direction, intNormal) > 0.0f)
- intNormal *= -1.0f;
- Material primitiveMaterial = hitPrimitive->getMaterial();
- if (hitPrimitive->getType() == IPrimitive::Type::PLANE)
- {
- primitiveMaterial = checkerboard(primitiveMaterial, intPoint);
- }
- glm::vec3 localColor (0.0f);
- // now compute phong shading
- for (auto& light : lights)
- {
- float shadow = 1.0f;
- glm::vec3 lightDir = glm::normalize(glm::vec3(light->getPosition()) - intPoint);
- Ray lightRay (intPoint + (intNormal * scEpsilon), lightDir);
- // check if color is in shadow
- for (auto& primitive : primitives)
- {
- if (primitive->intersect(lightRay, tHit) && (tHit > scEpsilon))
- {
- if (primitive->getType() == IPrimitive::Type::PLANE)
- {
- break;
- }
- shadow = 0.15f;
- }
- }
- glm::vec3 reflectDir = glm::normalize(glm::reflect(lightDir, intNormal));
- localColor += phongShading(light, primitiveMaterial, ray.direction, lightDir, intNormal, reflectDir, shadow);
- } // end lights
- finalColor += localColor * (1.0f - primitiveMaterial.getReflectiveness()) * colorFrac;
- colorFrac *= primitiveMaterial.getReflectiveness();
- // compute reflections
- if (primitiveMaterial.getReflectiveness() > 0.0f)
- {
- glm::vec3 reflectDir = glm::normalize(glm::reflect(ray.direction, intNormal));
- Ray reflectRay (intPoint + (intNormal * scEpsilon), reflectDir);
- ray = reflectRay;
- }
- // compute refractions
- if (primitiveMaterial.getIndexOfRefraction() > 0.0f)
- {
- // float ior = primitiveMaterial.getIndexOfRefraction();
- // glm::vec3 refractDir = glm::normalize(glm::refract(ray.direction, intNormal, ior));
- // Ray refractRay (intPoint + (refractDir * scEpsilon), refractDir);
- // ray = refractRay;
- // float ior = primitiveMaterial.getIndexOfRefraction();
- // float cosIntersect = -1.0f * glm::dot(intNormal, ray.direction);
- // float cosT2 = 1.0f - ior * ior * (1.0f - cosIntersect * cosIntersect);
- // if (cosT2 > 0.0f)
- // {
- // float iorFactor = ior * cosIntersect - glm::sqrt(cosT2);
- // glm::vec3 refractDir = glm::normalize(glm::vec3(ray.direction * ior + intNormal * iorFactor));
- // Ray refractRay (intPoint + (refractDir * scEpsilon), refractDir);
- // ray = refractRay;
- // }
- }
- if (colorFrac < 0.05f)
- break;
- } // end ray bounce for loop
- return finalColor;
- }
- // PPM format
- void printPixelsInPpmFormat() const {
- cout << "\nP6\n255\n" << endl;
- for (auto &pixel : pixels) {
- cout << pixel.x * 255.0 << \
- " " << pixel.y * 255.0 << \
- " " << pixel.z * 255.0 << endl;
- }
- }
- private:
- Rng rng;
- Camera camera;
- vector<Sphere> spheres;
- vector<vec3> pixels;
- }; // class Scene
- void handleArgumentsAndSetSeed(int argc, char **argv, unsigned int &seed) {
- if (argc == 1) {
- #if defined(DEBUG_MODE)
- cout << "Tracing with default seed" << endl;
- #endif
- }
- else if (argc == 2) {
- seed = atoi(argv[1]);
- } else {
- #if defined(DEBUG_MODE)
- cerr << "Arguments are incorrent. Expect \'./Spherical [seed]\'" << endl;
- #endif
- }
- }
- int main(int argc, char **argv) {
- const unsigned int seed = 0;
- handleArgumentsAndSetSeed(argc, argv, seed);
- time(nullptr);
- #if defined(DEBUG_MODE)
- cout << "Starting raytracer. Seed: " << seed << endl;
- #endif
- Scene scene (seed);
- scene.buildScene();
- scene.printPixelsInPpmFormat();
- return EXIT_SUCCESS;
- }
Add Comment
Please, Sign In to add comment