fixed the cross function in AVX, still incorrect output
parent
e320836909
commit
d10e9798dc
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,24 @@
|
|||
cmake_minimum_required(VERSION 3.23)
|
||||
project(Step_2)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
|
||||
# used to debug memory related issues
|
||||
if (UNIX AND (CMAKE_BUILD_TYPE MATCHES Debug))
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -g")
|
||||
add_compile_options(-fsanitize=address)
|
||||
add_link_options(-fsanitize=address)
|
||||
endif()
|
||||
|
||||
# enables AVX instructions. if you have issues, try disabling this.
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native")
|
||||
|
||||
#Setup project source compilation
|
||||
set(source_dir "${PROJECT_SOURCE_DIR}/src/")
|
||||
file(GLOB_RECURSE source_files "${source_dir}/*.cpp")
|
||||
file(GLOB_RECURSE source_c_files "${source_dir}/*.c")
|
||||
|
||||
#Setup project header files
|
||||
include_directories(${PROJECT_SOURCE_DIR}/include)
|
||||
|
||||
add_executable(${PROJECT_NAME} ${source_files} ${source_c_files})
|
|
@ -0,0 +1,6 @@
|
|||
#!/bin/bash
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Release ../
|
||||
make -j 16
|
||||
./Step_2
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Created by Brett Terpstra 6920201 on 14/10/22.
|
||||
* Copyright (c) Brett Terpstra 2022 All Rights Reserved
|
||||
*/
|
||||
|
||||
#ifndef STEP_2_IMAGE_H
|
||||
#define STEP_2_IMAGE_H
|
||||
|
||||
#include "util/std.h"
|
||||
#include "math/vectors.h"
|
||||
|
||||
namespace Raytracing {
|
||||
|
||||
// glorified structure to store our image data.
|
||||
class Image {
|
||||
private:
|
||||
int width;
|
||||
int height;
|
||||
Vec4 *pixelData;
|
||||
public:
|
||||
Image(int width, int height);
|
||||
Image(const Image &image);
|
||||
Image(const Image&& image) = delete;
|
||||
|
||||
inline void setPixelColor(int x, int y, const Vec4 &color) {
|
||||
pixelData[(x * height) + y] = color;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline Vec4 getPixelColor(int x, int y) const {
|
||||
return pixelData[(x * height) + y];
|
||||
}
|
||||
|
||||
[[nodiscard]] int getPixelR(int x, int y) const;
|
||||
[[nodiscard]] int getPixelG(int x, int y) const;
|
||||
[[nodiscard]] int getPixelB(int x, int y) const;
|
||||
[[nodiscard]] int getPixelA(int x, int y) const;
|
||||
|
||||
[[nodiscard]] inline int getWidth() const { return width; }
|
||||
|
||||
[[nodiscard]] inline int getHeight() const { return height; }
|
||||
|
||||
~Image();
|
||||
};
|
||||
|
||||
// image writer class used to output the image to a file.
|
||||
class ImageOutput {
|
||||
private:
|
||||
Image image;
|
||||
public:
|
||||
explicit ImageOutput(const Image& image): image(image) {}
|
||||
virtual void write(const std::string& file, const std::string& formatExtension);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //STEP_2_IMAGE_H
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
* Created by Brett Terpstra 6920201 on 17/10/22.
|
||||
* Copyright (c) 2022 Brett Terpstra. All Rights Reserved.
|
||||
*/
|
||||
|
||||
#ifndef STEP_2_BVH_H
|
||||
#define STEP_2_BVH_H
|
||||
|
||||
#include <util/std.h>
|
||||
#include <types.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
// A currently pure header implementation of a BVH. TODO: make source file.
|
||||
// this is also for testing and might not make it into the step 2.
|
||||
|
||||
namespace Raytracing {
|
||||
|
||||
struct BVHNode {
|
||||
public:
|
||||
std::vector<Object*> objs;
|
||||
AABB aabb;
|
||||
BVHNode* left;
|
||||
BVHNode* right;
|
||||
BVHNode(std::vector<Object*> objs, AABB aabb, BVHNode* left, BVHNode* right): objs(std::move(objs)), aabb(std::move(aabb)),
|
||||
left(left), right(right) {}
|
||||
~BVHNode() {
|
||||
delete (left);
|
||||
delete (right);
|
||||
}
|
||||
};
|
||||
|
||||
class BVHTree {
|
||||
private:
|
||||
const int MAX_TREE_DEPTH = 50;
|
||||
BVHNode* root = nullptr;
|
||||
|
||||
void del() {
|
||||
// delete copied objects
|
||||
for (auto* obj : root->objs)
|
||||
delete(obj);
|
||||
delete (root);
|
||||
}
|
||||
|
||||
// splits the objs in the vector based on the provided AABBs
|
||||
static std::pair<std::vector<Object*>, std::vector<Object*>>
|
||||
partition(const std::pair<AABB, AABB>& aabbs, const std::vector<Object*>& objs) {
|
||||
std::vector<Object*> a1;
|
||||
std::vector<Object*> a2;
|
||||
for (auto* obj: objs) {
|
||||
// if this object doesn't have an AABB, we cannot use a BVH on it
|
||||
if (obj->getAABB().isEmpty()) {
|
||||
throw std::runtime_error("Invalid AABB provided to the BVH! (Your implementation is flawed)");
|
||||
}
|
||||
if (obj->getAABB().intersects(aabbs.first)) {
|
||||
a1.push_back(obj);
|
||||
} else if (obj->getAABB().intersects(aabbs.second)) {
|
||||
a2.push_back(obj);
|
||||
}
|
||||
//tlog << "OBJ: " << obj->getAABB() << " " << obj->getAABB().intersects(aabbs.first) << " " << obj->getAABB().intersects(aabbs.second) << " " << objs.size() << "\n";
|
||||
}
|
||||
//tlog << "we split into two of sizes: " << a1.size() << " " << a2.size() << " orig size: " << (a1.size() + a2.size()) << "\n";
|
||||
return {a1, a2};
|
||||
}
|
||||
|
||||
BVHNode* addObjectsRecur(const std::vector<Object*>& objects, unsigned long prevSize) {
|
||||
//ilog << "size: " << objects.size() << "\n";
|
||||
// prevSize was required to solve some really weird bugs
|
||||
// which are a TODO:
|
||||
if ((objects.size() <= 2 && !objects.empty()) || prevSize == objects.size()) {
|
||||
AABB local;
|
||||
for (const auto& obj: objects)
|
||||
local = local.expand(obj->getAABB());
|
||||
return new BVHNode(objects, local, nullptr, nullptr);
|
||||
} else if (objects.empty()) // should never reach here!!
|
||||
return nullptr;
|
||||
// create a volume for the entire world.
|
||||
// yes, we could use the recursion provided AABB,
|
||||
// but that wouldn't be minimum, only half.
|
||||
// this ensures that we have a minimum AABB.
|
||||
AABB world;
|
||||
for (const auto& obj: objects) {
|
||||
//tlog << obj->getAABB();
|
||||
world = world.expand(obj->getAABB());
|
||||
}
|
||||
//tlog << "\n";
|
||||
// then split and partition the world
|
||||
auto spltAABB = world.splitByLongestAxis();
|
||||
//dlog << "We have " << world << " being split into: \n\t" << spltAABB.first << "\n\t" << spltAABB.second << "\n";
|
||||
auto partitionedObjs = partition(spltAABB, objects);
|
||||
|
||||
BVHNode* left = nullptr;
|
||||
BVHNode* right = nullptr;
|
||||
// don't try to explore nodes which don't have anything in them.
|
||||
if (!partitionedObjs.first.empty())
|
||||
left = addObjectsRecur(partitionedObjs.first, objects.size());
|
||||
if (!partitionedObjs.second.empty())
|
||||
right = addObjectsRecur(partitionedObjs.second, objects.size());
|
||||
|
||||
return new BVHNode(objects, world, left, right);
|
||||
}
|
||||
static std::vector<Object*>
|
||||
traverseFindRayIntersection(BVHNode* node, const Ray& ray, PRECISION_TYPE min, PRECISION_TYPE max) {
|
||||
// check for intersections on both sides of the tree
|
||||
if (node->left != nullptr) {
|
||||
if (node->left->aabb.intersects(ray, min, max))
|
||||
return traverseFindRayIntersection(node->left, ray, min, max);
|
||||
}
|
||||
// since each aabb should be minimum, we shouldn't have to traverse both sides.
|
||||
// we want to reduce our problem size by half each iteration anyways
|
||||
// divide and conquer and so on
|
||||
if (node->right != nullptr)
|
||||
if (node->right->aabb.intersects(ray, min, max))
|
||||
return traverseFindRayIntersection(node->left, ray, min, max);
|
||||
// return the objects of the lowest BVH node we can find
|
||||
// if this is implemented properly this should only contain one, maybe two objects
|
||||
// which is much faster! (especially when dealing with triangles)
|
||||
return node->objs;
|
||||
}
|
||||
public:
|
||||
std::vector<Object*> noAABBObjects;
|
||||
explicit BVHTree(const std::vector<Object*>& objectsInWorld) {
|
||||
addObjects(objectsInWorld);
|
||||
}
|
||||
|
||||
void addObjects(const std::vector<Object*>& objects) {
|
||||
if (root != nullptr)
|
||||
del();
|
||||
// move all the object's aabb's into world position
|
||||
std::vector<Object*> objs;
|
||||
for (auto* obj: objects) {
|
||||
// we don't want to store all the AABBs which don't exist
|
||||
// ie spheres
|
||||
if (obj->getAABB().isEmpty()) {
|
||||
//tlog << "Goodbye\n";
|
||||
noAABBObjects.push_back(obj);
|
||||
continue;
|
||||
}
|
||||
Object* objCopy = obj->clone();
|
||||
objCopy->setAABB(obj->getAABB().translate(obj->getPosition()));
|
||||
objs.push_back(objCopy);
|
||||
}
|
||||
root = addObjectsRecur(objs, -1);
|
||||
}
|
||||
|
||||
std::vector<Object*> rayIntersect(const Ray& ray, PRECISION_TYPE min, PRECISION_TYPE max) {
|
||||
return traverseFindRayIntersection(root, ray, min, max);
|
||||
}
|
||||
|
||||
~BVHTree() {
|
||||
del();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //STEP_2_BVH_H
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* Created by Brett Terpstra 6920201 on 17/10/22.
|
||||
* Copyright (c) 2022 Brett Terpstra. All Rights Reserved.
|
||||
*/
|
||||
|
||||
#ifndef STEP_2_COLLIDERS_H
|
||||
#define STEP_2_COLLIDERS_H
|
||||
|
||||
#include <math/vectors.h>
|
||||
|
||||
namespace Raytracing {
|
||||
// 3D Axis Aligned Bounding Box for use in a BVH
|
||||
class AABB {
|
||||
protected:
|
||||
Vec4 min;
|
||||
Vec4 max;
|
||||
bool empty = false;
|
||||
public:
|
||||
AABB() {
|
||||
empty = true;
|
||||
};
|
||||
|
||||
AABB(PRECISION_TYPE minX, PRECISION_TYPE minY, PRECISION_TYPE minZ, PRECISION_TYPE maxX, PRECISION_TYPE maxY, PRECISION_TYPE maxZ):
|
||||
min{minX, minY, minZ}, max{maxX, maxY, maxZ} {
|
||||
}
|
||||
|
||||
AABB(const Vec4& min, const Vec4& max): min(min), max(max) {}
|
||||
|
||||
// creates an AABB extending of size centered on x, y, z
|
||||
AABB(PRECISION_TYPE x, PRECISION_TYPE y, PRECISION_TYPE z, PRECISION_TYPE size):
|
||||
min{x - size, y - size, z - size}, max{x + size, y + size, z + size} {
|
||||
}
|
||||
|
||||
// translates the AABB to position x,y,z for world collision detection
|
||||
[[nodiscard]] AABB translate(PRECISION_TYPE x, PRECISION_TYPE y, PRECISION_TYPE z) const {
|
||||
Vec4 pos = {x, y, z};
|
||||
return {min + pos, max + pos};
|
||||
}
|
||||
|
||||
[[nodiscard]] AABB translate(const Vec4& vec) const {
|
||||
Vec4 pos = {vec.x(), vec.y(), vec.z()};
|
||||
return {min + pos, max + pos};
|
||||
}
|
||||
|
||||
// returns an expanded version of this AABB is the other AABB is larger then this AABB
|
||||
[[nodiscard]] AABB expand(const AABB& other) const {
|
||||
PRECISION_TYPE minX = std::min(min.x(), other.min.x());
|
||||
PRECISION_TYPE minY = std::min(min.y(), other.min.y());
|
||||
PRECISION_TYPE minZ = std::min(min.z(), other.min.z());
|
||||
PRECISION_TYPE maxX = std::max(max.x(), other.max.x());
|
||||
PRECISION_TYPE maxY = std::max(max.y(), other.max.y());
|
||||
PRECISION_TYPE maxZ = std::max(max.z(), other.max.z());
|
||||
return {minX, minY, minZ, maxX, maxY, maxZ};
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool
|
||||
intersects(PRECISION_TYPE minX, PRECISION_TYPE minY, PRECISION_TYPE minZ, PRECISION_TYPE maxX, PRECISION_TYPE maxY,
|
||||
PRECISION_TYPE maxZ) const {
|
||||
return min.x() <= maxX && max.x() >= minX && min.y() <= maxY && max.y() >= minY && min.z() <= maxZ && max.z() >= minZ;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool intersects(const Vec4& minV, const Vec4& maxV) const {
|
||||
return intersects(minV.x(), minV.y(), minV.z(), maxV.x(), maxV.y(), maxV.z());
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool intersects(const AABB& other) const {
|
||||
return intersects(other.min, other.max);
|
||||
}
|
||||
|
||||
bool intersects(const Ray& ray, PRECISION_TYPE tmin, PRECISION_TYPE tmax);
|
||||
bool simpleSlabRayAABBMethod(const Ray& ray, PRECISION_TYPE tmin, PRECISION_TYPE tmax);
|
||||
|
||||
[[nodiscard]] inline bool isInside(PRECISION_TYPE x, PRECISION_TYPE y, PRECISION_TYPE z) const {
|
||||
return x >= min.x() && x <= max.x() && y >= min.y() && y <= max.y() && z >= min.z() && z <= max.z();
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool intersectsWithYZ(PRECISION_TYPE y, PRECISION_TYPE z) const {
|
||||
return y >= min.y() && y <= max.y() && z >= min.z() && z <= max.z();
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool intersectsWithXZ(PRECISION_TYPE x, PRECISION_TYPE z) const {
|
||||
return x >= min.x() && x <= max.x() && z >= min.z() && z <= max.z();
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool intersectsWithXY(PRECISION_TYPE x, PRECISION_TYPE y) const {
|
||||
return x >= min.x() && x <= max.x() && y >= min.y() && y <= max.y();
|
||||
}
|
||||
|
||||
[[nodiscard]] inline Vec4 getCenter() const {
|
||||
return {min.x() + (max.x() - min.x()) * 0.5, min.y() + (max.y() - min.y()) * 0.5, min.z() + (max.z() - min.z()) * 0.5};
|
||||
}
|
||||
|
||||
[[nodiscard]] PRECISION_TYPE longestDistanceFromCenter() const;
|
||||
// 0 - x
|
||||
// 1 - y
|
||||
// 2 - z
|
||||
[[nodiscard]] int longestAxis() const;
|
||||
[[nodiscard]] PRECISION_TYPE longestAxisLength() const;
|
||||
[[nodiscard]] std::pair<AABB, AABB> splitByLongestAxis();
|
||||
|
||||
[[nodiscard]] PRECISION_TYPE avgDistanceFromCenter() const;
|
||||
|
||||
[[nodiscard]] inline bool isEmpty() const { return empty; }
|
||||
|
||||
[[nodiscard]] Vec4 getMin() const { return min; }
|
||||
|
||||
[[nodiscard]] Vec4 getMax() const { return max; }
|
||||
|
||||
};
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& out, const AABB& v) {
|
||||
return out << "AABB{min{" << v.getMin().x() << ", " << v.getMin().y() << ", " << v.getMin().z() << "}, max{" << v.getMax().x() << ", " << v.getMax().y()
|
||||
<< ", " << v.getMax().z() << "}} ";
|
||||
}
|
||||
}
|
||||
|
||||
#endif //STEP_2_COLLIDERS_H
|
|
@ -0,0 +1,355 @@
|
|||
/*
|
||||
* Created by Brett Terpstra 6920201 on 14/10/22.
|
||||
* Copyright (c) Brett Terpstra 2022 All Rights Reserved
|
||||
*/
|
||||
|
||||
#ifndef STEP_2_VECTORS_H
|
||||
#define STEP_2_VECTORS_H
|
||||
|
||||
// AVX512 isn't supported on my CPU. We will use AVX2 since it is supported by most modern CPUs
|
||||
// THIS IS CURRENTLY BROKEN. DO NOT USE. it's a great idea, but I've run out of time to debug this. Will be in step 3
|
||||
//#define USE_SIMD_CPU
|
||||
|
||||
#ifdef USE_SIMD_CPU
|
||||
|
||||
#include <immintrin.h>
|
||||
|
||||
#endif
|
||||
|
||||
#include <cmath>
|
||||
#include "util/std.h"
|
||||
|
||||
namespace Raytracing {
|
||||
|
||||
// when running on the CPU it's fine to be a double
|
||||
// Your CPU may be faster with floats.
|
||||
// but if we move to the GPU it has to be a float.
|
||||
// since GPUs generally are far more optimized for floats
|
||||
// If using AVX or other SIMD instructions it should be double, only to fit into 256bits.
|
||||
// TODO would be to add support for 128bit AVX vectors.
|
||||
|
||||
#ifdef USE_SIMD_CPU
|
||||
// don't change this. (working on a float version)
|
||||
typedef double PRECISION_TYPE;
|
||||
|
||||
union AVXConvert {
|
||||
struct {
|
||||
double _x, _y, _z, _w;
|
||||
};
|
||||
__m256d avxData;
|
||||
};
|
||||
|
||||
class Vec4 {
|
||||
private:
|
||||
// makes it easy to convert between AVX and double data types.
|
||||
union {
|
||||
struct {
|
||||
double _x, _y, _z, _w;
|
||||
};
|
||||
__m256d avxData;
|
||||
};
|
||||
|
||||
// finally a use for friend!
|
||||
friend Vec4 operator+(const Vec4& left, const Vec4& right);
|
||||
friend Vec4 operator-(const Vec4& left, const Vec4& right);
|
||||
friend Vec4 operator*(const Vec4& left, const Vec4& right);
|
||||
friend Vec4 operator/(const Vec4& left, const Vec4& right);
|
||||
public:
|
||||
|
||||
Vec4(): avxData{_mm256_set_pd(0.0, 0.0, 0.0, 0.0)} {}
|
||||
Vec4(PRECISION_TYPE x, PRECISION_TYPE y, PRECISION_TYPE z): avxData{_mm256_set_pd(x, y, z, 0)} {}
|
||||
Vec4(PRECISION_TYPE x, PRECISION_TYPE y, PRECISION_TYPE z, PRECISION_TYPE w): avxData{_mm256_set_pd(x, y, z, w)} {}
|
||||
Vec4(const Vec4& vec): avxData{_mm256_set_pd(vec.x(), vec.y(), vec.z(), vec.w())} {}
|
||||
|
||||
// most of the modern c++ here is because clang tidy was annoying me
|
||||
[[nodiscard]] inline PRECISION_TYPE x() const { return _x; }
|
||||
|
||||
[[nodiscard]] inline PRECISION_TYPE y() const { return _y; }
|
||||
|
||||
[[nodiscard]] inline PRECISION_TYPE z() const { return _z; }
|
||||
|
||||
[[nodiscard]] inline PRECISION_TYPE w() const { return _w; }
|
||||
|
||||
[[nodiscard]] inline PRECISION_TYPE r() const { return _x; }
|
||||
|
||||
[[nodiscard]] inline PRECISION_TYPE g() const { return _y; }
|
||||
|
||||
[[nodiscard]] inline PRECISION_TYPE b() const { return _z; }
|
||||
|
||||
[[nodiscard]] inline PRECISION_TYPE a() const { return _w; }
|
||||
|
||||
// negation operator
|
||||
Vec4 operator-() const { return {-x(), -y(), -z(), -w()}; }
|
||||
|
||||
[[nodiscard]] inline PRECISION_TYPE magnitude() const {
|
||||
return sqrt(lengthSquared());
|
||||
}
|
||||
|
||||
[[nodiscard]] inline PRECISION_TYPE lengthSquared() const {
|
||||
return Vec4::dot(*this, *this);
|
||||
}
|
||||
|
||||
// returns the unit-vector.
|
||||
[[nodiscard]] inline Vec4 normalize() const {
|
||||
PRECISION_TYPE mag = magnitude();
|
||||
return {x() / mag, y() / mag, z() / mag, w() / mag};
|
||||
}
|
||||
|
||||
// add operator before the vec returns the magnitude
|
||||
PRECISION_TYPE operator+() const {
|
||||
return magnitude();
|
||||
}
|
||||
|
||||
// preforms the dot product of left * right
|
||||
static inline PRECISION_TYPE dot(const Vec4& left, const Vec4& right) {
|
||||
// multiply the elements of the vectors
|
||||
__m256d mul = _mm256_mul_pd(left.avxData, right.avxData);
|
||||
// horizontal add. element 0 and 2 (or 1 and 3) contain the results which we must scalar add.
|
||||
__m256d sum = _mm256_hadd_pd(mul, mul);
|
||||
AVXConvert conv {};
|
||||
conv.avxData = sum;
|
||||
// boom! dot product. much easier than cross
|
||||
return conv._x + conv._y;
|
||||
}
|
||||
|
||||
// preforms the cross product of left X right
|
||||
// since a general solution to the cross product doesn't exist in 4d
|
||||
// we are going to ignore the w.
|
||||
static inline Vec4 cross(const Vec4& left, const Vec4& right) {
|
||||
// shuffle left values for alignment with the cross algorithm
|
||||
// (read the shuffle selector from right to left) takes the y and places it in the first element of the resultant vector
|
||||
// takes the z and places it in the second element of the vector
|
||||
// takes the x element and places it in the 3rd element of the vector
|
||||
// and then the w element in the last element of the vector
|
||||
// creating the alignment {left.y(), left.z(), left.x(), left.w()} (as seen in the cross algorithm
|
||||
__m256d leftLeftShuffle = _mm256_shuffle_pd(left.avxData, left.avxData, _MM_SHUFFLE(3,0,2,1));
|
||||
// same thing but produces {right.z(), right.x(), right.y(), right.w()}
|
||||
__m256d rightLeftShuffle = _mm256_shuffle_pd(right.avxData, right.avxData, _MM_SHUFFLE(3,1,0,2));
|
||||
// multiply to do the first step of the cross process
|
||||
__m256d multiLeft = _mm256_mul_pd(leftLeftShuffle, rightLeftShuffle);
|
||||
// now we have to do the right side multiplications
|
||||
// {left.z(), left.x(), left.y(), left.w()}
|
||||
__m256d leftRightShuffle = _mm256_shuffle_pd(left.avxData, left.avxData, _MM_SHUFFLE(3,1,0,2));
|
||||
// {right.y(), right.z(), right.x(), right.w()}
|
||||
__m256d rightRightShuffle = _mm256_shuffle_pd(right.avxData, right.avxData, _MM_SHUFFLE(3,0,2,1));
|
||||
// multiply the right sides of the subtraction sign
|
||||
__m256d multiRight = _mm256_mul_pd(leftRightShuffle, rightRightShuffle);
|
||||
// then subtract to produce the cross product
|
||||
__m256d subs = _mm256_sub_pd(multiLeft, multiRight);
|
||||
// yes this looks a lot more complicated, but it should be faster!
|
||||
AVXConvert conv {};
|
||||
conv.avxData = subs;
|
||||
return {conv._x, conv._y, conv._z, conv._w};
|
||||
}
|
||||
};
|
||||
|
||||
// adds the two vectors left and right
|
||||
inline Vec4 operator+(const Vec4& left, const Vec4& right) {
|
||||
__m256d added = _mm256_add_pd(left.avxData, right.avxData);
|
||||
AVXConvert conv {};
|
||||
conv.avxData = added;
|
||||
return {conv._x, conv._y, conv._z, conv._w};
|
||||
}
|
||||
|
||||
// subtracts the right vector from the left.
|
||||
inline Vec4 operator-(const Vec4& left, const Vec4& right) {
|
||||
__m256d subbed = _mm256_sub_pd(left.avxData, right.avxData);
|
||||
AVXConvert conv {};
|
||||
conv.avxData = subbed;
|
||||
return {conv._x, conv._y, conv._z, conv._w};
|
||||
}
|
||||
|
||||
// multiples the left with the right
|
||||
inline Vec4 operator*(const Vec4& left, const Vec4& right) {
|
||||
__m256d multiplied = _mm256_mul_pd(left.avxData, right.avxData);
|
||||
AVXConvert conv {};
|
||||
conv.avxData = multiplied;
|
||||
return {conv._x, conv._y, conv._z, conv._w};
|
||||
}
|
||||
|
||||
// divides each element individually
|
||||
inline Vec4 operator/(const Vec4& left, const Vec4& right) {
|
||||
__m256d dived = _mm256_div_pd(left.avxData, right.avxData);
|
||||
AVXConvert conv {};
|
||||
conv.avxData = dived;
|
||||
return {conv._x, conv._y, conv._z, conv._w};
|
||||
}
|
||||
|
||||
#else
|
||||
// change this if you want
|
||||
typedef double PRECISION_TYPE;
|
||||
|
||||
class Vec4 {
|
||||
private:
|
||||
union xType {
|
||||
PRECISION_TYPE x;
|
||||
PRECISION_TYPE r;
|
||||
};
|
||||
union yType {
|
||||
PRECISION_TYPE y;
|
||||
PRECISION_TYPE g;
|
||||
};
|
||||
union zType {
|
||||
PRECISION_TYPE z;
|
||||
PRECISION_TYPE b;
|
||||
};
|
||||
union wType {
|
||||
PRECISION_TYPE w;
|
||||
PRECISION_TYPE a;
|
||||
};
|
||||
|
||||
struct valueType {
|
||||
xType v1;
|
||||
yType v2;
|
||||
zType v3;
|
||||
wType v4;
|
||||
};
|
||||
// isn't much of a reason to do it this way
|
||||
// it's unlikely that we'll need to use the w component
|
||||
// but it helps better line up with the GPU and other SIMD type instructions, like what's above.
|
||||
valueType value;
|
||||
public:
|
||||
Vec4(): value{0, 0, 0, 0} {}
|
||||
Vec4(PRECISION_TYPE x, PRECISION_TYPE y, PRECISION_TYPE z): value{x, y, z, 0} {}
|
||||
Vec4(PRECISION_TYPE x, PRECISION_TYPE y, PRECISION_TYPE z, PRECISION_TYPE w): value{x, y, z, w} {}
|
||||
Vec4(const Vec4& vec): value{vec.x(), vec.y(), vec.z(), vec.w()} {}
|
||||
|
||||
|
||||
// most of the modern c++ here is because clang tidy was annoying me
|
||||
[[nodiscard]] inline PRECISION_TYPE x() const { return value.v1.x; }
|
||||
|
||||
[[nodiscard]] inline PRECISION_TYPE y() const { return value.v2.y; }
|
||||
|
||||
[[nodiscard]] inline PRECISION_TYPE z() const { return value.v3.z; }
|
||||
|
||||
[[nodiscard]] inline PRECISION_TYPE w() const { return value.v4.w; }
|
||||
|
||||
[[nodiscard]] inline PRECISION_TYPE r() const { return value.v1.r; }
|
||||
|
||||
[[nodiscard]] inline PRECISION_TYPE g() const { return value.v2.g; }
|
||||
|
||||
[[nodiscard]] inline PRECISION_TYPE b() const { return value.v3.b; }
|
||||
|
||||
[[nodiscard]] inline PRECISION_TYPE a() const { return value.v4.a; }
|
||||
|
||||
// negation operator
|
||||
Vec4 operator-() const { return {-x(), -y(), -z(), -w()}; }
|
||||
|
||||
[[nodiscard]] inline PRECISION_TYPE magnitude() const {
|
||||
return sqrt(lengthSquared());
|
||||
}
|
||||
|
||||
[[nodiscard]] inline PRECISION_TYPE lengthSquared() const {
|
||||
return x() * x() + y() * y() + z() * z() + w() * w();
|
||||
}
|
||||
|
||||
// returns the unit-vector.
|
||||
[[nodiscard]] inline Vec4 normalize() const {
|
||||
PRECISION_TYPE mag = magnitude();
|
||||
return {x() / mag, y() / mag, z() / mag, w() / mag};
|
||||
}
|
||||
|
||||
// add operator before the vec returns the magnitude
|
||||
PRECISION_TYPE operator+() const {
|
||||
return magnitude();
|
||||
}
|
||||
|
||||
// preforms the dot product of left * right
|
||||
static inline PRECISION_TYPE dot(const Vec4& left, const Vec4& right) {
|
||||
return left.x() * right.x()
|
||||
+ left.y() * right.y()
|
||||
+ left.z() * right.z();
|
||||
}
|
||||
|
||||
// preforms the cross product of left X right
|
||||
// since a general solution to the cross product doesn't exist in 4d
|
||||
// we are going to ignore the w.
|
||||
static inline Vec4 cross(const Vec4& left, const Vec4& right) {
|
||||
return {left.y() * right.z() - left.z() * right.y(),
|
||||
left.z() * right.x() - left.x() * right.z(),
|
||||
left.x() * right.y() - left.y() * right.x()};
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// Utility Functions
|
||||
|
||||
// adds the two vectors left and right
|
||||
inline Vec4 operator+(const Vec4& left, const Vec4& right) {
|
||||
return {left.x() + right.x(), left.y() + right.y(), left.z() + right.z(), left.w() + right.w()};
|
||||
}
|
||||
|
||||
// subtracts the right vector from the left.
|
||||
inline Vec4 operator-(const Vec4& left, const Vec4& right) {
|
||||
return {left.x() - right.x(), left.y() - right.y(), left.z() - right.z(), left.w() - right.w()};
|
||||
}
|
||||
|
||||
// multiples the left with the right
|
||||
inline Vec4 operator*(const Vec4& left, const Vec4& right) {
|
||||
return {left.x() * right.x(), left.y() * right.y(), left.z() * right.z(), left.w() * right.w()};
|
||||
}
|
||||
|
||||
// divides each element individually
|
||||
inline Vec4 operator/(const Vec4& left, const Vec4& right) {
|
||||
return {left.x() / right.x(), left.y() / right.y(), left.z() / right.z(), left.w() / right.w()};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// none of these can be vectorized with AVX instructions
|
||||
|
||||
// useful for printing out the vector to stdout
|
||||
inline std::ostream& operator<<(std::ostream& out, const Vec4& v) {
|
||||
return out << "Vec4{" << v.x() << ", " << v.y() << ", " << v.z() << ", " << v.w() << "} ";
|
||||
}
|
||||
|
||||
// multiplies the const c with each element in the vector v
|
||||
inline Vec4 operator*(const PRECISION_TYPE c, const Vec4& v) {
|
||||
return {c * v.x(), c * v.y(), c * v.z(), c * v.w()};
|
||||
}
|
||||
|
||||
// same as above but for right sided constants
|
||||
inline Vec4 operator*(const Vec4& v, PRECISION_TYPE c) {
|
||||
return c * v;
|
||||
}
|
||||
|
||||
// divides the vector by the constant c
|
||||
inline Vec4 operator/(const Vec4& v, PRECISION_TYPE c) {
|
||||
return {v.x() / c, v.y() / c, v.z() / c, v.w() / c};
|
||||
}
|
||||
|
||||
// divides each element in the vector by over the constant
|
||||
inline Vec4 operator/(PRECISION_TYPE c, const Vec4& v) {
|
||||
return {c / v.x(), c / v.y(), c / v.z(), c / v.w()};
|
||||
}
|
||||
|
||||
class Ray {
|
||||
private:
|
||||
// the starting point for our ray
|
||||
Vec4 start;
|
||||
// and the direction it is currently traveling
|
||||
Vec4 direction;
|
||||
Vec4 inverseDirection;
|
||||
public:
|
||||
Ray(const Vec4& start, const Vec4& direction): start(start), direction(direction), inverseDirection(1 / direction) {}
|
||||
|
||||
[[nodiscard]] Vec4 getStartingPoint() const { return start; }
|
||||
|
||||
[[nodiscard]] Vec4 getDirection() const { return direction; }
|
||||
|
||||
// not always needed, but it's good to not have to calculate the inverse inside the intersection
|
||||
// as that would be very every AABB, and that is expensive
|
||||
[[nodiscard]] Vec4 getInverseDirection() const { return inverseDirection; }
|
||||
|
||||
// returns a point along the ray, extended away from start by the length.
|
||||
[[nodiscard]] inline Vec4 along(PRECISION_TYPE length) const { return start + length * direction; }
|
||||
|
||||
};
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& out, const Ray& v) {
|
||||
return out << "Ray{" << v.getStartingPoint() << " " << v.getDirection() << "} ";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif //STEP_2_VECTORS_H
|
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* Created by Brett Terpstra 6920201 on 16/10/22.
|
||||
* Copyright (c) 2022 Brett Terpstra. All Rights Reserved.
|
||||
*
|
||||
* The general class for all things raytracing!
|
||||
*/
|
||||
|
||||
#ifndef STEP_2_RAYTRACING_H
|
||||
#define STEP_2_RAYTRACING_H
|
||||
|
||||
#include <math/vectors.h>
|
||||
#include <image/image.h>
|
||||
#include <util/parser.h>
|
||||
#include <world.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace Raytracing {
|
||||
|
||||
class Camera {
|
||||
private:
|
||||
/* Image details */
|
||||
const Image image;
|
||||
const PRECISION_TYPE aspectRatio;
|
||||
|
||||
/* Camera details */
|
||||
PRECISION_TYPE viewportHeight;
|
||||
PRECISION_TYPE viewportWidth;
|
||||
PRECISION_TYPE focalLength = 1.0;
|
||||
|
||||
Vec4 position{0, 0, 0};
|
||||
Vec4 horizontalAxis;
|
||||
Vec4 verticalAxis;
|
||||
Vec4 imageOrigin;
|
||||
public:
|
||||
Camera(PRECISION_TYPE fov, const Image& image): image(image),
|
||||
aspectRatio(double(image.getWidth()) / double(image.getHeight())) {
|
||||
// scale the viewport height based on the camera's FOV
|
||||
viewportHeight = (2.0 * tan(degreeeToRadian(fov) / 2));
|
||||
// with must respect the aspect ratio of the image, otherwise we'd get funky results
|
||||
viewportWidth = (aspectRatio * viewportHeight);
|
||||
// horizontal direction from the camera. used to translate the camera
|
||||
horizontalAxis = (Vec4{viewportWidth, 0, 0, 0});
|
||||
// virtual direction, also used to translate the camera
|
||||
verticalAxis = (Vec4{0, viewportHeight, 0, 0});
|
||||
// lower left of the camera's view port. used to project our vectors from image space to world space
|
||||
imageOrigin = (position - horizontalAxis / 2 - verticalAxis / 2 - Vec4(0, 0, focalLength, 0));
|
||||
|
||||
tlog << viewportHeight << "\n";
|
||||
tlog << viewportWidth << "\n";
|
||||
tlog << "\n";
|
||||
tlog << horizontalAxis << "\n";
|
||||
tlog << verticalAxis << "\n";
|
||||
tlog << imageOrigin << "\n";
|
||||
|
||||
}
|
||||
|
||||
Ray projectRay(PRECISION_TYPE x, PRECISION_TYPE y);
|
||||
// makes the camera look at the lookatpos from the position p, with respects to the up direction up. (set to 0,1,0)
|
||||
void lookAt(const Vec4& pos, const Vec4& lookAtPos, const Vec4& up);
|
||||
|
||||
void setPosition(const Vec4& pos) { this->position = pos; }
|
||||
|
||||
void setRotation(PRECISION_TYPE yaw, PRECISION_TYPE pitch, PRECISION_TYPE roll);
|
||||
|
||||
};
|
||||
|
||||
static Random rnd{-1, 1};
|
||||
|
||||
class Raycaster {
|
||||
private:
|
||||
const int maxBounceDepth = 50;
|
||||
// 50 seems to be the magic number for the point of diminishing returns
|
||||
// 100 looks like 50 but slightly clearer
|
||||
// 25 is noisy
|
||||
// 1 is VERY noisy.
|
||||
const int raysPerPixel = 50;
|
||||
|
||||
Camera& camera;
|
||||
Image& image;
|
||||
World& world;
|
||||
|
||||
Vec4 raycast(const Ray& ray, int depth);
|
||||
public:
|
||||
inline static Vec4 randomUnitVector() {
|
||||
// there are two methods to generating a random unit sphere
|
||||
// one which is fast and approximate:
|
||||
auto v = Vec4(rnd.getDouble(), rnd.getDouble(), rnd.getDouble());
|
||||
return v.normalize();
|
||||
// and the one which generates an actual unit vector
|
||||
/*while (true) {
|
||||
auto v = Vec4(rnd.getDouble(), rnd.getDouble(), rnd.getDouble());
|
||||
if (v.lengthSquared() >= 1)
|
||||
continue;
|
||||
return v;
|
||||
}*/
|
||||
// the second creates better results but is 18% slower (better defined shadows)
|
||||
// likely due to not over generating unit vectors biased towards the corners
|
||||
}
|
||||
Raycaster(Camera& c, Image& i, World& world, const Parser& p): camera(c), image(i), world(world) {
|
||||
world.generateBVH();
|
||||
}
|
||||
|
||||
void run();
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
#endif //STEP_2_RAYTRACING_H
|
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
* Created by Brett Terpstra 6920201 on 18/10/22.
|
||||
* Copyright (c) 2022 Brett Terpstra. All Rights Reserved.
|
||||
*/
|
||||
|
||||
#ifndef STEP_2_TYPES_H
|
||||
#define STEP_2_TYPES_H
|
||||
|
||||
#include <math/vectors.h>
|
||||
#include <math/colliders.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
// there were some files which needed access to these types
|
||||
// but including them from world.h would've resulted in circular includes,
|
||||
// so I moved them here.
|
||||
|
||||
namespace Raytracing {
|
||||
|
||||
struct HitData {
|
||||
// all the other values only matter if this is true
|
||||
bool hit{false};
|
||||
// the hit point on the object
|
||||
Vec4 hitPoint{};
|
||||
// the normal of that hit point
|
||||
Vec4 normal{};
|
||||
// the length of the vector from its origin in its direction.
|
||||
PRECISION_TYPE length{0};
|
||||
};
|
||||
|
||||
struct ScatterResults {
|
||||
// returns true to recast the ray with the provided ray
|
||||
bool scattered;
|
||||
// the new ray to be cast if scattered
|
||||
Ray newRay;
|
||||
// the color of the material
|
||||
Vec4 attenuationColor;
|
||||
};
|
||||
|
||||
// triangle type for model loading
|
||||
struct Triangle {
|
||||
public:
|
||||
Vec4 vertex1, vertex2, vertex3;
|
||||
bool hasNormals = false;
|
||||
Vec4 normal1, normal2, normal3;
|
||||
Vec4 uv1, uv2, uv3;
|
||||
AABB aabb;
|
||||
|
||||
Triangle(const Vec4& v1, const Vec4& v2, const Vec4& v3): vertex1(v1), vertex2(v2), vertex3(v3) {}
|
||||
|
||||
Triangle(const Vec4& v1, const Vec4& v2, const Vec4& v3,
|
||||
const Vec4& n1, const Vec4& n2, const Vec4& n3): vertex1(v1), vertex2(v2), vertex3(v3),
|
||||
hasNormals(true), normal1(n1), normal2(n2), normal3(n3) {}
|
||||
|
||||
Triangle(const Vec4& v1, const Vec4& v2, const Vec4& v3,
|
||||
const Vec4& uv1, const Vec4& uv2, const Vec4& uv3,
|
||||
const Vec4& n1, const Vec4& n2, const Vec4& n3): vertex1(v1), vertex2(v2), vertex3(v3),
|
||||
uv1(uv1), uv2(uv2), uv3(uv3),
|
||||
hasNormals(true), normal1(n1), normal2(n2), normal3(n3) {}
|
||||
|
||||
// slow method, not really required as all normals should be equal
|
||||
[[nodiscard]] Vec4 findClosestNormal(const Vec4& point) const {
|
||||
// no need to sqrt as exact distance doesn't matter
|
||||
auto n1Dist = (point - normal1).lengthSquared();
|
||||
auto n2Dist = (point - normal2).lengthSquared();
|
||||
auto n3Dist = (point - normal3).lengthSquared();
|
||||
return (n1Dist < n2Dist && n1Dist < n3Dist) ? normal1 : (n2Dist < n3Dist ? normal2 : normal3);
|
||||
}
|
||||
};
|
||||
|
||||
// face type for model loading
|
||||
struct face {
|
||||
int v1, v2, v3;
|
||||
int uv1, uv2, uv3;
|
||||
int n1, n2, n3;
|
||||
};
|
||||
|
||||
class Material {
|
||||
protected:
|
||||
// most materials will need an albedo
|
||||
Vec4 baseColor;
|
||||
public:
|
||||
explicit Material(const Vec4& baseColor): baseColor(baseColor) {}
|
||||
|
||||
// returns true if the ray was scattered along with the scattered ray, otherwise will return false with empty ray.
|
||||
// the returned vec4 is the attenuation color
|
||||
[[nodiscard]] virtual ScatterResults scatter(const Ray& ray, const HitData& hitData) const = 0;
|
||||
|
||||
[[nodiscard]] Vec4 getBaseColor() const { return baseColor; }
|
||||
virtual ~Material() = default;
|
||||
};
|
||||
|
||||
class Object {
|
||||
protected:
|
||||
AABB aabb;
|
||||
Vec4 position;
|
||||
Material* material;
|
||||
public:
|
||||
Object(Material* material, const Vec4& position): material(material), position(position), aabb({}) {};
|
||||
// return true if the ray intersects with this object, only between min and max
|
||||
[[nodiscard]] virtual HitData checkIfHit(const Ray& ray, PRECISION_TYPE min, PRECISION_TYPE max) const = 0;
|
||||
|
||||
[[nodiscard]] Material* getMaterial() const { return material; }
|
||||
virtual Object* clone() = 0;
|
||||
virtual AABB& getAABB() { return aabb; }
|
||||
virtual void setAABB(const AABB& ab) { this->aabb = ab; }
|
||||
[[nodiscard]] Vec4 getPosition() const { return position; }
|
||||
virtual ~Object() = default;
|
||||
};
|
||||
|
||||
// used for using an object, mostly BVH
|
||||
class EmptyObject : public Object {
|
||||
protected:
|
||||
public:
|
||||
Triangle& tri;
|
||||
EmptyObject(const Vec4& position, const AABB& a, Triangle& tri): Object(nullptr, position), tri(tri) {this->aabb = a;};
|
||||
// unused
|
||||
[[nodiscard]] virtual HitData checkIfHit(const Ray& ray, PRECISION_TYPE min, PRECISION_TYPE max) const {
|
||||
wlog << "Warning! A empty object has made its way into the raycaster!\n";
|
||||
return {};
|
||||
}
|
||||
virtual Object* clone(){return new EmptyObject(position, aabb, tri);}
|
||||
};
|
||||
}
|
||||
|
||||
#endif //STEP_2_TYPES_H
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Created by Brett Terpstra 6920201 on 18/10/22.
|
||||
* Copyright (c) 2022 Brett Terpstra. All Rights Reserved.
|
||||
*
|
||||
* this is a direct one-to-one copy of the profiler class used in my Game Engine
|
||||
* it functions very well especially when used in a GUI context,
|
||||
* so why reinvent the wheel right?
|
||||
* So to avoid any kind of self plagiarism, I fully credit the source which is here:
|
||||
* https://github.com/Tri11Paragon/Trapdoor-Engine/tree/dev/C%2B%2B%20Engine
|
||||
*/
|
||||
|
||||
#ifndef STEP_2_DEBUG_H
|
||||
#define STEP_2_DEBUG_H
|
||||
|
||||
#include <util/std.h>
|
||||
|
||||
namespace Raytracing {
|
||||
class profiler;
|
||||
extern std::unordered_map<std::string, profiler*> profiles;
|
||||
|
||||
class DebugTab{
|
||||
protected:
|
||||
std::string name;
|
||||
public:
|
||||
virtual void render() {}
|
||||
std::string getName() {
|
||||
return name;
|
||||
}
|
||||
};
|
||||
|
||||
class profiler : public DebugTab {
|
||||
private:
|
||||
long _start = 0;
|
||||
long _end = 0;
|
||||
std::unordered_map<std::string, std::pair<long, long>> timings;
|
||||
public:
|
||||
explicit profiler(std::string name);
|
||||
|
||||
void start();
|
||||
void start(const std::string& name);
|
||||
static void start(const std::string& name, const std::string& tabName) {
|
||||
auto p = new profiler(name);
|
||||
profiles.insert(std::pair(name, p));
|
||||
p->start(tabName);
|
||||
}
|
||||
|
||||
void end();
|
||||
void end(const std::string& name);
|
||||
static void end(const std::string& name, const std::string& tabName){
|
||||
try {
|
||||
profiles.at(name)->end(tabName);
|
||||
} catch (std::exception& e){}
|
||||
}
|
||||
|
||||
void print();
|
||||
static void print(const std::string& name){
|
||||
try {
|
||||
profiles.at(name)->print();
|
||||
delete(profiles.at(name));
|
||||
} catch (std::exception& e){}
|
||||
}
|
||||
|
||||
void endAndPrint();
|
||||
static void endAndPrint(const std::string& name, const std::string& tabName){
|
||||
profiler::end(name, tabName);
|
||||
profiler::print(name);
|
||||
}
|
||||
|
||||
void render();
|
||||
static void render(int count) {
|
||||
for (auto p : profiles)
|
||||
p.second->render();
|
||||
}
|
||||
|
||||
~profiler();
|
||||
static void cleanup(){
|
||||
for (const auto& p : profiles)
|
||||
delete(p.second);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //STEP_2_DEBUG_H
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Created by Brett Terpstra 6920201 on 14/10/22.
|
||||
* Copyright (c) Brett Terpstra 2022 All Rights Reserved
|
||||
*/
|
||||
|
||||
#ifndef STEP_2_LOGGING_H
|
||||
#define STEP_2_LOGGING_H
|
||||
|
||||
#include <iostream>
|
||||
|
||||
// im only going to make this class if I have time (to add dates, output to file, and colored severity)
|
||||
|
||||
// log fatal
|
||||
#define flog std::cerr << "[Fatal]: "
|
||||
// log error
|
||||
#define elog std::cerr << "[Error]: "
|
||||
// log warning
|
||||
#define wlog std::cout << "[Warning]: "
|
||||
// log info
|
||||
#define ilog std::cout << "[Info]: "
|
||||
// log debug
|
||||
#define dlog std::cout << "[Debug]: "
|
||||
// log trace
|
||||
#define tlog std::cout << "[Trace]: "
|
||||
|
||||
|
||||
#endif //STEP_2_LOGGING_H
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* Created by Brett Terpstra 6920201 on 17/10/22.
|
||||
* Copyright (c) 2022 Brett Terpstra. All Rights Reserved.
|
||||
*/
|
||||
|
||||
#ifndef STEP_2_MODELS_H
|
||||
#define STEP_2_MODELS_H
|
||||
|
||||
#include <util/std.h>
|
||||
#include <math/vectors.h>
|
||||
#include <math/colliders.h>
|
||||
#include <math/bvh.h>
|
||||
|
||||
namespace Raytracing {
|
||||
|
||||
struct ModelData {
|
||||
public:
|
||||
// storing all this data is memory inefficient
|
||||
// since normals and vertices are only vec3s
|
||||
// and uvs are vec2s
|
||||
// TODO: create lower order vector classes
|
||||
std::vector<Vec4> vertices;
|
||||
std::vector<Vec4> uvs;
|
||||
std::vector<Vec4> normals;
|
||||
std::vector<face> faces;
|
||||
AABB aabb;
|
||||
|
||||
std::vector<Triangle> toTriangles() {
|
||||
std::vector<Triangle> triangles;
|
||||
|
||||
PRECISION_TYPE minX = infinity, minY = infinity, minZ = infinity, maxX = ninfinity, maxY = ninfinity, maxZ = ninfinity;
|
||||
|
||||
for (face f: faces) {
|
||||
Triangle t {vertices[f.v1], vertices[f.v2], vertices[f.v3],
|
||||
uvs[f.uv1], uvs[f.uv2], uvs[f.uv3],
|
||||
normals[f.n1], normals[f.n2], normals[f.n3]};
|
||||
|
||||
PRECISION_TYPE tMinX = infinity, tMinY = infinity, tMinZ = infinity, tMaxX = ninfinity, tMaxY = ninfinity, tMaxZ = ninfinity;
|
||||
// find the min and max of all the triangles
|
||||
tMinX = std::min(t.vertex1.x(), std::min(t.vertex2.x(), std::min(t.vertex3.x(), tMinX)));
|
||||
tMinY = std::min(t.vertex1.y(), std::min(t.vertex2.y(), std::min(t.vertex3.y(), tMinY)));
|
||||
tMinZ = std::min(t.vertex1.z(), std::min(t.vertex2.z(), std::min(t.vertex3.z(), tMinZ)));
|
||||
|
||||
tMaxX = std::max(t.vertex1.x(), std::max(t.vertex2.x(), std::max(t.vertex3.x(), tMaxX)));
|
||||
tMaxY = std::max(t.vertex1.y(), std::max(t.vertex2.y(), std::max(t.vertex3.y(), tMaxY)));
|
||||
tMaxZ = std::max(t.vertex1.z(), std::max(t.vertex2.z(), std::max(t.vertex3.z(), tMaxZ)));
|
||||
|
||||
// create a AABB for model local BVH
|
||||
t.aabb = {tMinX, tMinY, tMinZ, tMaxX, tMaxY, tMaxZ};
|
||||
|
||||
// and of course for a model AABB,
|
||||
minX = std::min(tMinX, minX);
|
||||
minY = std::min(tMinY, minY);
|
||||
minZ = std::min(tMinZ, minZ);
|
||||
|
||||
maxX = std::max(tMaxX, maxX);
|
||||
maxY = std::max(tMaxY, maxY);
|
||||
maxZ = std::max(tMaxZ, maxZ);
|
||||
|
||||
triangles.push_back(t);
|
||||
}
|
||||
// to generate a AABB
|
||||
aabb = {minX, minY, minZ, maxX, maxY, maxZ};
|
||||
|
||||
return triangles;
|
||||
}
|
||||
|
||||
// creates a BVH tree and returns the list of objects we created. make sure to delete them.
|
||||
static std::vector<Object*> createBVHTree(std::vector<Triangle>& triangles, const Vec4& pos) {
|
||||
std::vector<Object*> objects;
|
||||
for (auto& tri : triangles){
|
||||
Object* obj = new EmptyObject(pos, tri.aabb, tri);
|
||||
objects.push_back(obj);
|
||||
}
|
||||
return objects;
|
||||
}
|
||||
};
|
||||
|
||||
class ModelLoader {
|
||||
private:
|
||||
public:
|
||||
virtual ModelData loadModel(std::string file) = 0;
|
||||
};
|
||||
|
||||
class OBJLoader : public ModelLoader {
|
||||
private:
|
||||
public:
|
||||
virtual ModelData loadModel(std::string file);
|
||||
};
|
||||
}
|
||||
#endif //STEP_2_MODELS_H
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Created by Brett Terpstra 6920201 on 14/10/22.
|
||||
* Copyright (c) Brett Terpstra 2022 All Rights Reserved
|
||||
*/
|
||||
|
||||
#ifndef STEP_2_PARSER_H
|
||||
#define STEP_2_PARSER_H
|
||||
|
||||
#include "util/std.h"
|
||||
|
||||
namespace Raytracing {
|
||||
|
||||
/**
|
||||
* The parser class is used to parse incoming values from the commandline.
|
||||
* It is not required that you use it for provided values
|
||||
* and in fact should work for values provided via stdin
|
||||
* however they must be provided in single line SSV char* form.
|
||||
*/
|
||||
class Parser {
|
||||
private:
|
||||
std::unordered_map<std::string, std::string> raw_values;
|
||||
std::unordered_map<std::string, std::string> defaultValues;
|
||||
std::vector<std::string> descriptions;
|
||||
public:
|
||||
Parser();
|
||||
// Adds an option (or options) with default values.
|
||||
// default value defaults to an empty string and will not be added
|
||||
// useful if you want to add a description to the help menu.
|
||||
void addOption(const std::string& option, const std::string& description, const std::string& defaultValue = "");
|
||||
void addOption(const std::vector<std::string>& options, const std::string& description, const std::string& defaultValue = "");
|
||||
// returns true if the option provided is different from the default option.
|
||||
bool hasOptionChanged(const std::string& option);
|
||||
// checks if the option has been provided to the parser
|
||||
bool hasOption(const std::string& option);
|
||||
// check if any of the options exist, only use for checking options that lead to the same path
|
||||
// as this will return true at first option.
|
||||
bool hasOption(const std::vector<std::string>& options);
|
||||
// does not check to see if the option exists.
|
||||
std::string getOptionValue(const std::string& option);
|
||||
|
||||
// parse the options from args
|
||||
int parse(char** args, int argc);
|
||||
void printDifferenceInInfo();
|
||||
void printAllInInfo();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //STEP_2_PARSER_H
|
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
* Created by Brett Terpstra 6920201 on 14/10/22.
|
||||
* Copyright (c) Brett Terpstra 2022 All Rights Reserved
|
||||
*
|
||||
* This file is used to include common standard library headers
|
||||
* There are some things {String, Maps} that I use a lot
|
||||
* Plus common defines that might be useful in the future.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef STEP_2_STD_H
|
||||
#define STEP_2_STD_H
|
||||
|
||||
/**
|
||||
* includes
|
||||
*/
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include "util/logging.h"
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <locale>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <random>
|
||||
|
||||
/**
|
||||
* defines
|
||||
*/
|
||||
#define RAYTRACING_VERSION_MAJOR 0
|
||||
#define RAYTRACING_VERSION_MINOR 0
|
||||
#define RAYTRACING_VERSION_PATCH 1
|
||||
#define RAYTRACING_VERSION_STRING "0.0.1"
|
||||
|
||||
/**
|
||||
* Constants
|
||||
*/
|
||||
const double infinity = std::numeric_limits<double>::infinity();
|
||||
const double ninfinity = -std::numeric_limits<double>::infinity();
|
||||
// PI, to a large # of digits
|
||||
const double PI = 3.1415926535897932385;
|
||||
// very small number
|
||||
const double EPSILON = 0.0000001;
|
||||
|
||||
/**
|
||||
* classes
|
||||
*/
|
||||
static inline double degreeeToRadian(double deg){
|
||||
return deg * PI/180.0;
|
||||
}
|
||||
|
||||
namespace Raytracing {
|
||||
class Random {
|
||||
private:
|
||||
std::random_device rd; // obtain a random number from hardware
|
||||
std::mt19937 gen;
|
||||
std::uniform_real_distribution<double> doubleDistr {0, 1};
|
||||
public:
|
||||
Random(): gen(std::mt19937(long(rd.entropy() * 691 * 691))) {}
|
||||
Random(double min, double max): gen(std::mt19937(long(rd.entropy() * 691 * 691))), doubleDistr{min, max} {}
|
||||
double getDouble(){
|
||||
return doubleDistr(gen);
|
||||
}
|
||||
};
|
||||
class String {
|
||||
public:
|
||||
static inline std::string toLowerCase(const std::string& s){
|
||||
std::stringstream str;
|
||||
std::for_each(s.begin(), s.end(), [&str](unsigned char ch) {
|
||||
str << (char) std::tolower(ch);
|
||||
});
|
||||
return str.str();
|
||||
}
|
||||
static inline std::string toUpperCase(const std::string& s){
|
||||
std::stringstream str;
|
||||
std::for_each(s.begin(), s.end(), [&str](unsigned char ch) {
|
||||
str << (char) std::toupper(ch);
|
||||
});
|
||||
return str.str();
|
||||
}
|
||||
// taken from https://stackoverflow.com/questions/14265581/parse-split-a-string-in-c-using-string-delimiter-standard-c
|
||||
// extended to return a vector
|
||||
static inline std::vector<std::string> split(std::string s, const std::string& delim){
|
||||
size_t pos = 0;
|
||||
std::vector<std::string> tokens;
|
||||
while ((pos = s.find(delim)) != std::string::npos) {
|
||||
auto token = s.substr(0, pos);
|
||||
tokens.push_back(token);
|
||||
s.erase(0, pos + delim.length());
|
||||
}
|
||||
tokens.push_back(s);
|
||||
return tokens;
|
||||
}
|
||||
// taken from https://stackoverflow.com/questions/216823/how-to-trim-an-stdstring
|
||||
// would've preferred to use boost lib but instructions said to avoid external libs
|
||||
// trim from start (in place)
|
||||
static inline std::string& ltrim(std::string& s) {
|
||||
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) {
|
||||
return !std::isspace(ch);
|
||||
}));
|
||||
return s;
|
||||
}
|
||||
|
||||
// trim from end (in place)
|
||||
static inline std::string& rtrim(std::string& s) {
|
||||
s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) {
|
||||
return !std::isspace(ch);
|
||||
}).base(), s.end());
|
||||
return s;
|
||||
}
|
||||
|
||||
// trim from both ends (in place)
|
||||
static inline std::string& trim(std::string& s) {
|
||||
ltrim(s);
|
||||
rtrim(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
// trim from start (copying)
|
||||
static inline std::string ltrim_copy(std::string s) {
|
||||
ltrim(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
// trim from end (copying)
|
||||
static inline std::string rtrim_copy(std::string s) {
|
||||
rtrim(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
// trim from both ends (copying)
|
||||
static inline std::string trim_copy(std::string s) {
|
||||
trim(s);
|
||||
return s;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static Raytracing::Random rnd {};
|
||||
|
||||
static inline double getRandomDouble(){
|
||||
return rnd.getDouble();
|
||||
}
|
||||
|
||||
static inline double clamp(double val, double min, double max) {
|
||||
if (val < min)
|
||||
return min;
|
||||
if (val > max)
|
||||
return max;
|
||||
return val;
|
||||
}
|
||||
|
||||
#endif //STEP_2_STD_H
|
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
* Created by Brett Terpstra 6920201 on 16/10/22.
|
||||
* Copyright (c) 2022 Brett Terpstra. All Rights Reserved.
|
||||
*/
|
||||
|
||||
#ifndef STEP_2_WORLD_H
|
||||
#define STEP_2_WORLD_H
|
||||
|
||||
#include <util/std.h>
|
||||
#include <math/vectors.h>
|
||||
#include <util/models.h>
|
||||
#include <math/bvh.h>
|
||||
#include <types.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace Raytracing {
|
||||
|
||||
class SphereObject : public Object {
|
||||
private:
|
||||
PRECISION_TYPE radius;
|
||||
public:
|
||||
SphereObject(const Vec4& position, PRECISION_TYPE radius, Material* material): radius(radius), Object(material, position) {}
|
||||
|
||||
[[nodiscard]] virtual HitData checkIfHit(const Ray& ray, PRECISION_TYPE min, PRECISION_TYPE max) const;
|
||||
virtual Object* clone(){
|
||||
return new SphereObject(position, radius, material);
|
||||
}
|
||||
};
|
||||
|
||||
class TriangleObject : public Object {
|
||||
private:
|
||||
Triangle theTriangle;
|
||||
public:
|
||||
TriangleObject(const Vec4& position, Triangle theTriangle, Material* material): Object(material, position),
|
||||
theTriangle(std::move(theTriangle)) {}
|
||||
[[nodiscard]] virtual HitData checkIfHit(const Ray& ray, PRECISION_TYPE min, PRECISION_TYPE max) const;
|
||||
virtual Object* clone() {
|
||||
return new TriangleObject(position, theTriangle, material);
|
||||
}
|
||||
};
|
||||
|
||||
class ModelObject : public Object {
|
||||
private:
|
||||
std::vector<Triangle> triangles;
|
||||
ModelData& data;
|
||||
// basically we have to store this crap here because c++ loves to copy stuff
|
||||
//std::vector<Object*> createdTreeObjects{};
|
||||
//BVHTree* tree = nullptr;
|
||||
public:
|
||||
ModelObject(const Vec4& position, ModelData& data, Material* material): Object(material, position), data(data) {
|
||||
// since all of this occurs before the main ray tracing algorithm it's fine to do sequentially
|
||||
triangles = data.toTriangles();
|
||||
this->aabb = data.aabb;
|
||||
//createdTreeObjects = Raytracing::ModelData::createBVHTree(triangles, position);
|
||||
//tree = new BVHTree(createdTreeObjects);
|
||||
}
|
||||
[[nodiscard]] virtual HitData checkIfHit(const Ray& ray, PRECISION_TYPE min, PRECISION_TYPE max) const;
|
||||
virtual Object* clone() {
|
||||
return new ModelObject(position, data, material);
|
||||
}
|
||||
virtual ~ModelObject() {
|
||||
// Disabled for now, causing bugs when on release mode.
|
||||
//for (auto* p : createdTreeObjects)
|
||||
// delete(p);
|
||||
//delete(tree);
|
||||
}
|
||||
};
|
||||
|
||||
class DiffuseMaterial : public Material {
|
||||
private:
|
||||
public:
|
||||
explicit DiffuseMaterial(const Vec4& scatterColor): Material(scatterColor) {}
|
||||
|
||||
[[nodiscard]] virtual ScatterResults scatter(const Ray& ray, const HitData& hitData) const;
|
||||
};
|
||||
|
||||
class MetalMaterial : public Material {
|
||||
protected:
|
||||
static inline Vec4 reflect(const Vec4& incomingVector, const Vec4& normal) {
|
||||
return incomingVector - 2 * Vec4::dot(incomingVector, normal) * normal;
|
||||
}
|
||||
|
||||
public:
|
||||
explicit MetalMaterial(const Vec4& metalColor): Material(metalColor) {}
|
||||
|
||||
[[nodiscard]] virtual ScatterResults scatter(const Ray& ray, const HitData& hitData) const;
|
||||
};
|
||||
|
||||
class BrushedMetalMaterial : public MetalMaterial {
|
||||
private:
|
||||
PRECISION_TYPE fuzzyness;
|
||||
public:
|
||||
explicit BrushedMetalMaterial(const Vec4& metalColor, PRECISION_TYPE fuzzyness): MetalMaterial(metalColor), fuzzyness(fuzzyness) {}
|
||||
|
||||
[[nodiscard]] virtual ScatterResults scatter(const Ray& ray, const HitData& hitData) const;
|
||||
};
|
||||
|
||||
class TexturedMaterial : public Material {
|
||||
public:
|
||||
TexturedMaterial(const std::string& file): Material({}) {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
class World {
|
||||
private:
|
||||
// store all the objects in the world,
|
||||
std::vector<Object*> objects;
|
||||
/*TODO: create a kd-tree or bvh version to store the objects
|
||||
* this way we can easily tell if a ray is near and object or not
|
||||
* saving on computation
|
||||
*/
|
||||
// TODO: the above todo has been done, now we need to test the performance advantage of the BVH
|
||||
//BVHTree* bvhTree = nullptr;
|
||||
std::unordered_map<std::string, Material*> materials;
|
||||
public:
|
||||
World() = default;
|
||||
World(const World& world) = delete;
|
||||
World(const World&& world) = delete;
|
||||
|
||||
// call this after you've added all the objects to the world. (Called by the raycaster class)
|
||||
void generateBVH();
|
||||
|
||||
inline void add(Object* object) { objects.push_back(object); }
|
||||
|
||||
inline void addMaterial(const std::string& materialName, Material* mat) { materials.insert({materialName, mat}); }
|
||||
|
||||
inline Material* getMaterial(const std::string& materialName) { return materials.at(materialName); }
|
||||
|
||||
[[nodiscard]] virtual std::pair<HitData, Object*> checkIfHit(const Ray& ray, PRECISION_TYPE min, PRECISION_TYPE max) const;
|
||||
~World();
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif //STEP_2_WORLD_H
|
|
@ -0,0 +1,47 @@
|
|||
# Blender 3.3.1
|
||||
# www.blender.org
|
||||
mtllib cube.mtl
|
||||
o Cube
|
||||
v -250.000000 -250.000000 250.000000
|
||||
v -250.000000 250.000000 250.000000
|
||||
v -250.000000 -250.000000 -250.000000
|
||||
v -250.000000 250.000000 -250.000000
|
||||
v 250.000000 -250.000000 250.000000
|
||||
v 250.000000 250.000000 250.000000
|
||||
v 250.000000 -250.000000 -250.000000
|
||||
v 250.000000 250.000000 -250.000000
|
||||
vn 1.0000 -0.0000 -0.0000
|
||||
vn -0.0000 -0.0000 1.0000
|
||||
vn -1.0000 -0.0000 -0.0000
|
||||
vn -0.0000 -0.0000 -1.0000
|
||||
vn -0.0000 1.0000 -0.0000
|
||||
vn -0.0000 -1.0000 -0.0000
|
||||
vt 0.000000 0.000000
|
||||
vt 0.000000 1.000000
|
||||
vt 0.000000 0.000000
|
||||
vt 0.000000 0.000000
|
||||
vt 0.000000 1.000000
|
||||
vt 1.000000 0.000000
|
||||
vt 1.000000 1.000000
|
||||
vt 0.000000 1.000000
|
||||
vt 0.000000 0.000000
|
||||
vt 1.000000 0.000000
|
||||
vt 0.000000 1.000000
|
||||
vt 1.000000 1.000000
|
||||
vt 1.000000 0.000000
|
||||
vt 1.000000 0.000000
|
||||
vt 1.000000 1.000000
|
||||
vt 1.000000 1.000000
|
||||
s 0
|
||||
f 3/6/1 2/2/1 1/1/1
|
||||
f 7/14/2 4/8/2 3/4/2
|
||||
f 5/9/3 8/16/3 7/14/3
|
||||
f 1/1/4 6/12/4 5/10/4
|
||||
f 1/1/5 7/15/5 3/5/5
|
||||
f 6/13/6 4/8/6 8/16/6
|
||||
f 3/6/1 4/7/1 2/2/1
|
||||
f 7/14/2 8/16/2 4/8/2
|
||||
f 5/9/3 6/11/3 8/16/3
|
||||
f 1/1/4 2/2/4 6/12/4
|
||||
f 1/1/5 5/10/5 7/15/5
|
||||
f 6/13/6 2/3/6 4/8/6
|
|
@ -0,0 +1,47 @@
|
|||
# Blender 3.3.1
|
||||
# www.blender.org
|
||||
mtllib cubeflipped.mtl
|
||||
o Cube
|
||||
v -250.000000 -250.000000 250.000000
|
||||
v -250.000000 250.000000 250.000000
|
||||
v -250.000000 -250.000000 -250.000000
|
||||
v -250.000000 250.000000 -250.000000
|
||||
v 250.000000 -250.000000 250.000000
|
||||
v 250.000000 250.000000 250.000000
|
||||
v 250.000000 -250.000000 -250.000000
|
||||
v 250.000000 250.000000 -250.000000
|
||||
vn -1.0000 -0.0000 -0.0000
|
||||
vn -0.0000 -0.0000 -1.0000
|
||||
vn 1.0000 -0.0000 -0.0000
|
||||
vn -0.0000 -0.0000 1.0000
|
||||
vn -0.0000 -1.0000 -0.0000
|
||||
vn -0.0000 1.0000 -0.0000
|
||||
vt 0.000000 0.000000
|
||||
vt 0.000000 1.000000
|
||||
vt 0.000000 0.000000
|
||||
vt 0.000000 0.000000
|
||||
vt 0.000000 1.000000
|
||||
vt 1.000000 0.000000
|
||||
vt 1.000000 1.000000
|
||||
vt 0.000000 1.000000
|
||||
vt 0.000000 0.000000
|
||||
vt 1.000000 0.000000
|
||||
vt 0.000000 1.000000
|
||||
vt 1.000000 1.000000
|
||||
vt 1.000000 0.000000
|
||||
vt 1.000000 0.000000
|
||||
vt 1.000000 1.000000
|
||||
vt 1.000000 1.000000
|
||||
s 0
|
||||
f 3/6/1 1/1/1 2/2/1
|
||||
f 7/14/2 3/4/2 4/8/2
|
||||
f 5/9/3 7/14/3 8/16/3
|
||||
f 1/1/4 5/10/4 6/12/4
|
||||
f 1/1/5 3/5/5 7/15/5
|
||||
f 6/13/6 8/16/6 4/8/6
|
||||
f 3/6/1 2/2/1 4/7/1
|
||||
f 7/14/2 4/8/2 8/16/2
|
||||
f 5/9/3 8/16/3 6/11/3
|
||||
f 1/1/4 6/12/4 2/2/4
|
||||
f 1/1/5 7/15/5 5/10/5
|
||||
f 6/13/6 4/8/6 2/3/6
|
|
@ -0,0 +1,255 @@
|
|||
# Blender 3.3.1
|
||||
# www.blender.org
|
||||
mtllib house.mtl
|
||||
o Cube
|
||||
v -0.800000 -1.000000 1.000000
|
||||
v -0.800000 1.000000 1.000000
|
||||
v -0.800000 -1.000000 -1.000000
|
||||
v -0.800000 1.000000 -1.000000
|
||||
v 0.800000 -1.000000 1.000000
|
||||
v 0.800000 1.000000 1.000000
|
||||
v 0.800000 -1.000000 -1.000000
|
||||
v 0.800000 1.000000 -1.000000
|
||||
vn -1.0000 -0.0000 -0.0000
|
||||
vn -0.0000 -0.0000 -1.0000
|
||||
vn 1.0000 -0.0000 -0.0000
|
||||
vn -0.0000 -0.0000 1.0000
|
||||
vn -0.0000 -1.0000 -0.0000
|
||||
vn -0.0000 1.0000 -0.0000
|
||||
vt 0.375000 0.000000
|
||||
vt 0.375000 1.000000
|
||||
vt 0.125000 0.750000
|
||||
vt 0.625000 0.000000
|
||||
vt 0.625000 1.000000
|
||||
vt 0.875000 0.750000
|
||||
vt 0.125000 0.500000
|
||||
vt 0.375000 0.250000
|
||||
vt 0.625000 0.250000
|
||||
vt 0.875000 0.500000
|
||||
vt 0.375000 0.750000
|
||||
vt 0.625000 0.750000
|
||||
vt 0.375000 0.500000
|
||||
vt 0.625000 0.500000
|
||||
s 0
|
||||
f 2/4/1 3/8/1 1/1/1
|
||||
f 4/9/2 7/13/2 3/8/2
|
||||
f 8/14/3 5/11/3 7/13/3
|
||||
f 6/12/4 1/2/4 5/11/4
|
||||
f 7/13/5 1/3/5 3/7/5
|
||||
f 4/10/6 6/12/6 8/14/6
|
||||
f 2/4/1 4/9/1 3/8/1
|
||||
f 4/9/2 8/14/2 7/13/2
|
||||
f 8/14/3 6/12/3 5/11/3
|
||||
f 6/12/4 2/5/4 1/2/4
|
||||
f 7/13/5 5/11/5 1/3/5
|
||||
f 4/10/6 2/6/6 6/12/6
|
||||
o Cube.001
|
||||
v -1.000000 1.719057 0.057522
|
||||
v -1.000000 1.832386 -0.037572
|
||||
v -1.000000 0.433482 -1.474567
|
||||
v -1.000000 0.546811 -1.569661
|
||||
v 1.000000 1.719057 0.057522
|
||||
v 1.000000 1.832386 -0.037572
|
||||
v 1.000000 0.433482 -1.474567
|
||||
v 1.000000 0.546811 -1.569661
|
||||
vn -1.0000 -0.0000 -0.0000
|
||||
vn -0.0000 -0.6428 -0.7660
|
||||
vn 1.0000 -0.0000 -0.0000
|
||||
vn -0.0000 0.6428 0.7660
|
||||
vn -0.0000 -0.7660 0.6428
|
||||
vn -0.0000 0.7660 -0.6428
|
||||
vt 0.375000 0.000000
|
||||
vt 0.375000 1.000000
|
||||
vt 0.125000 0.750000
|
||||
vt 0.625000 0.000000
|
||||
vt 0.625000 1.000000
|
||||
vt 0.875000 0.750000
|
||||
vt 0.125000 0.500000
|
||||
vt 0.375000 0.250000
|
||||
vt 0.625000 0.250000
|
||||
vt 0.875000 0.500000
|
||||
vt 0.375000 0.750000
|
||||
vt 0.625000 0.750000
|
||||
vt 0.375000 0.500000
|
||||
vt 0.625000 0.500000
|
||||
s 0
|
||||
f 10/18/7 11/22/7 9/15/7
|
||||
f 12/23/8 15/27/8 11/22/8
|
||||
f 16/28/9 13/25/9 15/27/9
|
||||
f 14/26/10 9/16/10 13/25/10
|
||||
f 15/27/11 9/17/11 11/21/11
|
||||
f 12/24/12 14/26/12 16/28/12
|
||||
f 10/18/7 12/23/7 11/22/7
|
||||
f 12/23/8 16/28/8 15/27/8
|
||||
f 16/28/9 14/26/9 13/25/9
|
||||
f 14/26/10 10/19/10 9/16/10
|
||||
f 15/27/11 13/25/11 9/17/11
|
||||
f 12/24/12 10/20/12 14/26/12
|
||||
o Cube.002
|
||||
v -1.000000 0.433482 1.461952
|
||||
v -1.000000 0.546811 1.557046
|
||||
v -1.000000 1.719057 -0.070137
|
||||
v -1.000000 1.832386 0.024957
|
||||
v 1.000000 0.433482 1.461952
|
||||
v 1.000000 0.546811 1.557046
|
||||
v 1.000000 1.719057 -0.070137
|
||||
v 1.000000 1.832386 0.024957
|
||||
vn -1.0000 -0.0000 -0.0000
|
||||
vn -0.0000 0.6428 -0.7660
|
||||
vn 1.0000 -0.0000 -0.0000
|
||||
vn -0.0000 -0.6428 0.7660
|
||||
vn -0.0000 -0.7660 -0.6428
|
||||
vn -0.0000 0.7660 0.6428
|
||||
vt 0.375000 0.000000
|
||||
vt 0.375000 1.000000
|
||||
vt 0.125000 0.750000
|
||||
vt 0.625000 0.000000
|
||||
vt 0.625000 1.000000
|
||||
vt 0.875000 0.750000
|
||||
vt 0.125000 0.500000
|
||||
vt 0.375000 0.250000
|
||||
vt 0.625000 0.250000
|
||||
vt 0.875000 0.500000
|
||||
vt 0.375000 0.750000
|
||||
vt 0.625000 0.750000
|
||||
vt 0.375000 0.500000
|
||||
vt 0.625000 0.500000
|
||||
s 0
|
||||
f 18/32/13 19/36/13 17/29/13
|
||||
f 20/37/14 23/41/14 19/36/14
|
||||
f 24/42/15 21/39/15 23/41/15
|
||||
f 22/40/16 17/30/16 21/39/16
|
||||
f 23/41/17 17/31/17 19/35/17
|
||||
f 20/38/18 22/40/18 24/42/18
|
||||
f 18/32/13 20/37/13 19/36/13
|
||||
f 20/37/14 24/42/14 23/41/14
|
||||
f 24/42/15 22/40/15 21/39/15
|
||||
f 22/40/16 18/33/16 17/30/16
|
||||
f 23/41/17 21/39/17 17/31/17
|
||||
f 20/38/18 18/34/18 22/40/18
|
||||
o Cube.003
|
||||
v 0.787552 -0.999883 0.157434
|
||||
v 0.787552 -0.426687 0.157434
|
||||
v 0.787552 -0.999883 -0.157434
|
||||
v 0.787552 -0.426687 -0.157434
|
||||
v 0.836243 -0.999883 0.157434
|
||||
v 0.836243 -0.426687 0.157434
|
||||
v 0.836243 -0.999883 -0.157434
|
||||
v 0.836243 -0.426687 -0.157434
|
||||
vn -1.0000 -0.0000 -0.0000
|
||||
vn -0.0000 -0.0000 -1.0000
|
||||
vn 1.0000 -0.0000 -0.0000
|
||||
vn -0.0000 -0.0000 1.0000
|
||||
vn -0.0000 -1.0000 -0.0000
|
||||
vn -0.0000 1.0000 -0.0000
|
||||
vt 0.375000 0.000000
|
||||
vt 0.375000 1.000000
|
||||
vt 0.125000 0.750000
|
||||
vt 0.625000 0.000000
|
||||
vt 0.625000 1.000000
|
||||
vt 0.875000 0.750000
|
||||
vt 0.125000 0.500000
|
||||
vt 0.375000 0.250000
|
||||
vt 0.625000 0.250000
|
||||
vt 0.875000 0.500000
|
||||
vt 0.375000 0.750000
|
||||
vt 0.625000 0.750000
|
||||
vt 0.375000 0.500000
|
||||
vt 0.625000 0.500000
|
||||
s 0
|
||||
f 26/46/19 27/50/19 25/43/19
|
||||
f 28/51/20 31/55/20 27/50/20
|
||||
f 32/56/21 29/53/21 31/55/21
|
||||
f 30/54/22 25/44/22 29/53/22
|
||||
f 31/55/23 25/45/23 27/49/23
|
||||
f 28/52/24 30/54/24 32/56/24
|
||||
f 26/46/19 28/51/19 27/50/19
|
||||
f 28/51/20 32/56/20 31/55/20
|
||||
f 32/56/21 30/54/21 29/53/21
|
||||
f 30/54/22 26/47/22 25/44/22
|
||||
f 31/55/23 29/53/23 25/45/23
|
||||
f 28/52/24 26/48/24 30/54/24
|
||||
o Cube.004
|
||||
v 0.787552 -0.090162 0.721031
|
||||
v 0.787552 0.409838 0.721031
|
||||
v 0.787552 -0.090162 0.221031
|
||||
v 0.787552 0.409838 0.221031
|
||||
v 0.836243 -0.090162 0.721031
|
||||
v 0.836243 0.409838 0.721031
|
||||
v 0.836243 -0.090162 0.221031
|
||||
v 0.836243 0.409838 0.221031
|
||||
vn -1.0000 -0.0000 -0.0000
|
||||
vn -0.0000 -0.0000 -1.0000
|
||||
vn 1.0000 -0.0000 -0.0000
|
||||
vn -0.0000 -0.0000 1.0000
|
||||
vn -0.0000 -1.0000 -0.0000
|
||||
vn -0.0000 1.0000 -0.0000
|
||||
vt 0.375000 0.000000
|
||||
vt 0.375000 1.000000
|
||||
vt 0.125000 0.750000
|
||||
vt 0.625000 0.000000
|
||||
vt 0.625000 1.000000
|
||||
vt 0.875000 0.750000
|
||||
vt 0.125000 0.500000
|
||||
vt 0.375000 0.250000
|
||||
vt 0.625000 0.250000
|
||||
vt 0.875000 0.500000
|
||||
vt 0.375000 0.750000
|
||||
vt 0.625000 0.750000
|
||||
vt 0.375000 0.500000
|
||||
vt 0.625000 0.500000
|
||||
s 0
|
||||
f 34/60/25 35/64/25 33/57/25
|
||||
f 36/65/26 39/69/26 35/64/26
|
||||
f 40/70/27 37/67/27 39/69/27
|
||||
f 38/68/28 33/58/28 37/67/28
|
||||
f 39/69/29 33/59/29 35/63/29
|
||||
f 36/66/30 38/68/30 40/70/30
|
||||
f 34/60/25 36/65/25 35/64/25
|
||||
f 36/65/26 40/70/26 39/69/26
|
||||
f 40/70/27 38/68/27 37/67/27
|
||||
f 38/68/28 34/61/28 33/58/28
|
||||
f 39/69/29 37/67/29 33/59/29
|
||||
f 36/66/30 34/62/30 38/68/30
|
||||
o Cube.005
|
||||
v 0.787552 -0.090162 -0.227981
|
||||
v 0.787552 0.409838 -0.227981
|
||||
v 0.787552 -0.090162 -0.727981
|
||||
v 0.787552 0.409838 -0.727981
|
||||
v 0.836243 -0.090162 -0.227981
|
||||
v 0.836243 0.409838 -0.227981
|
||||
v 0.836243 -0.090162 -0.727981
|
||||
v 0.836243 0.409838 -0.727981
|
||||
vn -1.0000 -0.0000 -0.0000
|
||||
vn -0.0000 -0.0000 -1.0000
|
||||
vn 1.0000 -0.0000 -0.0000
|
||||
vn -0.0000 -0.0000 1.0000
|
||||
vn -0.0000 -1.0000 -0.0000
|
||||
vn -0.0000 1.0000 -0.0000
|
||||
vt 0.375000 0.000000
|
||||
vt 0.375000 1.000000
|
||||
vt 0.125000 0.750000
|
||||
vt 0.625000 0.000000
|
||||
vt 0.625000 1.000000
|
||||
vt 0.875000 0.750000
|
||||
vt 0.125000 0.500000
|
||||
vt 0.375000 0.250000
|
||||
vt 0.625000 0.250000
|
||||
vt 0.875000 0.500000
|
||||
vt 0.375000 0.750000
|
||||
vt 0.625000 0.750000
|
||||
vt 0.375000 0.500000
|
||||
vt 0.625000 0.500000
|
||||
s 0
|
||||
f 42/74/31 43/78/31 41/71/31
|
||||
f 44/79/32 47/83/32 43/78/32
|
||||
f 48/84/33 45/81/33 47/83/33
|
||||
f 46/82/34 41/72/34 45/81/34
|
||||
f 47/83/35 41/73/35 43/77/35
|
||||
f 44/80/36 46/82/36 48/84/36
|
||||
f 42/74/31 44/79/31 43/78/31
|
||||
f 44/79/32 48/84/32 47/83/32
|
||||
f 48/84/33 46/82/33 45/81/33
|
||||
f 46/82/34 42/75/34 41/72/34
|
||||
f 47/83/35 45/81/35 41/73/35
|
||||
f 44/80/36 42/76/36 46/82/36
|
|
@ -0,0 +1,2 @@
|
|||
# Blender 3.3.1 MTL File: 'None'
|
||||
# www.blender.org
|
|
@ -0,0 +1,16 @@
|
|||
# Blender 3.3.1
|
||||
# www.blender.org
|
||||
mtllib plane.mtl
|
||||
o Plane
|
||||
v 0.000001 -8.781555 -9.817993
|
||||
v -0.000000 -8.781555 9.817993
|
||||
v 0.000000 8.781555 -9.817993
|
||||
v -0.000001 8.781555 9.817993
|
||||
vn 1.0000 -0.0000 -0.0000
|
||||
vt 0.000000 0.000000
|
||||
vt 1.000000 0.000000
|
||||
vt 0.000000 1.000000
|
||||
vt 1.000000 1.000000
|
||||
s 0
|
||||
f 3/3/1 2/2/1 1/1/1
|
||||
f 3/3/1 4/4/1 2/2/1
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,10 @@
|
|||
/*
|
||||
* Created by Brett Terpstra 6920201 on 17/10/22.
|
||||
* Copyright (c) 2022 Brett Terpstra. All Rights Reserved.
|
||||
*/
|
||||
// Yes, globals are bad.
|
||||
#include <util/debug.h>
|
||||
|
||||
namespace Raytracing {
|
||||
std::unordered_map<std::string, profiler*> profiles;
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* Created by Brett Terpstra 6920201 on 14/10/22.
|
||||
* Copyright (c) Brett Terpstra 2022 All Rights Reserved
|
||||
*/
|
||||
#include "image/image.h"
|
||||
#include <ios>
|
||||
#include <fstream>
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
#include <image/stb_image_write.h>
|
||||
|
||||
namespace Raytracing {
|
||||
|
||||
Image::Image(int width, int height) : width(width), height(height) {
|
||||
pixelData = new Vec4[width * height];
|
||||
}
|
||||
|
||||
Image::Image(const Image& image) : width(image.width), height(image.height) {
|
||||
pixelData = new Vec4[image.width * image.height];
|
||||
for (int i = 0; i < image.width; i++) {
|
||||
for (int j = 0; j < image.height; j++) {
|
||||
this->setPixelColor(i, j, image.pixelData[i * image.height + j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int Image::getPixelR(int x, int y) const {
|
||||
// values are stored as a floating point number [0, 1)
|
||||
// but most formats want an int [0, 255]
|
||||
return int(255.0 * getPixelColor(x, y).r());
|
||||
}
|
||||
|
||||
int Image::getPixelG(int x, int y) const {
|
||||
return int(255.0 * getPixelColor(x, y).g());
|
||||
}
|
||||
|
||||
int Image::getPixelB(int x, int y) const {
|
||||
return int(255.0 * getPixelColor(x, y).b());
|
||||
}
|
||||
|
||||
int Image::getPixelA(int x, int y) const {
|
||||
return int(255.0 * getPixelColor(x, y).a());
|
||||
}
|
||||
|
||||
Image::~Image() {
|
||||
delete[](pixelData);
|
||||
}
|
||||
|
||||
void ImageOutput::write(const std::string& file, const std::string& formatExtension) {
|
||||
auto lowerExtension = Raytracing::String::toLowerCase(formatExtension);
|
||||
auto fullFile = file + "." + lowerExtension;
|
||||
|
||||
if (!lowerExtension.ends_with("hdr")) {
|
||||
// unfortunately we do have to put the data into a format that STB can read
|
||||
unsigned char data[image.getWidth() * image.getHeight() * 3];
|
||||
int pixelIndex = 0;
|
||||
for (int j = image.getHeight()-1; j >= 0; j--) {
|
||||
for (int i = 0; i < image.getWidth(); i++) {
|
||||
data[pixelIndex++] = image.getPixelR(i, j);
|
||||
data[pixelIndex++] = image.getPixelG(i, j);
|
||||
data[pixelIndex++] = image.getPixelB(i, j);
|
||||
}
|
||||
}
|
||||
|
||||
// Writing a PPM was giving me issues, so I switched to using STB Image Write
|
||||
// It's a single threaded, public domain header only image writing library
|
||||
// I didn't want to use an external lib for this, however since it is public domain
|
||||
// I've simply included it in the include directory.
|
||||
if (lowerExtension.ends_with("bmp")) {
|
||||
stbi_write_bmp(fullFile.c_str(), image.getWidth(), image.getHeight(), 3, data);
|
||||
} else if (lowerExtension.ends_with("png")) {
|
||||
// stride here isn't clearly defined in the docs for some reason,
|
||||
// but it's just the image's width times the number of channels
|
||||
stbi_write_png(fullFile.c_str(), image.getWidth(), image.getHeight(), 3, data, image.getWidth() * 3);
|
||||
} else if (lowerExtension.ends_with("jpg") || lowerExtension.ends_with("jpeg")) {
|
||||
stbi_write_jpg(fullFile.c_str(), image.getWidth(), image.getHeight(), 3, data, 90);
|
||||
} else
|
||||
throw std::runtime_error("Invalid format! Please use bmp, png, or jpg");
|
||||
} else {
|
||||
// the TODO: here is to check if HDR is in [0,1] or if we need to transform the value.
|
||||
float data[image.getWidth() * image.getHeight() * 3];
|
||||
int pixelIndex = 0;
|
||||
for (int i = 0; i < image.getWidth(); i++) {
|
||||
for (int j = 0; j < image.getHeight(); j++) {
|
||||
data[pixelIndex++] = (float) image.getPixelColor(i, j).r();
|
||||
data[pixelIndex++] = (float) image.getPixelColor(i, j).g();
|
||||
data[pixelIndex++] = (float) image.getPixelColor(i, j).b();
|
||||
}
|
||||
}
|
||||
stbi_write_hdr(fullFile.c_str(), image.getWidth(), image.getHeight(), 3, data);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
#include "util/std.h"
|
||||
#include "util/parser.h"
|
||||
#include "image/image.h"
|
||||
#include <raytracing.h>
|
||||
#include <world.h>
|
||||
#include <chrono>
|
||||
|
||||
/**
|
||||
* Brett Terpstra 6920201
|
||||
*
|
||||
*/
|
||||
|
||||
int main(int argc, char** args) {
|
||||
// not a feature full parser but it'll work for what I need.
|
||||
Raytracing::Parser parser;
|
||||
|
||||
parser.addOption("--single", "Enable Single Thread\n\tUse a single thread for ray tracing\n", "true");
|
||||
// not implemented yet
|
||||
parser.addOption("--multi", "Enable Multi-threading\n"
|
||||
"\tUse multiple threads for ray tracing,\n"
|
||||
"\tYou can set the max threads using -t or --threads\n");
|
||||
parser.addOption({{"-t"}, {"--threads"}}, "Max Usable Threads\n"
|
||||
"\tSet the max threads the ray tracer will attempt to use.\n"
|
||||
"\tDefaults to all cores of your cpu.\n", "0");
|
||||
// not implemented yet
|
||||
parser.addOption({{"--gui"}, {"-g"}}, "Enable GLFW GUI\n"
|
||||
"\tWill create a GUI using GLFW and display the image there.\n"
|
||||
"\tRequires the you compile with GLFW enabled. Will do nothing otherwise\n");
|
||||
// not implemented yet
|
||||
parser.addOption({{"--gpu"}, {"-c"}}, "Enables GPU Compute\n"
|
||||
"\tRequires the --gui/-g flag enabled,\n"
|
||||
"\tWill use OpenGL compute shaders to render the image\n");
|
||||
parser.addOption("--output", "Output Directory\n"
|
||||
"\tSet the output directory for the rendered image. Defaults to the local directory.\n", "./");
|
||||
parser.addOption("--format", "Output Format\n"
|
||||
"\tSets the output format to BMP, PNG, or JPEG. \n", "PNG");
|
||||
parser.addOption("-w", "Image Width\n"
|
||||
"\tSets the width of the output image.\n", "910");
|
||||
parser.addOption("-h", "Image Height\n"
|
||||
"\tSets the height of the output image.\n", "512");
|
||||
parser.addOption("--fov", "Camera FOV\n"
|
||||
"\tSets the FOV used to render the camera.\n", "90");
|
||||
parser.addOption("--resources", "Resources Directory\n"
|
||||
"\tSets the directory where the resources are stored.\n"
|
||||
"\tThis can be relative.Must have trailing '/' \n", "../resources/");
|
||||
// disabled because don't currently have a way to parse vectors. TODO
|
||||
//parser.addOption("--position", "Camera Position\n\tSets the position used to render the scene with the camera.\n", "{0, 0, 0}");
|
||||
|
||||
// if the parser returns non-zero then it wants us to stop execution
|
||||
// likely due to a help function being called.
|
||||
if (parser.parse(args, argc))
|
||||
return 0;
|
||||
|
||||
tlog << "Parsing complete! Starting raytracer with options:" << std::endl;
|
||||
// not perfect (contains duplicates) but good enough.
|
||||
parser.printAllInInfo();
|
||||
|
||||
//Raytracing::Image image(445, 256);
|
||||
Raytracing::Image image(std::stoi(parser.getOptionValue("-w")), std::stoi(parser.getOptionValue("-h")));
|
||||
|
||||
Raytracing::Camera camera(std::stoi(parser.getOptionValue("--fov")), image);
|
||||
//camera.setPosition({0, 0, 1});
|
||||
camera.lookAt({6,5,6}, {0, 0, 0}, {0, 1, 0});
|
||||
|
||||
Raytracing::World world;
|
||||
|
||||
Raytracing::OBJLoader loader;
|
||||
// assumes you are running it from a subdirectory, "build" or "cmake-build-release", etc.
|
||||
Raytracing::ModelData spider = loader.loadModel(parser.getOptionValue("--resources") + "spider.obj");
|
||||
Raytracing::ModelData house = loader.loadModel(parser.getOptionValue("--resources") + "house.obj");
|
||||
Raytracing::ModelData plane = loader.loadModel(parser.getOptionValue("--resources") + "plane.obj");
|
||||
|
||||
world.addMaterial("greenDiffuse", new Raytracing::DiffuseMaterial{Raytracing::Vec4{0, 1.0, 0, 1}});
|
||||
world.addMaterial("redDiffuse", new Raytracing::DiffuseMaterial{Raytracing::Vec4{1.0, 0, 0, 1}});
|
||||
world.addMaterial("blueDiffuse", new Raytracing::DiffuseMaterial{Raytracing::Vec4{0, 0, 1.0, 1}});
|
||||
|
||||
world.addMaterial("greenMetal", new Raytracing::MetalMaterial{Raytracing::Vec4{0.4, 1.0, 0.4, 1}});
|
||||
world.addMaterial("redMetal", new Raytracing::BrushedMetalMaterial{Raytracing::Vec4{1.0, 0.4, 0.4, 1}, 0.6f});
|
||||
world.addMaterial("blueMetal", new Raytracing::MetalMaterial{Raytracing::Vec4{0.4, 0.4, 1.0, 1}});
|
||||
|
||||
//world.add(new Raytracing::SphereObject({0,0,-1,0{}, 0.5, world.getMaterial("redDiffuse")));
|
||||
//world.add(new Raytracing::SphereObject({-1,0,-1,0}, 0.5, world.getMaterial("blueMetal")));
|
||||
//world.add(new Raytracing::SphereObject({1,0,-1,0}, 0.5, world.getMaterial("redMetal")));
|
||||
world.add(new Raytracing::SphereObject({0,-100.5,-1,0}, 100, world.getMaterial("greenDiffuse")));
|
||||
//world.add(new Raytracing::TriangleObject({0,0.1,-0.5f,0}, {{-0.5, -0.5, 0.0}, {0.5, -0.5, 0.0}, {0.0, 0.5, 0}}, world.getMaterial("greenDiffuse")));
|
||||
world.add(new Raytracing::ModelObject({0, 1, 0}, spider, world.getMaterial("redDiffuse")));
|
||||
world.add(new Raytracing::ModelObject({-5, 0.5, 0}, plane, world.getMaterial("greenMetal")));
|
||||
world.add(new Raytracing::ModelObject({5, 1, 0}, house, world.getMaterial("redDiffuse")));
|
||||
world.add(new Raytracing::ModelObject({0, 0, -5}, house, world.getMaterial("blueDiffuse")));
|
||||
world.add(new Raytracing::ModelObject({0, 0, 5}, house, world.getMaterial("blueDiffuse")));
|
||||
|
||||
Raytracing::Raycaster raycaster {camera, image, world, parser};
|
||||
|
||||
ilog << "Running raycaster!\n";
|
||||
raycaster.run();
|
||||
|
||||
Raytracing::ImageOutput imageOutput(image);
|
||||
|
||||
auto t = std::time(nullptr);
|
||||
auto now = std::localtime(&t);
|
||||
std::stringstream timeString;
|
||||
timeString << (1900 + now->tm_year);
|
||||
timeString << "-";
|
||||
timeString << (1 + now->tm_mon);
|
||||
timeString << "-";
|
||||
timeString << now->tm_mday;
|
||||
timeString << " ";
|
||||
timeString << now->tm_hour;
|
||||
timeString << ":";
|
||||
timeString << now->tm_min;
|
||||
timeString << ":";
|
||||
timeString << now->tm_sec;
|
||||
ilog << "Writing Image!\n";
|
||||
imageOutput.write(parser.getOptionValue("--output") + timeString.str(), parser.getOptionValue("--format"));
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* Created by Brett Terpstra 6920201 on 17/10/22.
|
||||
* Copyright (c) 2022 Brett Terpstra. All Rights Reserved.
|
||||
*/
|
||||
#include <math/colliders.h>
|
||||
|
||||
namespace Raytracing {
|
||||
|
||||
PRECISION_TYPE AABB::longestDistanceFromCenter() const {
|
||||
Vec4 center = getCenter();
|
||||
PRECISION_TYPE maxX = std::abs(max.x() - center.x());
|
||||
PRECISION_TYPE minX = std::abs(min.x() - center.x());
|
||||
PRECISION_TYPE maxY = std::abs(max.y() - center.y());
|
||||
PRECISION_TYPE minY = std::abs(min.y() - center.y());
|
||||
PRECISION_TYPE maxZ = std::abs(max.z() - center.z());
|
||||
PRECISION_TYPE minZ = std::abs(min.z() - center.z());
|
||||
return std::max(maxX, std::max(minX, std::max(maxY, std::max(minY, std::max(maxZ, minZ)))));
|
||||
}
|
||||
|
||||
PRECISION_TYPE AABB::avgDistanceFromCenter() const {
|
||||
Vec4 center = getCenter();
|
||||
PRECISION_TYPE maxX = std::abs(max.x() - center.x());
|
||||
PRECISION_TYPE minX = std::abs(min.x() - center.x());
|
||||
PRECISION_TYPE maxY = std::abs(max.y() - center.y());
|
||||
PRECISION_TYPE minY = std::abs(min.y() - center.y());
|
||||
PRECISION_TYPE maxZ = std::abs(max.z() - center.z());
|
||||
PRECISION_TYPE minZ = std::abs(min.z() - center.z());
|
||||
maxX *= maxX;
|
||||
minX *= minX;
|
||||
maxY *= maxY;
|
||||
minY *= minY;
|
||||
maxZ *= maxZ;
|
||||
minZ *= minZ;
|
||||
return std::sqrt(maxX + minX + maxY + minY + maxZ + minZ);
|
||||
}
|
||||
|
||||
int AABB::longestAxis() const {
|
||||
PRECISION_TYPE X = std::abs(max.x() - min.x());
|
||||
PRECISION_TYPE Y = std::abs(max.y() - min.y());
|
||||
PRECISION_TYPE Z = std::abs(max.z() - min.z());
|
||||
return X > Y && X > Z ? 0 : Y > Z ? 1 : 2;
|
||||
}
|
||||
|
||||
PRECISION_TYPE AABB::longestAxisLength() const {
|
||||
PRECISION_TYPE X = std::abs(max.x() - min.x());
|
||||
PRECISION_TYPE Y = std::abs(max.y() - min.y());
|
||||
PRECISION_TYPE Z = std::abs(max.z() - min.z());
|
||||
return X > Y && X > Z ? X : Y > Z ? Y : Z;
|
||||
}
|
||||
|
||||
std::pair<AABB, AABB> AABB::splitByLongestAxis() {
|
||||
PRECISION_TYPE X = std::abs(max.x() - min.x());
|
||||
PRECISION_TYPE X2 = X/2;
|
||||
PRECISION_TYPE Y = std::abs(max.y() - min.y());
|
||||
PRECISION_TYPE Y2 = Y/2;
|
||||
PRECISION_TYPE Z = std::abs(max.z() - min.z());
|
||||
PRECISION_TYPE Z2 = Z/2;
|
||||
// return the new split AABBs based on the calculated max lengths, but only in their respective axis.
|
||||
if (X > Y && X > Z) {
|
||||
return {{min.x(), min.y(), min.z(), max.x()-X2, max.y(), max.z()},
|
||||
// start the second AABB at the end of the first AABB.
|
||||
{min.x()+X2, min.y(), min.z(), max.x(), max.y(), max.z()}};
|
||||
} else if (Y > Z) {
|
||||
return {{min.x(), min.y(), min.z(), max.x(), max.y()-Y2, max.z()}, {min.x(), min.y()+Y2, min.z(), max.x(), max.y(), max.z()}};
|
||||
} else {
|
||||
return {{min.x(), min.y(), min.z(), max.x(), max.y(), max.z()-Z2}, {min.x(), min.y(), min.z()+Z2, max.x(), max.y(), max.z()}};
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Sources for designing these various algorithms
|
||||
* TODO: test these methods for performance
|
||||
* https://www.realtimerendering.com/intersections.html
|
||||
* https://web.archive.org/web/20090803054252/http://tog.acm.org/resources/GraphicsGems/gems/RayBox.c
|
||||
* https://www.scratchapixel.com/lessons/3d-basic-rendering/minimal-ray-tracer-rendering-simple-shapes/ray-box-intersection
|
||||
* https://tavianator.com/2011/ray_box.html
|
||||
* https://medium.com/@bromanz/another-view-on-the-classic-ray-aabb-intersection-algorithm-for-bvh-traversal-41125138b525
|
||||
*/
|
||||
|
||||
bool AABB::simpleSlabRayAABBMethod(const Ray& ray, PRECISION_TYPE tmin, PRECISION_TYPE tmax){
|
||||
// branch less design
|
||||
// adapted from 2d to fit our 3d scene.
|
||||
// (turns out this is actually a pretty standard design, but could use some optimization)
|
||||
PRECISION_TYPE tx1 = (min.x() - ray.getStartingPoint().x())*ray.getInverseDirection().x();
|
||||
PRECISION_TYPE tx2 = (max.x() - ray.getStartingPoint().x())*ray.getInverseDirection().x();
|
||||
|
||||
tmin = std::min(tmin, std::min(tx1, tx2));
|
||||
tmax = std::max(tmax, std::max(tx1, tx2));
|
||||
|
||||
PRECISION_TYPE ty1 = (min.y() - ray.getStartingPoint().y())*ray.getInverseDirection().y();
|
||||
PRECISION_TYPE ty2 = (max.y() - ray.getStartingPoint().y())*ray.getInverseDirection().y();
|
||||
|
||||
tmin = std::max(tmin, std::min(ty1, ty2));
|
||||
tmax = std::min(tmax, std::max(ty1, ty2));
|
||||
|
||||
PRECISION_TYPE tz1 = (min.z() - ray.getStartingPoint().z())*ray.getInverseDirection().z();
|
||||
PRECISION_TYPE tz2 = (max.z() - ray.getStartingPoint().z())*ray.getInverseDirection().z();
|
||||
|
||||
tmin = std::max(tmin, std::min(tz1, tz2));
|
||||
tmax = std::min(tmax, std::max(tz1, tz2));
|
||||
|
||||
return tmax > std::max(tmin, 0.0);
|
||||
}
|
||||
|
||||
bool AABB::intersects(const Ray& ray, PRECISION_TYPE tmin, PRECISION_TYPE tmax) {
|
||||
return simpleSlabRayAABBMethod(ray, tmin, tmax);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* Created by Brett Terpstra 6920201 on 16/10/22.
|
||||
* Copyright (c) 2022 Brett Terpstra. All Rights Reserved.
|
||||
*/
|
||||
#include <raytracing.h>
|
||||
#include <queue>
|
||||
|
||||
namespace Raytracing {
|
||||
|
||||
Ray Camera::projectRay(PRECISION_TYPE x, PRECISION_TYPE y) {
|
||||
// transform the x and y to points from image coords to be inside the camera's viewport.
|
||||
double transformedX = (x / (image.getWidth() - 1));
|
||||
auto transformedY = (y / (image.getHeight() - 1));
|
||||
// then generate a ray which extends out from the camera position in the direction with respects to its position on the image
|
||||
return {position, imageOrigin + transformedX * horizontalAxis + transformedY * verticalAxis - position};
|
||||
}
|
||||
|
||||
void Camera::lookAt(const Vec4& pos, const Vec4& lookAtPos, const Vec4& up) {
|
||||
// standard camera lookAt function
|
||||
auto w = (pos - lookAtPos).normalize();
|
||||
auto u = (Vec4::cross(up, w)).normalize();
|
||||
auto v = Vec4::cross(w, u);
|
||||
|
||||
position = pos;
|
||||
horizontalAxis = viewportWidth * u;
|
||||
verticalAxis = viewportHeight * v;
|
||||
imageOrigin = position - horizontalAxis/2 - verticalAxis/2 - w;
|
||||
}
|
||||
|
||||
void Camera::setRotation(const PRECISION_TYPE yaw, const PRECISION_TYPE pitch, const PRECISION_TYPE roll) {
|
||||
// TODO:
|
||||
}
|
||||
|
||||
void Raycaster::run() {
|
||||
for (int i = 0; i < image.getWidth(); i++){
|
||||
for (int j = 0; j < image.getHeight(); j++){
|
||||
Raytracing::Vec4 color;
|
||||
// TODO: profile for speed;
|
||||
for (int s = 0; s < raysPerPixel; s++){
|
||||
// simulate anti aliasing by generating rays with very slight random directions
|
||||
color = color + raycast(camera.projectRay(i + rnd.getDouble(), j + rnd.getDouble()), 0);
|
||||
}
|
||||
PRECISION_TYPE sf = 1.0 / raysPerPixel;
|
||||
// apply pixel color with gamma correction
|
||||
image.setPixelColor(i, j, {std::sqrt(sf * color.r()), std::sqrt(sf * color.g()), std::sqrt(sf * color.b())});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vec4 Raycaster::raycast(const Ray& ray, int depth) {
|
||||
if (depth > maxBounceDepth)
|
||||
return {0,0,0};
|
||||
|
||||
auto hit = world.checkIfHit(ray, 0.001, infinity);
|
||||
|
||||
if (hit.first.hit) {
|
||||
auto object = hit.second;
|
||||
auto scatterResults = object->getMaterial()->scatter(ray, hit.first);
|
||||
// if the material scatters the ray, ie casts a new one,
|
||||
if (scatterResults.scattered) // attenuate the recursive raycast by the material's color
|
||||
return scatterResults.attenuationColor * raycast(scatterResults.newRay, depth + 1);
|
||||
//tlog << "Not scattered? " << object->getMaterial() << "\n";
|
||||
return {0,0,0};
|
||||
}
|
||||
|
||||
// skybox color
|
||||
return {0.5, 0.7, 1.0};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Created by Brett Terpstra 6920201 on 18/10/22.
|
||||
* Copyright (c) 2022 Brett Terpstra. All Rights Reserved.
|
||||
*/
|
||||
#include <util/debug.h>
|
||||
#include <chrono>
|
||||
|
||||
namespace Raytracing {
|
||||
profiler::profiler(std::string name) {
|
||||
this->name = name;
|
||||
// currently unused as we don't have a UI yet.
|
||||
//TD::debugUI::addTab(this);
|
||||
}
|
||||
|
||||
void profiler::start() {
|
||||
start("Unnamed");
|
||||
}
|
||||
void profiler::start(const std::string& name) {
|
||||
auto p1 = std::chrono::high_resolution_clock::now();
|
||||
_start = std::chrono::duration_cast<std::chrono::nanoseconds>(p1.time_since_epoch()).count();
|
||||
timings[name] = std::pair<long, long>(_start, 0);
|
||||
}
|
||||
|
||||
void profiler::end() {
|
||||
end("Unnamed");
|
||||
}
|
||||
void profiler::end(const std::string& name) {
|
||||
auto p1 = std::chrono::high_resolution_clock::now();
|
||||
_end = std::chrono::duration_cast<std::chrono::nanoseconds>(p1.time_since_epoch()).count();
|
||||
timings[name] = std::pair<long, long>(timings[name].first, _end);
|
||||
}
|
||||
|
||||
void profiler::print() {
|
||||
ilog << "Profiler " << name << " recorded: ";
|
||||
for (std::pair<std::string, std::pair<long, long>> e : timings){
|
||||
ilog << "\t" << e.first << " took " << ((double)(e.second.second - e.second.first) / 1000000.0) << "ms to run!";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void profiler::endAndPrint() {
|
||||
end();
|
||||
print();
|
||||
}
|
||||
|
||||
void profiler::render() {
|
||||
// currently unused as we don't have a UI yet.
|
||||
/*ImGui::Text("CPU Timings:");
|
||||
ImGui::Indent();
|
||||
for (std::pair<std::string, std::pair<long, long>> e : timings) {
|
||||
ImGui::Text("Elapsed Time(%s): %fms", e.first.c_str(), (double) ((e.second.second - e.second.first) / 1000000.0));
|
||||
}
|
||||
ImGui::Unindent();
|
||||
ImGui::NewLine();*/
|
||||
}
|
||||
|
||||
profiler::~profiler() {
|
||||
// currently unused as we don't have a UI yet.
|
||||
//TD::debugUI::deleteTab(this);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Created by Brett Terpstra 6920201 on 17/10/22.
|
||||
* Copyright (c) 2022 Brett Terpstra. All Rights Reserved.
|
||||
*/
|
||||
#include <util/models.h>
|
||||
#include <fstream>
|
||||
#include <ios>
|
||||
|
||||
Raytracing::ModelData Raytracing::OBJLoader::loadModel(std::string file) {
|
||||
std::ifstream modelFile;
|
||||
|
||||
modelFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
|
||||
modelFile.open(file);
|
||||
std::stringstream modelSource;
|
||||
|
||||
std::cout.flush();
|
||||
// read the entire file into a string
|
||||
modelSource << modelFile.rdbuf();
|
||||
|
||||
modelFile.close();
|
||||
std::string modelUnprocessedSource = modelSource.str();
|
||||
|
||||
auto lines = String::split(modelUnprocessedSource, "\n");
|
||||
ilog << "Loading of model file " << file << " complete! Now processing " << lines.size() << " lines.\n";
|
||||
|
||||
ModelData data;
|
||||
|
||||
for (const auto& line : lines){
|
||||
auto spaces = String::split(line, " ");
|
||||
if (line.starts_with("v ")) { // vertex
|
||||
data.vertices.emplace_back(std::stof(spaces[1]), std::stof(spaces[2]), std::stof(spaces[3]));
|
||||
} else if (line.starts_with("vt ")){ // uv
|
||||
data.uvs.emplace_back(std::stof(spaces[1]), std::stof(spaces[2]), 0);
|
||||
} else if (line.starts_with("vn ")){ // normal
|
||||
data.normals.emplace_back(std::stof(spaces[1]), std::stof(spaces[2]), std::stof(spaces[3]));
|
||||
} else if (line.starts_with("f ")){ // face
|
||||
// we've reached the faces, we'll need to process them later.
|
||||
auto t1 = String::split(spaces[1], "/");
|
||||
auto t2 = String::split(spaces[2], "/");
|
||||
auto t3 = String::split(spaces[3], "/");
|
||||
face f {};
|
||||
|
||||
// obj files are 1 indexed,
|
||||
// but arrays are 0 indexed,
|
||||
// must be transformed.
|
||||
f.v1 = std::stoi(t1[0])-1;
|
||||
f.v2 = std::stoi(t2[0])-1;
|
||||
f.v3 = std::stoi(t3[0])-1;
|
||||
|
||||
f.uv1 = std::stoi(t1[1])-1;
|
||||
f.uv2 = std::stoi(t2[1])-1;
|
||||
f.uv3 = std::stoi(t3[1])-1;
|
||||
|
||||
f.n1 = std::stoi(t1[2])-1;
|
||||
f.n2 = std::stoi(t2[2])-1;
|
||||
f.n3 = std::stoi(t3[2])-1;
|
||||
|
||||
data.faces.push_back(f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ilog << "Completed extracting vertex data from model file " << file << "!\n";
|
||||
return data;
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* Created by Brett Terpstra 6920201 on 14/10/22.
|
||||
* Copyright (c) Brett Terpstra 2022 All Rights Reserved
|
||||
*/
|
||||
#include "util/parser.h"
|
||||
#include <sstream>
|
||||
namespace Raytracing {
|
||||
|
||||
|
||||
Parser::Parser() {
|
||||
// only to add the description (very annoying we have to have double {}!)
|
||||
addOption({{"--help"}, {"-h"}}, "Show this help menu!");
|
||||
}
|
||||
|
||||
std::string Parser::getOptionValue(const std::string& option) {
|
||||
return raw_values.contains(option) ? raw_values.at(option) : defaultValues.at(option);
|
||||
}
|
||||
|
||||
int Parser::parse(char **args, int argc) {
|
||||
// first arg is always the command path
|
||||
// which we don't care about.
|
||||
for (int i = 1; i < argc; i++) {
|
||||
std::string option = std::string(args[i]);
|
||||
std::string value = "true";
|
||||
// we only want to get the value if it was actually provided.
|
||||
if (i + 1 < argc) {
|
||||
value = std::string(args[i + 1]);
|
||||
// must make sure we don't skip over valuable parasble options!
|
||||
if (!value.starts_with("-")) {
|
||||
// we must add to the value of i since we don't want to try and parse values.
|
||||
i++;
|
||||
} else // if it does start with -, it's an option, we need to reset to true.
|
||||
value = "true";
|
||||
}
|
||||
// if values are provided to us with -- or - we simply don't care about them!
|
||||
if (option.starts_with("-"))
|
||||
raw_values.insert(std::pair(option, value));
|
||||
else
|
||||
throw std::runtime_error("Unfortunately an invalid argument was provided! {" + option + "}");
|
||||
}
|
||||
if (hasOption({{"--help"}, {"-h"}})){
|
||||
std::cout << "Raytracer help information:" << std::endl;
|
||||
for (const auto& d : descriptions){
|
||||
std::cout << d;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Parser::addOption(const std::string& option, const std::string& description, const std::string& defaultValue) {
|
||||
// we only want to add if the default value isn't empty,
|
||||
// since we might want to use the option as a single optional flag and not a value.
|
||||
if (!Raytracing::String::trim_copy(defaultValue).empty())
|
||||
defaultValues.insert(std::pair(option, defaultValue));
|
||||
descriptions.emplace_back(option + " :" + description);
|
||||
}
|
||||
|
||||
bool Parser::hasOptionChanged(const std::string& option) {
|
||||
return raw_values.contains(option) && defaultValues.contains(option) && raw_values.at(option) != defaultValues.at(option);
|
||||
}
|
||||
|
||||
void Parser::addOption(const std::vector<std::string>& options, const std::string &description, const std::string &defaultValue) {
|
||||
// add option like normal
|
||||
for (const auto& option : options){
|
||||
if (!Raytracing::String::trim_copy(defaultValue).empty())
|
||||
defaultValues.insert(std::pair(option, defaultValue));
|
||||
}
|
||||
// but improve the description for multi-option
|
||||
std::stringstream desStr;
|
||||
|
||||
// add all the options to the description, seperated by ","
|
||||
for (int i = 0; i < options.size(); i++){
|
||||
desStr << options[i];
|
||||
if (i != options.size()-1)
|
||||
desStr << ", ";
|
||||
}
|
||||
desStr << " :";
|
||||
desStr << description;
|
||||
|
||||
descriptions.emplace_back(desStr.str());
|
||||
}
|
||||
|
||||
bool Parser::hasOption(const std::string &option) {
|
||||
return raw_values.contains(option);
|
||||
}
|
||||
|
||||
bool Parser::hasOption(const std::vector<std::string> &options) {
|
||||
for (const auto& option : options)
|
||||
if (hasOption(option))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void Parser::printAllInInfo() {
|
||||
// print all with default values
|
||||
for (const auto& opt : defaultValues){
|
||||
ilog << opt.first << ": " << (raw_values.contains(opt.first) ? raw_values.at(opt.first) : opt.second) << " (default: " << opt.second << ")" << std::endl;
|
||||
}
|
||||
// then just print all the ones which don't have but where provided
|
||||
for (const auto& opt : raw_values){
|
||||
if (!defaultValues.contains(opt.first))
|
||||
ilog << "With: " << opt.first;
|
||||
}
|
||||
}
|
||||
|
||||
void Parser::printDifferenceInInfo() {
|
||||
for (const auto& opt : raw_values){
|
||||
if (defaultValues.contains(opt.first))
|
||||
if (opt.second == defaultValues.at(opt.first))
|
||||
ilog << opt.first << ": " << opt.second << " (default: " << defaultValues.at(opt.first) << ")" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,212 @@
|
|||
/*
|
||||
* Created by Brett Terpstra 6920201 on 16/10/22.
|
||||
* Copyright (c) 2022 Brett Terpstra. All Rights Reserved.
|
||||
*/
|
||||
#include <world.h>
|
||||
#include <raytracing.h>
|
||||
|
||||
namespace Raytracing {
|
||||
|
||||
World::~World() {
|
||||
for (auto* p: objects)
|
||||
delete (p);
|
||||
for (const auto& p: materials)
|
||||
delete (p.second);
|
||||
//delete(bvhTree);
|
||||
}
|
||||
|
||||
HitData SphereObject::checkIfHit(const Ray& ray, PRECISION_TYPE min, PRECISION_TYPE max) const {
|
||||
PRECISION_TYPE radiusSquared = radius * radius;
|
||||
// move the ray to be with respects to the sphere
|
||||
Vec4 RayWRTSphere = ray.getStartingPoint() - position;
|
||||
// now determine the discriminant for the quadratic formula for the function of line sphere intercept
|
||||
PRECISION_TYPE a = ray.getDirection().lengthSquared();
|
||||
PRECISION_TYPE b = Raytracing::Vec4::dot(RayWRTSphere, ray.getDirection());
|
||||
PRECISION_TYPE c = RayWRTSphere.lengthSquared() - radiusSquared;
|
||||
// > 0: the hit has two roots, meaning we hit both sides of the sphere
|
||||
// = 0: the ray has one root, we hit the edge of the sphere
|
||||
// < 0: ray isn't inside the sphere.
|
||||
PRECISION_TYPE discriminant = b * b - (a * c);
|
||||
|
||||
// < 0: ray isn't inside the sphere. Don't need to bother calculating the roots.
|
||||
if (discriminant < 0)
|
||||
return {false, Vec4(), Vec4(), 0};
|
||||
|
||||
// now we have to find the root which exists inside our range [min,max]
|
||||
auto root = (-b - std::sqrt(discriminant)) / a;
|
||||
// if the first root isn't in our range
|
||||
if (root < min || root > max) {
|
||||
// check the second root
|
||||
root = (-b + std::sqrt(discriminant)) / a;
|
||||
if (root < min || root > max) {
|
||||
// if the second isn't in the range then we also must return false.
|
||||
return {false, Vec4(), Vec4(), 0};
|
||||
}
|
||||
}
|
||||
// the hit point is where the ray is when extended to the root
|
||||
auto RayAtRoot = ray.along(root);
|
||||
// The normal of a sphere is just the point of the hit minus the center position
|
||||
auto normal = (RayAtRoot - position).normalize();
|
||||
|
||||
/*if (Raytracing::vec4::dot(ray.getDirection(), normal) > 0.0) {
|
||||
tlog << "ray inside sphere\n";
|
||||
} else
|
||||
tlog << "ray outside sphere\n";
|
||||
*/
|
||||
return {true, RayAtRoot, normal, root};
|
||||
}
|
||||
|
||||
std::pair<HitData, Object*> World::checkIfHit(const Ray& ray, PRECISION_TYPE min, PRECISION_TYPE max) const {
|
||||
/*if (bvhTree != nullptr){
|
||||
auto hResult = HitData{false, Vec4(), Vec4(), max};
|
||||
Object* objPtr = nullptr;
|
||||
|
||||
auto intersected = bvhTree->rayIntersect(ray, min, max);
|
||||
|
||||
//dlog << "Intersections " << intersected.size() << " " << ray << "\n";
|
||||
|
||||
for (auto* ptr : intersected) {
|
||||
auto cResult = ptr->checkIfHit(ray, min, hResult.length);
|
||||
if (cResult.hit) {
|
||||
hResult = cResult;
|
||||
objPtr = ptr;
|
||||
}
|
||||
}
|
||||
// after we check the BVH, we have to check for other missing objects
|
||||
// since stuff like spheres currently don't have AABB and AABB isn't a requirement
|
||||
// for the object class (to be assigned)
|
||||
for (auto* obj: bvhTree->noAABBObjects) {
|
||||
// check up to the point of the last closest hit,
|
||||
// will give the closest object's hit result
|
||||
auto cResult = obj->checkIfHit(ray, min, hResult.length);
|
||||
if (cResult.hit) {
|
||||
hResult = cResult;
|
||||
objPtr = obj;
|
||||
}
|
||||
}
|
||||
|
||||
return {hResult, objPtr};
|
||||
} else {*/
|
||||
// rejection algo without using a binary space partitioning data structure
|
||||
auto hResult = HitData{false, Vec4(), Vec4(), max};
|
||||
Object* objPtr = nullptr;
|
||||
for (auto* obj: objects) {
|
||||
// check up to the point of the last closest hit,
|
||||
// will give the closest object's hit result
|
||||
auto cResult = obj->checkIfHit(ray, min, hResult.length);
|
||||
if (cResult.hit) {
|
||||
hResult = cResult;
|
||||
objPtr = obj;
|
||||
}
|
||||
}
|
||||
return {hResult, objPtr};
|
||||
//}
|
||||
}
|
||||
|
||||
void World::generateBVH() {
|
||||
//bvhTree = new BVHTree(objects);
|
||||
}
|
||||
|
||||
ScatterResults DiffuseMaterial::scatter(const Ray& ray, const HitData& hitData) const {
|
||||
Vec4 newRay = hitData.normal + Raytracing::Raycaster::randomUnitVector().normalize();
|
||||
|
||||
// rays that are close to zero are liable to floating point precision errors
|
||||
if (newRay.x() < EPSILON && newRay.y() < EPSILON && newRay.z() < EPSILON && newRay.w() < EPSILON)
|
||||
newRay = hitData.normal;
|
||||
|
||||
return {true, Ray{hitData.hitPoint, newRay}, getBaseColor()};
|
||||
}
|
||||
|
||||
ScatterResults MetalMaterial::scatter(const Ray& ray, const HitData& hitData) const {
|
||||
// create a ray reflection
|
||||
Vec4 newRay = reflect(ray.getDirection().normalize(), hitData.normal);
|
||||
// make sure our reflected ray is outside the sphere and doesn't point inwards
|
||||
bool shouldReflect = Vec4::dot(newRay, hitData.normal) > 0;
|
||||
return {shouldReflect, Ray{hitData.hitPoint, newRay}, getBaseColor()};
|
||||
}
|
||||
|
||||
ScatterResults BrushedMetalMaterial::scatter(const Ray& ray, const HitData& hitData) const {
|
||||
// create a ray reflection
|
||||
Vec4 newRay = reflect(ray.getDirection().normalize(), hitData.normal);
|
||||
// make sure our reflected ray is outside the sphere and doesn't point inwards
|
||||
bool shouldReflect = Vec4::dot(newRay, hitData.normal) > 0;
|
||||
return {shouldReflect, Ray{hitData.hitPoint, newRay + Raycaster::randomUnitVector() * fuzzyness}, getBaseColor()};
|
||||
}
|
||||
|
||||
static HitData checkIfTriangleGotHit(const Triangle& theTriangle, const Vec4& position, const Ray& ray, PRECISION_TYPE min, PRECISION_TYPE max) {
|
||||
// Möller–Trumbore intersection algorithm
|
||||
// https://www.scratchapixel.com/lessons/3d-basic-rendering/ray-tracing-rendering-a-triangle/moller-trumbore-ray-triangle-intersection
|
||||
// https://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm
|
||||
Vec4 edge1, edge2, h, s, q;
|
||||
PRECISION_TYPE a, f, u, v;
|
||||
edge1 = (theTriangle.vertex2 + position) - (theTriangle.vertex1 + position);
|
||||
edge2 = (theTriangle.vertex3 + position) - (theTriangle.vertex1 + position);
|
||||
|
||||
h = Vec4::cross(ray.getDirection(), edge2);
|
||||
a = Vec4::dot(edge1, h);
|
||||
|
||||
if (a > -EPSILON && a < EPSILON)
|
||||
return {false, Vec4(), Vec4(), 0}; //parallel to triangle
|
||||
|
||||
f = 1.0 / a;
|
||||
s = ray.getStartingPoint() - (theTriangle.vertex1 + position);
|
||||
u = f * Vec4::dot(s, h);
|
||||
|
||||
if (u < 0.0 || u > 1.0)
|
||||
return {false, Vec4(), Vec4(), 0};
|
||||
|
||||
q = Vec4::cross(s, edge1);
|
||||
v = f * Vec4::dot(ray.getDirection(), q);
|
||||
if (v < 0.0 || u + v > 1.0)
|
||||
return {false, Vec4(), Vec4(), 0};
|
||||
|
||||
// At this stage we can compute t to find out where the intersection point is on the line.
|
||||
PRECISION_TYPE t = f * Vec4::dot(edge2, q);
|
||||
// keep t in reasonable bounds, ensuring we respect depth
|
||||
if (t > EPSILON && t >= min && t <= max) {
|
||||
// ray intersects
|
||||
Vec4 rayIntersectionPoint = ray.along(t);
|
||||
Vec4 normal;
|
||||
// normal = theTriangle.findClosestNormal(rayIntersectionPoint - position);
|
||||
if (theTriangle.hasNormals) // returning the closest normal is extra computation when n1 would likely be fine.
|
||||
normal = theTriangle.normal1;
|
||||
else {
|
||||
// standard points to normal algorithm but using already computed edges
|
||||
normal = Vec4{edge1.y() * edge2.z(), edge1.z() * edge2.x(), edge1.x() * edge2.y()} -
|
||||
Vec4{edge1.z() * edge2.y(), edge1.x() * edge2.z(), edge1.y() * edge2.x()};
|
||||
}
|
||||
return {true, rayIntersectionPoint, normal, t};
|
||||
}
|
||||
return {false, Vec4(), Vec4(), 0};
|
||||
}
|
||||
|
||||
HitData TriangleObject::checkIfHit(const Ray& ray, PRECISION_TYPE min, PRECISION_TYPE max) const {
|
||||
return checkIfTriangleGotHit(theTriangle, position, ray, min, max);
|
||||
}
|
||||
|
||||
HitData ModelObject::checkIfHit(const Ray& ray, PRECISION_TYPE min, PRECISION_TYPE max) const {
|
||||
auto hResult = HitData{false, Vec4(), Vec4(), max};
|
||||
// must check through all the triangles in the object
|
||||
// respecting depth along the way
|
||||
// but reducing the max it can reach my the last longest vector length.
|
||||
for (const Triangle& t : triangles) {
|
||||
auto cResult = checkIfTriangleGotHit(t, position, ray, min, hResult.length);
|
||||
if (cResult.hit)
|
||||
hResult = cResult;
|
||||
}
|
||||
/*auto hResult = HitData{false, Vec4(), Vec4(), max};
|
||||
|
||||
auto intersected = tree->rayIntersect(ray, min, max);
|
||||
|
||||
for (auto t : intersected){
|
||||
// apparently this kind of casting is okay
|
||||
// which makes sense since the actual data behind it is a empty object
|
||||
// just this is really bad and im too annoyed to figure out a better way. TODO:.
|
||||
auto cResult = checkIfTriangleGotHit(((EmptyObject*)(t))->tri, position, ray, min, hResult.length);
|
||||
if (cResult.hit)
|
||||
hResult = cResult;
|
||||
}*/
|
||||
|
||||
return hResult;
|
||||
}
|
||||
}
|
Binary file not shown.
|
@ -1,105 +1,18 @@
|
|||
# ninja log v5
|
||||
1 1269 1666191947233881035 CMakeFiles/Step_2.dir/src/main.cpp.o 387235f0a298f257
|
||||
1 1329 1666208294813201609 CMakeFiles/Step_2.dir/src/math/colliders.cpp.o 7957915dde476a69
|
||||
10 1796 1666150208085056398 CMakeFiles/Step_2.dir/src/util/parser.cpp.o 4bae007146d86363
|
||||
1 840 1666191946805877238 CMakeFiles/Step_2.dir/src/raytracing.cpp.o e78f62563fb6428
|
||||
1269 1307 1666191947269881356 Step_2 2fd6ed1a72fdaa11
|
||||
9 1453 1666150207745047625 CMakeFiles/Step_2.dir/src/util/debug.cpp.o e1fa95ca3d6a9ce2
|
||||
1 1770 1666208295257215005 CMakeFiles/Step_2.dir/src/util/models.cpp.o 12734a9214cf8718
|
||||
1 1112 1666150207401038752 CMakeFiles/Step_2.dir/src/globals.cpp.o 5fc3599d89d4b8a2
|
||||
2 1036 1666191947001878976 CMakeFiles/Step_2.dir/src/world.cpp.o 713f22c63a585103
|
||||
1 2726 1666208296213243852 CMakeFiles/Step_2.dir/src/image/image.cpp.o 4d16c0d3199b9aa8
|
||||
1 874 1666208421288995782 CMakeFiles/Step_2.dir/src/globals.cpp.o 8caaf06f90e0c038
|
||||
2 1437 1666208421849012493 CMakeFiles/Step_2.dir/src/util/debug.cpp.o 2bec3f4c06e82095
|
||||
1 1911 1666240985448103414 CMakeFiles/Step_2.dir/src/main.cpp.o 8da3d580eb6e9a1
|
||||
1 1263 1666240984800084042 CMakeFiles/Step_2.dir/src/math/colliders.cpp.o c0cac41f151ac323
|
||||
2 1440 1666208421853012610 CMakeFiles/Step_2.dir/src/util/parser.cpp.o 67ef710b86dd32d6
|
||||
2 1496 1666208421909014283 CMakeFiles/Step_2.dir/src/raytracing.cpp.o d0ae86d239f7153
|
||||
2 1709 1666208422121020608 CMakeFiles/Step_2.dir/src/math/colliders.cpp.o c0cac41f151ac323
|
||||
2 1728 1666208422141021205 CMakeFiles/Step_2.dir/src/world.cpp.o 9ae7336b91871660
|
||||
2 1966 1666208422381028366 CMakeFiles/Step_2.dir/src/util/models.cpp.o 7aa2ab72df0658e
|
||||
1 2034 1666208422449030392 CMakeFiles/Step_2.dir/src/main.cpp.o 8da3d580eb6e9a1
|
||||
1 2886 1666208423301055812 CMakeFiles/Step_2.dir/src/image/image.cpp.o f94f9df081e81271
|
||||
2887 2929 1666208423341057004 Step_2 7437176fc9e68547
|
||||
2 1224 1666210629933631775 CMakeFiles/Step_2.dir/src/math/colliders.cpp.o c0cac41f151ac323
|
||||
2 1374 1666210630081636055 CMakeFiles/Step_2.dir/src/raytracing.cpp.o d0ae86d239f7153
|
||||
2 1548 1666210630257641141 CMakeFiles/Step_2.dir/src/world.cpp.o 9ae7336b91871660
|
||||
2 1652 1666210630361644148 CMakeFiles/Step_2.dir/src/util/models.cpp.o 7aa2ab72df0658e
|
||||
2 1913 1666210630621651665 CMakeFiles/Step_2.dir/src/main.cpp.o 8da3d580eb6e9a1
|
||||
1 2643 1666210631353672824 CMakeFiles/Step_2.dir/src/image/image.cpp.o f94f9df081e81271
|
||||
2643 2681 1666210631389673865 Step_2 7437176fc9e68547
|
||||
1 1212 1666212386048431924 CMakeFiles/Step_2.dir/src/math/colliders.cpp.o c0cac41f151ac323
|
||||
1 1360 1666212386196434896 CMakeFiles/Step_2.dir/src/raytracing.cpp.o d0ae86d239f7153
|
||||
1 1484 1666212386320437387 CMakeFiles/Step_2.dir/src/world.cpp.o 9ae7336b91871660
|
||||
1 1611 1666212386448439957 CMakeFiles/Step_2.dir/src/util/models.cpp.o 7aa2ab72df0658e
|
||||
1 1875 1666212386712445259 CMakeFiles/Step_2.dir/src/main.cpp.o 8da3d580eb6e9a1
|
||||
1 2656 1666212387492460928 CMakeFiles/Step_2.dir/src/image/image.cpp.o f94f9df081e81271
|
||||
2656 2703 1666212387540461893 Step_2 7437176fc9e68547
|
||||
2 1250 1666212415865034379 CMakeFiles/Step_2.dir/src/math/colliders.cpp.o c0cac41f151ac323
|
||||
2 1375 1666212415989036899 CMakeFiles/Step_2.dir/src/raytracing.cpp.o d0ae86d239f7153
|
||||
2 1566 1666212416181040803 CMakeFiles/Step_2.dir/src/world.cpp.o 9ae7336b91871660
|
||||
2 1617 1666212416233041863 CMakeFiles/Step_2.dir/src/util/models.cpp.o 7aa2ab72df0658e
|
||||
1 1928 1666212416545048206 CMakeFiles/Step_2.dir/src/main.cpp.o 8da3d580eb6e9a1
|
||||
1 2621 1666212417237062279 CMakeFiles/Step_2.dir/src/image/image.cpp.o f94f9df081e81271
|
||||
2621 2660 1666212417273063011 Step_2 7437176fc9e68547
|
||||
1 1269 1666212741664012313 CMakeFiles/Step_2.dir/src/math/colliders.cpp.o c0cac41f151ac323
|
||||
1 1368 1666212741764014542 CMakeFiles/Step_2.dir/src/raytracing.cpp.o d0ae86d239f7153
|
||||
2 1543 1666212741940018466 CMakeFiles/Step_2.dir/src/world.cpp.o 9ae7336b91871660
|
||||
2 1646 1666212742044020786 CMakeFiles/Step_2.dir/src/util/models.cpp.o 7aa2ab72df0658e
|
||||
1 1910 1666212742308026673 CMakeFiles/Step_2.dir/src/main.cpp.o 8da3d580eb6e9a1
|
||||
1 2600 1666212742996042017 CMakeFiles/Step_2.dir/src/image/image.cpp.o f94f9df081e81271
|
||||
2600 2642 1666212743036042909 Step_2 7437176fc9e68547
|
||||
1 1791 1666215595209880342 CMakeFiles/Step_2.dir/src/math/colliders.cpp.o c0cac41f151ac323
|
||||
1 1840 1666215595257881412 CMakeFiles/Step_2.dir/src/raytracing.cpp.o d0ae86d239f7153
|
||||
1 1888 1666215595305882480 CMakeFiles/Step_2.dir/src/util/models.cpp.o 7aa2ab72df0658e
|
||||
1 1990 1666215595409884798 CMakeFiles/Step_2.dir/src/world.cpp.o 9ae7336b91871660
|
||||
1 2395 1666215595813893798 CMakeFiles/Step_2.dir/src/main.cpp.o 8da3d580eb6e9a1
|
||||
0 3052 1666215596469908420 CMakeFiles/Step_2.dir/src/image/image.cpp.o f94f9df081e81271
|
||||
3052 3125 1666215596541910026 Step_2 7437176fc9e68547
|
||||
1 788 1666215661443385226 CMakeFiles/Step_2.dir/src/math/colliders.cpp.o c0cac41f151ac323
|
||||
1 949 1666215661603388929 CMakeFiles/Step_2.dir/src/raytracing.cpp.o d0ae86d239f7153
|
||||
1 1072 1666215661727391802 CMakeFiles/Step_2.dir/src/world.cpp.o 9ae7336b91871660
|
||||
1 1204 1666215661859394855 CMakeFiles/Step_2.dir/src/util/models.cpp.o 7aa2ab72df0658e
|
||||
1 1441 1666215662095400322 CMakeFiles/Step_2.dir/src/main.cpp.o 8da3d580eb6e9a1
|
||||
1 2153 1666215662807416814 CMakeFiles/Step_2.dir/src/image/image.cpp.o f94f9df081e81271
|
||||
2153 2191 1666215662847417737 Step_2 7437176fc9e68547
|
||||
1 1023 1666216381510163196 CMakeFiles/Step_2.dir/src/raytracing.cpp.o d0ae86d239f7153
|
||||
1 1068 1666216381554164424 CMakeFiles/Step_2.dir/src/math/colliders.cpp.o c0cac41f151ac323
|
||||
1 1274 1666216381762170234 CMakeFiles/Step_2.dir/src/util/models.cpp.o 7aa2ab72df0658e
|
||||
1 1520 1666216382006177047 CMakeFiles/Step_2.dir/src/world.cpp.o 9ae7336b91871660
|
||||
1 1732 1666216382218182968 CMakeFiles/Step_2.dir/src/main.cpp.o 8da3d580eb6e9a1
|
||||
1 2646 1666216383134208548 CMakeFiles/Step_2.dir/src/image/image.cpp.o f94f9df081e81271
|
||||
2646 2694 1666216383182209890 Step_2 7437176fc9e68547
|
||||
1 878 1666217200233659155 CMakeFiles/Step_2.dir/src/raytracing.cpp.o d0ae86d239f7153
|
||||
0 1380 1666217200733673732 CMakeFiles/Step_2.dir/src/main.cpp.o 8da3d580eb6e9a1
|
||||
1380 1421 1666217200773674899 Step_2 7437176fc9e68547
|
||||
1 1341 1666217588772696599 CMakeFiles/Step_2.dir/src/main.cpp.o 8da3d580eb6e9a1
|
||||
1341 1380 1666217588812697660 Step_2 7437176fc9e68547
|
||||
0 1305 1666217602745067311 CMakeFiles/Step_2.dir/src/main.cpp.o 8da3d580eb6e9a1
|
||||
1305 1345 1666217602785068374 Step_2 7437176fc9e68547
|
||||
0 1381 1666217890128871360 CMakeFiles/Step_2.dir/src/main.cpp.o 8da3d580eb6e9a1
|
||||
1381 1424 1666217890172872578 Step_2 7437176fc9e68547
|
||||
0 997 1666218374062493836 CMakeFiles/Step_2.dir/src/world.cpp.o 9ae7336b91871660
|
||||
0 1320 1666218374386503078 CMakeFiles/Step_2.dir/src/main.cpp.o 8da3d580eb6e9a1
|
||||
1320 1358 1666218374422504103 Step_2 7437176fc9e68547
|
||||
1 942 1666218674383099244 CMakeFiles/Step_2.dir/src/math/colliders.cpp.o c0cac41f151ac323
|
||||
1 1007 1666218674447101084 CMakeFiles/Step_2.dir/src/raytracing.cpp.o d0ae86d239f7153
|
||||
1 1289 1666218674727109140 CMakeFiles/Step_2.dir/src/util/models.cpp.o 7aa2ab72df0658e
|
||||
1 1342 1666218674779110635 CMakeFiles/Step_2.dir/src/world.cpp.o 9ae7336b91871660
|
||||
1 1597 1666218675035117999 CMakeFiles/Step_2.dir/src/main.cpp.o 8da3d580eb6e9a1
|
||||
0 2433 1666218675871142048 CMakeFiles/Step_2.dir/src/image/image.cpp.o f94f9df081e81271
|
||||
2433 2478 1666218675919143428 Step_2 7437176fc9e68547
|
||||
0 1373 1666218737704921878 CMakeFiles/Step_2.dir/src/main.cpp.o 8da3d580eb6e9a1
|
||||
1373 1413 1666218737744923030 Step_2 7437176fc9e68547
|
||||
2 1448 1666220004262511612 CMakeFiles/Step_2.dir/src/math/colliders.cpp.o c0cac41f151ac323
|
||||
2 1482 1666220004294511493 CMakeFiles/Step_2.dir/src/raytracing.cpp.o d0ae86d239f7153
|
||||
2 1663 1666220004478510816 CMakeFiles/Step_2.dir/src/world.cpp.o 9ae7336b91871660
|
||||
2 1879 1666220004694510019 CMakeFiles/Step_2.dir/src/util/models.cpp.o 7aa2ab72df0658e
|
||||
1 2049 1666220004862509401 CMakeFiles/Step_2.dir/src/main.cpp.o 8da3d580eb6e9a1
|
||||
1 2908 1666220005722506266 CMakeFiles/Step_2.dir/src/image/image.cpp.o f94f9df081e81271
|
||||
2909 2956 1666220005770506091 Step_2 7437176fc9e68547
|
||||
1 1232 1666220317311320782 CMakeFiles/Step_2.dir/src/math/colliders.cpp.o c0cac41f151ac323
|
||||
1 1413 1666220317491322171 CMakeFiles/Step_2.dir/src/raytracing.cpp.o d0ae86d239f7153
|
||||
1 1568 1666220317647323373 CMakeFiles/Step_2.dir/src/world.cpp.o 9ae7336b91871660
|
||||
1 1708 1666220317783324422 CMakeFiles/Step_2.dir/src/util/models.cpp.o 7aa2ab72df0658e
|
||||
1 2010 1666220318087326770 CMakeFiles/Step_2.dir/src/main.cpp.o 8da3d580eb6e9a1
|
||||
1 2840 1666220318919333214 CMakeFiles/Step_2.dir/src/image/image.cpp.o f94f9df081e81271
|
||||
2840 2881 1666220318959333521 Step_2 7437176fc9e68547
|
||||
1 1419 1666240984956088704 CMakeFiles/Step_2.dir/src/raytracing.cpp.o d0ae86d239f7153
|
||||
2673 2712 1666240986252127454 Step_2 7437176fc9e68547
|
||||
2 1437 1666208421849012493 CMakeFiles/Step_2.dir/src/util/debug.cpp.o 2bec3f4c06e82095
|
||||
2 1611 1666240985148094447 CMakeFiles/Step_2.dir/src/util/models.cpp.o 7aa2ab72df0658e
|
||||
1 874 1666208421288995782 CMakeFiles/Step_2.dir/src/globals.cpp.o 8caaf06f90e0c038
|
||||
2 1517 1666240985056091694 CMakeFiles/Step_2.dir/src/world.cpp.o 9ae7336b91871660
|
||||
1 2673 1666240986212126256 CMakeFiles/Step_2.dir/src/image/image.cpp.o f94f9df081e81271
|
||||
1 1202 1666241022097198348 CMakeFiles/Step_2.dir/src/math/colliders.cpp.o c0cac41f151ac323
|
||||
1 1575 1666241022473209572 CMakeFiles/Step_2.dir/src/util/models.cpp.o 7aa2ab72df0658e
|
||||
1 1914 1666241022809219604 CMakeFiles/Step_2.dir/src/raytracing.cpp.o d0ae86d239f7153
|
||||
1 1942 1666241022837220441 CMakeFiles/Step_2.dir/src/main.cpp.o 8da3d580eb6e9a1
|
||||
2 1962 1666241022857221038 CMakeFiles/Step_2.dir/src/world.cpp.o 9ae7336b91871660
|
||||
1 2624 1666241023521240863 CMakeFiles/Step_2.dir/src/image/image.cpp.o f94f9df081e81271
|
||||
2624 2663 1666241023561242054 Step_2 7437176fc9e68547
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 358 KiB |
Binary file not shown.
After Width: | Height: | Size: 198 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,3 +1,3 @@
|
|||
Start testing: Oct 19 18:58 EDT
|
||||
Start testing: Oct 20 00:46 EDT
|
||||
----------------------------------------------------------
|
||||
End testing: Oct 19 18:58 EDT
|
||||
End testing: Oct 20 00:46 EDT
|
||||
|
|
|
@ -1 +1,4 @@
|
|||
cmake -DCMAKE_BUILD_TYPE=Deug -G Ninja ..
|
||||
#!/bin/bash
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -G Ninja ..
|
||||
ninja
|
||||
./Step_2
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
// AVX512 isn't supported on my CPU. We will use AVX2 since it is supported by most modern CPUs
|
||||
// THIS IS CURRENTLY BROKEN. DO NOT USE. it's a great idea, but I've run out of time to debug this. Will be in step 3
|
||||
//#define USE_SIMD_CPU
|
||||
#define USE_SIMD_CPU
|
||||
|
||||
#ifdef USE_SIMD_CPU
|
||||
|
||||
|
@ -44,7 +44,8 @@ namespace Raytracing {
|
|||
// makes it easy to convert between AVX and double data types.
|
||||
union {
|
||||
struct {
|
||||
double _x, _y, _z, _w;
|
||||
PRECISION_TYPE _x{}, _y{}, _z{}, _w{};
|
||||
//PRECISION_TYPE _w, _z, _y, _x;
|
||||
};
|
||||
__m256d avxData;
|
||||
};
|
||||
|
@ -56,10 +57,16 @@ namespace Raytracing {
|
|||
friend Vec4 operator/(const Vec4& left, const Vec4& right);
|
||||
public:
|
||||
|
||||
Vec4(): avxData{_mm256_set_pd(0.0, 0.0, 0.0, 0.0)} {}
|
||||
Vec4(PRECISION_TYPE x, PRECISION_TYPE y, PRECISION_TYPE z): avxData{_mm256_set_pd(x, y, z, 0)} {}
|
||||
Vec4(PRECISION_TYPE x, PRECISION_TYPE y, PRECISION_TYPE z, PRECISION_TYPE w): avxData{_mm256_set_pd(x, y, z, w)} {}
|
||||
Vec4(const Vec4& vec): avxData{_mm256_set_pd(vec.x(), vec.y(), vec.z(), vec.w())} {}
|
||||
Vec4(): avxData(_mm256_setzero_pd()) {}
|
||||
Vec4(PRECISION_TYPE x, PRECISION_TYPE y, PRECISION_TYPE z): avxData(_mm256_setr_pd(x, y, z, 0.0)) {
|
||||
//tlog << x << ":" << _x << " " << y << ":" << _y << " " << z << ":" << _z << "\n";
|
||||
}
|
||||
Vec4(PRECISION_TYPE x, PRECISION_TYPE y, PRECISION_TYPE z, PRECISION_TYPE w): avxData(_mm256_setr_pd(x, y, z, w)) {
|
||||
//dlog << x << ":" << _x << " " << y << ":" << _y << " " << z << ":" << _z << "\n";
|
||||
}
|
||||
Vec4(const Vec4& vec): avxData(_mm256_setr_pd(vec.x(), vec.y(), vec.z(), vec.w())) {
|
||||
//ilog << vec.x() << ":" << _x << " " << vec.y() << ":" << _y << " " << vec.z() << ":" << _z << "\n";
|
||||
}
|
||||
|
||||
// most of the modern c++ here is because clang tidy was annoying me
|
||||
[[nodiscard]] inline PRECISION_TYPE x() const { return _x; }
|
||||
|
@ -122,16 +129,16 @@ namespace Raytracing {
|
|||
// takes the x element and places it in the 3rd element of the vector
|
||||
// and then the w element in the last element of the vector
|
||||
// creating the alignment {left.y(), left.z(), left.x(), left.w()} (as seen in the cross algorithm
|
||||
__m256d leftLeftShuffle = _mm256_shuffle_pd(left.avxData, left.avxData, _MM_SHUFFLE(3,0,2,1));
|
||||
__m256d leftLeftShuffle = _mm256_permute4x64_pd(left.avxData, _MM_SHUFFLE(3,0,2,1));
|
||||
// same thing but produces {right.z(), right.x(), right.y(), right.w()}
|
||||
__m256d rightLeftShuffle = _mm256_shuffle_pd(right.avxData, right.avxData, _MM_SHUFFLE(3,1,0,2));
|
||||
// multiply to do the first step of the cross process
|
||||
__m256d multiLeft = _mm256_mul_pd(leftLeftShuffle, rightLeftShuffle);
|
||||
__m256d rightLeftShuffle = _mm256_permute4x64_pd(right.avxData, _MM_SHUFFLE(3,1,0,2));
|
||||
// now we have to do the right side multiplications
|
||||
// {left.z(), left.x(), left.y(), left.w()}
|
||||
__m256d leftRightShuffle = _mm256_shuffle_pd(left.avxData, left.avxData, _MM_SHUFFLE(3,1,0,2));
|
||||
__m256d leftRightShuffle = _mm256_permute4x64_pd(left.avxData, _MM_SHUFFLE(3,1,0,2));
|
||||
// {right.y(), right.z(), right.x(), right.w()}
|
||||
__m256d rightRightShuffle = _mm256_shuffle_pd(right.avxData, right.avxData, _MM_SHUFFLE(3,0,2,1));
|
||||
__m256d rightRightShuffle = _mm256_permute4x64_pd(right.avxData, _MM_SHUFFLE(3,0,2,1));
|
||||
// multiply to do the first step of the cross process
|
||||
__m256d multiLeft = _mm256_mul_pd(leftLeftShuffle, rightLeftShuffle);
|
||||
// multiply the right sides of the subtraction sign
|
||||
__m256d multiRight = _mm256_mul_pd(leftRightShuffle, rightRightShuffle);
|
||||
// then subtract to produce the cross product
|
||||
|
@ -139,6 +146,11 @@ namespace Raytracing {
|
|||
// yes this looks a lot more complicated, but it should be faster!
|
||||
AVXConvert conv {};
|
||||
conv.avxData = subs;
|
||||
/*auto b = Vec4{left.y() * right.z() - left.z() * right.y(),
|
||||
left.z() * right.x() - left.x() * right.z(),
|
||||
left.x() * right.y() - left.y() * right.x()};
|
||||
tlog << b._x << " " << b._y << " " << b._z << "\n";
|
||||
tlog << conv._x << " " << conv._y << " " << conv._z << "\n\n";*/
|
||||
return {conv._x, conv._y, conv._z, conv._w};
|
||||
}
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue