/// Includes #include <array> #include <cstdlib> #include <fstream> #include <iostream> #include <limits> #include <vector> #define GLM_FORCE_SWIZZLE #define GLM_FORCE_INTRINSICS // For `GLM_SWIZZLE_OPERATOR`. #include <glm/glm.hpp> // #include <glm/gtx/io.hpp> // For `operator<<(std::ostream &)`. /// Namespaces using namespace glm; /// Constants auto const infinity = std::numeric_limits<float>::infinity(); /// Types //// Ray struct Ray { vec3 origin; vec3 direction; }; //// Trace struct Trace { float distance; explicit operator bool() const { return distance < infinity; } }; //// Sphere struct Sphere { vec3 center; float radius; Trace trace(Ray const & ray) const { auto const offs = center - ray.origin; auto const proj = dot(offs, ray.direction); if (proj < 0.0F) return {infinity}; // Past. auto const perp2 = dot(offs, offs) - proj * proj; auto const over2 = radius * radius - perp2; if (over2 < 0.0F) return {infinity}; // Miss. auto const dist = proj - sqrt(over2); if (dist < 0.0F) return {infinity}; // Inside. return {dist}; // Hit. } }; //// Shapes using Shapes = std::vector<Sphere>; //// Scene struct Scene { Shapes shapes; Trace trace(Ray const & ray) const { auto nearest = Trace{infinity}; for (auto const & shape : shapes) { auto const trace = shape.trace(ray); if (trace.distance < nearest.distance) nearest = trace; } return nearest; } }; //// Camera struct Camera { uvec2 size; Ray ray(vec4 const & frag_coord) const { auto const point = vec3( (2.0F * vec2(frag_coord.xy) - vec2(size)) / float(size.y), -1.0F ); auto const origin = vec3(0.0F); auto const direction = normalize(point); return Ray{origin, direction}; } }; //// Framebuffer struct Framebuffer { uvec2 size; std::vector<u8vec4> color; explicit Framebuffer(uvec2 const & size_) : size{size_}, color((std::size_t)(size_.x * size_.y)) {} template<typename Shader> void render(Shader const & shader) { auto const begin = uvec2(0); auto const end = size; for (auto y = begin.y; y < end.y; ++y) for (auto x = begin.x; x < end.x; ++x) { auto const index = size.x * y + x; auto const frag_coord = vec4(vec2(x, y) + 0.5F, 0.0F, 1.0F); auto const frag_color = vec4(shader(frag_coord).bgra); color[index] = clamp(frag_color, 0.0F, 1.0F) * 255.0F + 0.5F; } } void write_tga(char const * path) const { auto header = std::array<uint8_t, 18>{ 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, (uint8_t)(size.x >> 0U), (uint8_t)(size.x >> 8U), (uint8_t)(size.y >> 0U), (uint8_t)(size.y >> 8U), 32, 0, }; auto ostream = std::ofstream(path, std::ios::binary); write(ostream, header); write(ostream, color); } template<typename Collection> void write(std::ostream & ostream, Collection const & collection) const { ostream.write( (char const *) collection.data(), (std::streamsize)(sizeof(*collection.data()) * collection.size()) ); } }; /// Main int main(int argc, char const * argv[]) { //// Arguments if (argc != 2) { std::cerr << "Usage: raytracer <path>" << std::endl; return EXIT_FAILURE; } auto const * path = argv[1]; // NOLINT //// Configure auto size = uvec2(640, 480); auto framebuffer = Framebuffer(size); auto camera = Camera{size}; auto scene = Scene{ { // shapes Sphere{{ 6.0F, -4.0F, -24.0F}, 12.0F}, Sphere{{ 2.0F, 6.0F, -16.0F}, 8.0F}, Sphere{{ -4.0F, 0.0F, -10.0F}, 4.0F}, Sphere{{ 0.0F, -4.0F, -6.0F}, 1.0F}, Sphere{{ -1.1F, 0.8F, -1.1F}, 0.2F}, Sphere{{-110.0F, -80.0F, -110.0F}, 20.0F}, }, }; //// Render framebuffer.render([&](vec4 const & frag_coord) { auto const ray = camera.ray(frag_coord); auto const trace = scene.trace(ray); auto const rgb = vec3(1.0F / (1.0F + trace.distance)); auto const alpha = (bool)trace; return vec4(rgb, alpha); }); //// Finalize framebuffer.write_tga(path); return EXIT_SUCCESS; }