mirror of
https://github.com/MorizzG/ray-tracer.git
synced 2025-12-06 04:22:42 +00:00
init commit
This commit is contained in:
commit
a3e1542250
10 changed files with 360382 additions and 0 deletions
4
.vscode/settings.json
vendored
Normal file
4
.vscode/settings.json
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"mesonbuild.configureOnOpen": false,
|
||||
"C_Cpp.default.compileCommands": "/home/mg/00_Source/ray-tracer/debug/compile_commands.json"
|
||||
}
|
||||
20
meson.build
Normal file
20
meson.build
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
project('ray-tracer', 'cpp', default_options : [
|
||||
'warning_level=3',
|
||||
'werror=true',
|
||||
'cpp_std=c++23',
|
||||
'b_lto=true',
|
||||
'b_lto_mode=thin',
|
||||
])
|
||||
|
||||
add_project_arguments(
|
||||
'-Wconversion',
|
||||
|
||||
language : 'cpp'
|
||||
)
|
||||
|
||||
sources = []
|
||||
subdir('src')
|
||||
|
||||
inc = include_directories('src')
|
||||
|
||||
executable('ray-tracer', sources, include_directories : inc)
|
||||
54
src/colour.h
Normal file
54
src/colour.h
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <iomanip>
|
||||
#include <ios>
|
||||
#include <ostream>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
class Colour {
|
||||
public:
|
||||
static const Colour kBlack;
|
||||
static const Colour kWhite;
|
||||
|
||||
constexpr Colour() = default;
|
||||
constexpr Colour(double r, double g, double b) : r_{r}, g_{g}, b_{b} {
|
||||
assert(0.0 <= r && r <= 1.0);
|
||||
assert(0.0 <= g && g <= 1.0);
|
||||
assert(0.0 <= b && b <= 1.0);
|
||||
}
|
||||
|
||||
constexpr double& r() { return r_; }
|
||||
constexpr double& g() { return g_; }
|
||||
constexpr double& b() { return b_; }
|
||||
|
||||
constexpr double r() const { return r_; }
|
||||
constexpr double g() const { return g_; }
|
||||
constexpr double b() const { return b_; }
|
||||
|
||||
private:
|
||||
double r_;
|
||||
double g_;
|
||||
double b_;
|
||||
};
|
||||
|
||||
constexpr Colour Colour::kBlack{0.0, 0.0, 0.0};
|
||||
|
||||
constexpr Colour operator*(double t, Colour col) {
|
||||
return {t * col.r(), t * col.g(), t * col.b()};
|
||||
}
|
||||
|
||||
constexpr Colour operator+(Colour c1, Colour c2) {
|
||||
return {c1.r() + c2.r(), c1.g() + c2.g(), c1.b() + c2.b()};
|
||||
}
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& out, Colour colour) {
|
||||
out << '#';
|
||||
|
||||
out << std::hex << std::setfill('0') << std::setw(2) << FToU8(colour.r());
|
||||
out << std::hex << std::setfill('0') << std::setw(2) << FToU8(colour.g());
|
||||
out << std::hex << std::setfill('0') << std::setw(2) << FToU8(colour.b());
|
||||
|
||||
return out;
|
||||
}
|
||||
66
src/image.h
Normal file
66
src/image.h
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
|
||||
#include "colour.h"
|
||||
#include "util.h"
|
||||
|
||||
class Image {
|
||||
public:
|
||||
constexpr Image(uint32_t width, uint32_t height)
|
||||
// NOLINTNEXTLINE(modernize-avoid-c-arrays,cppcoreguidelines-avoid-c-arrays)
|
||||
: buf_(std::make_unique_for_overwrite<Colour[]>(static_cast<std::size_t>(width) * height)),
|
||||
width_{width},
|
||||
height_{height} {}
|
||||
|
||||
constexpr uint32_t width() const { return width_; }
|
||||
constexpr uint32_t height() const { return height_; }
|
||||
|
||||
Colour& operator[](uint32_t x, uint32_t y) {
|
||||
assert(x < width_);
|
||||
assert(y < height_);
|
||||
|
||||
assert(buf_.get() != nullptr);
|
||||
|
||||
return buf_[y * width_ + x];
|
||||
}
|
||||
|
||||
Colour operator[](uint32_t x, uint32_t y) const {
|
||||
assert(x < width_);
|
||||
assert(y < height_);
|
||||
|
||||
assert(buf_.get() != nullptr);
|
||||
|
||||
return buf_[y * width_ + x];
|
||||
}
|
||||
|
||||
private:
|
||||
// NOLINTNEXTLINE(modernize-avoid-c-arrays,cppcoreguidelines-avoid-c-arrays)
|
||||
std::unique_ptr<Colour[]> buf_;
|
||||
|
||||
uint32_t width_;
|
||||
uint32_t height_;
|
||||
};
|
||||
|
||||
constexpr std::ostream& operator<<(std::ostream& os, const Image& img) {
|
||||
os << "P3\n" << img.width() << ' ' << img.height() << "\n255\n";
|
||||
|
||||
for (uint32_t j = 0; j < img.height(); j++) {
|
||||
std::clog << "\rWriting line " << j << " of " << img.height() << std::flush;
|
||||
|
||||
for (uint32_t i = 0; i < img.width(); i++) {
|
||||
const Colour col = img[i, j];
|
||||
|
||||
os << FToU8(col.r()) << ' ' << FToU8(col.g()) << ' ' << FToU8(col.b()) << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
std::clog << "\rDone " << std::endl;
|
||||
|
||||
return os;
|
||||
}
|
||||
88
src/main.cc
Normal file
88
src/main.cc
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
#include <cstdint>
|
||||
#include <iostream>
|
||||
|
||||
#include "colour.h"
|
||||
#include "image.h"
|
||||
#include "ray.h"
|
||||
#include "vec3.h"
|
||||
|
||||
bool IntersectsSphere(const Ray& r, const Point3& centre, double radius) {
|
||||
Vec3 oc = r.origin() - centre;
|
||||
|
||||
double a = r.direction().squared();
|
||||
double b = 2.0 * dot(oc, r.direction());
|
||||
double c = oc.squared() - radius * radius;
|
||||
|
||||
auto discr = b * b - 4 * a * c;
|
||||
|
||||
return discr >= 0;
|
||||
}
|
||||
|
||||
Colour RayColour(const Ray& ray) {
|
||||
if (IntersectsSphere(ray, -Vec3::e_z, 0.5)) {
|
||||
return {1.0, 0.0, 0.0};
|
||||
}
|
||||
|
||||
auto unit_dir = ray.direction().normed();
|
||||
|
||||
double a = 0.5 * (unit_dir.y() + 1.0);
|
||||
|
||||
return (1.0 - a) * Colour{1.0, 1.0, 1.0} + a * Colour{0.5, 0.7, 1.0};
|
||||
}
|
||||
|
||||
int main(int /* argc */, char* /* argv */[]) {
|
||||
// image properties
|
||||
|
||||
constexpr double aspect_ratio = 16.0 / 9.0;
|
||||
|
||||
constexpr uint32_t image_width = 800;
|
||||
|
||||
constexpr auto image_height = static_cast<uint32_t>(image_width / aspect_ratio);
|
||||
|
||||
static_assert(image_height >= 1);
|
||||
|
||||
// camera
|
||||
|
||||
constexpr double focal_length = 1.0;
|
||||
|
||||
constexpr double viewport_height = 2.0;
|
||||
constexpr double viewport_width =
|
||||
viewport_height * static_cast<double>(image_width) / image_height;
|
||||
|
||||
Point3 camera_centre{0.0, 0.0, 0.0};
|
||||
|
||||
// viewport
|
||||
|
||||
constexpr Vec3 u_viewport = viewport_width * Vec3::e_x;
|
||||
constexpr Vec3 v_viewport = -viewport_height * Vec3::e_y;
|
||||
|
||||
constexpr Vec3 d_u_pixel = u_viewport / image_width;
|
||||
constexpr Vec3 d_v_pixel = v_viewport / image_height;
|
||||
|
||||
auto viewport_upper_left =
|
||||
camera_centre - focal_length * Vec3::e_z - u_viewport / 2 - v_viewport / 2;
|
||||
|
||||
auto pixel_00 = viewport_upper_left + 0.5 * d_u_pixel + 0.5 * d_v_pixel;
|
||||
|
||||
// render
|
||||
|
||||
Image img{image_width, image_height};
|
||||
|
||||
for (uint32_t j = 0; j < img.height(); j++) {
|
||||
std::clog << "\rWriting line " << j << " of " << img.height() << std::flush;
|
||||
|
||||
for (uint32_t i = 0; i < img.width(); i++) {
|
||||
auto pixel_centre = pixel_00 + i * d_u_pixel + j * d_v_pixel;
|
||||
|
||||
auto ray_direction = pixel_centre - camera_centre;
|
||||
|
||||
Ray r{camera_centre, ray_direction};
|
||||
|
||||
img[i, j] = RayColour(r);
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << img;
|
||||
|
||||
return 0;
|
||||
}
|
||||
3
src/meson.build
Normal file
3
src/meson.build
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
sources += files(
|
||||
'main.cc',
|
||||
)
|
||||
19
src/ray.h
Normal file
19
src/ray.h
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
#pragma once
|
||||
|
||||
#include "vec3.h"
|
||||
|
||||
class Ray {
|
||||
public:
|
||||
Ray() = default;
|
||||
|
||||
Ray(const Point3& origin, const Vec3& direction) : orig_{origin}, dir_{direction} {}
|
||||
|
||||
Point3 origin() const { return orig_; }
|
||||
Vec3 direction() const { return dir_; }
|
||||
|
||||
Point3 At(double t) { return orig_ + t * dir_; }
|
||||
|
||||
private:
|
||||
Point3 orig_;
|
||||
Vec3 dir_;
|
||||
};
|
||||
6
src/util.h
Normal file
6
src/util.h
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
constexpr uint32_t FToU8(double x) {
|
||||
return static_cast<uint16_t>(255.999 * x);
|
||||
}
|
||||
119
src/vec3.h
Normal file
119
src/vec3.h
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <ostream>
|
||||
|
||||
class Vec3 {
|
||||
public:
|
||||
static const Vec3 e_x;
|
||||
static const Vec3 e_y;
|
||||
static const Vec3 e_z;
|
||||
|
||||
constexpr Vec3(double x, double y, double z) : xyz_{x, y, z} {}
|
||||
constexpr Vec3() : Vec3{0, 0, 0} {}
|
||||
|
||||
constexpr double& x() { return xyz_[0]; }
|
||||
constexpr double& y() { return xyz_[1]; }
|
||||
constexpr double& z() { return xyz_[2]; }
|
||||
|
||||
constexpr double x() const { return xyz_[0]; }
|
||||
constexpr double y() const { return xyz_[1]; }
|
||||
constexpr double z() const { return xyz_[2]; }
|
||||
|
||||
constexpr Vec3 operator-() const { return Vec3{-x(), -y(), -z()}; }
|
||||
|
||||
constexpr double& operator[](std::size_t i) {
|
||||
assert(i <= 3);
|
||||
return xyz_[i];
|
||||
}
|
||||
|
||||
constexpr double operator[](std::size_t i) const {
|
||||
assert(i <= 3);
|
||||
return xyz_[i];
|
||||
}
|
||||
|
||||
constexpr Vec3& operator+=(const Vec3& v) {
|
||||
x() += v.x();
|
||||
y() += v.y();
|
||||
z() += v.z();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr Vec3& operator*=(double t) {
|
||||
x() *= t;
|
||||
y() *= t;
|
||||
z() *= t;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr Vec3& operator/=(double t) {
|
||||
*this *= 1 / t;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr double squared() const { return x() * x() + y() * y() + z() * z(); }
|
||||
|
||||
constexpr double norm() const { return std::sqrt(squared()); }
|
||||
|
||||
constexpr Vec3 normed() const {
|
||||
auto r = *this;
|
||||
r /= norm();
|
||||
return r;
|
||||
}
|
||||
|
||||
private:
|
||||
std::array<double, 3> xyz_;
|
||||
};
|
||||
|
||||
constexpr Vec3 Vec3::e_x{1.0, 0.0, 0.0};
|
||||
constexpr Vec3 Vec3::e_y{0.0, 1.0, 0.0};
|
||||
constexpr Vec3 Vec3::e_z{0.0, 0.0, 1.0};
|
||||
|
||||
using Point3 = Vec3;
|
||||
|
||||
constexpr std::ostream& operator<<(std::ostream& out, const Vec3& v) {
|
||||
return out << v.x() << ' ' << v.y() << ' ' << v.z();
|
||||
}
|
||||
|
||||
constexpr Vec3 operator+(const Vec3& u, const Vec3& v) {
|
||||
Vec3 out = u;
|
||||
out += v;
|
||||
return out;
|
||||
}
|
||||
|
||||
constexpr Vec3 operator-(const Vec3& u, const Vec3& v) {
|
||||
return u + (-v);
|
||||
}
|
||||
|
||||
constexpr Vec3 operator*(const Vec3& u, const Vec3& v) {
|
||||
return {u.x() * v.x(), u.y() * v.y(), u.z() * v.z()};
|
||||
}
|
||||
|
||||
constexpr Vec3 operator*(double t, const Vec3& v) {
|
||||
Vec3 out = v;
|
||||
out *= t;
|
||||
return out;
|
||||
}
|
||||
|
||||
constexpr Vec3 operator/(const Vec3& v, double t) {
|
||||
return (1 / t) * v;
|
||||
}
|
||||
|
||||
constexpr double dot(const Vec3& u, const Vec3& v) {
|
||||
Vec3 tmp = u * v;
|
||||
return tmp.x() + tmp.y() + tmp.z();
|
||||
}
|
||||
|
||||
constexpr Vec3 cross(const Vec3& u, const Vec3& v) {
|
||||
return {u.y() * v.z() - u.z() - v.y(), u.z() * v.x() - u.x() * v.z(),
|
||||
u.x() * v.y() - u.y() - v.x()};
|
||||
}
|
||||
|
||||
constexpr Vec3 normed(const Vec3& v) {
|
||||
return v.normed();
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue