#include "Raychel/Core/Deserialize.h" #include "Raychel/Core/Raymarch.h" #include "Raychel/Core/SDFBooleans.h" #include "Raychel/Core/SDFModifiers.h" #include "Raychel/Core/SDFPrimitives.h" #include "Raychel/Core/SDFTransforms.h" #include "Raychel/Core/Scene.h" #include "Raychel/Core/Types.h" #include "Raychel/Render/RenderUtils.h" #include "Raychel/Render/Renderer.h" #include "RaychelMath/vector.h" #include #include #include #include #include #include #include struct FlatMaterial { Raychel::color surface_color{}; }; struct ReflectiveMaterial { Raychel::color reflectivity{}; }; struct DiffuseMaterial { Raychel::color surface_color{}; }; struct TransparentMaterial { Raychel::color transparency{}; double ior{1.0}; double ior_variation{.1}; }; struct DebugMaterial {}; template <> struct Raychel::is_transparent_material : std::true_type {}; Raychel::color get_surface_color(const FlatMaterial& material, const Raychel::ShadingData& /*unused*/) noexcept { return material.surface_color; } Raychel::color get_surface_color(const ReflectiveMaterial& material, const Raychel::ShadingData& data) noexcept { return Raychel::get_shaded_color(Raychel::RenderData{ .origin = data.position, .direction = reflect(data.incoming_direction, data.normal), .state = data.state, .recursion_depth = data.recursion_depth}) * material.reflectivity; } Raychel::color get_surface_color(const DiffuseMaterial& material, const Raychel::ShadingData& data) noexcept { return Raychel::get_diffuse_lighting(data) * material.surface_color; } Raychel::color get_surface_color(const TransparentMaterial& material, const Raychel::ShadingData& data) noexcept { return Raychel::get_refraction(Raychel::RefractionData{ .surface_point = data.position, .incoming_direction = data.incoming_direction, .normal = data.normal, .material_ior = material.ior, .ior_variation = material.ior_variation, .state = data.state, .recursion_depth = data.recursion_depth}) * material.transparency; } Raychel::color get_surface_color(const DebugMaterial& /*unused*/, const Raychel::ShadingData& data) noexcept { const auto [x, y, z] = data.normal; return Raychel::color{std::abs(x), std::abs(y), std::abs(z)}; } double get_material_ior(const TransparentMaterial& material) noexcept { return material.ior; } [[maybe_unused]] static void write_framebuffer(const std::string& file_name, const Raychel::Framebuffer& framebuffer) noexcept { //Don't bother writing an empty framebuffer if (framebuffer.pixel_data.empty()) { return; } const auto label = Logger::startTimer("Write time"); std::ofstream output_image{file_name}; if (!output_image) { Logger::error("Unable to open output file '", file_name, "'\n"); return; } output_image << "P6\n" << framebuffer.size.x() << ' ' << framebuffer.size.y() << '\n' << "255\n"; for (const auto& pixel : framebuffer.pixel_data) { const auto pixel_rgb = convert_color(pixel); if (!output_image.write(reinterpret_cast(&pixel_rgb), sizeof(pixel_rgb)).good()) { Logger::error("Error while writing output file!\n"); break; } } Logger::logDuration(label); } [[maybe_unused]] static Raychel::Framebuffer fat_pixels_to_regular(const Raychel::FatFramebuffer& input) noexcept { std::vector output{}; output.reserve(input.size.x() * input.size.y()); for (const auto& pixel : input) { output.emplace_back(pixel.noisy_color); } return {input.size, std::move(output)}; } [[maybe_unused]] static void build_cornell_box(Raychel::Scene& scene) { using namespace Raychel; constexpr auto room_size = 1.0; constexpr auto box_size = room_size * 1.1; constexpr auto slim = 0.1; //Floor scene.add_object(Translate{Box{vec3{box_size, slim, box_size}}, vec3{0, -room_size, 0}}, DiffuseMaterial{color{1, 1, 1}}); //Ceiling scene.add_object(Translate{Box{vec3{box_size, slim, box_size}}, vec3{0, room_size, 0}}, FlatMaterial{color{1, 1, .9} * 2.5}); //Left wall scene.add_object( Translate{Box{vec3{slim, box_size, box_size}}, vec3{-room_size * 1.01, 0, 0}}, DiffuseMaterial{color{1, 0, 0}}); //Right wall scene.add_object(Translate{Box{vec3{slim, box_size, box_size}}, vec3{room_size, 0, 0}}, DiffuseMaterial{color{0, 1, 0}}); //Back wall scene.add_object(Translate{Box{vec3{box_size, box_size, slim}}, vec3{0, 0, room_size}}, DiffuseMaterial{color{1, 1, 1}}); //Back sphere scene.add_object( Translate{Sphere{0.5}, vec3{-room_size + slim + 0.5, -room_size + 1.1 * slim + 0.5, room_size - 2 * slim - 0.5}}, ReflectiveMaterial{color_from_hex(0xFF5733) * 0.95}); //Front box scene.add_object( Translate{ Rotate{Sphere{.25}, rotate_around(vec3{0, 1, 0}, 60 * deg_to_rad)}, vec3{room_size - slim - .5625, -room_size + slim + .25, -room_size + .375}}, TransparentMaterial{.transparency = color_from_hex(0xa8ccd7), .ior = 1.5}); } bool do_serialize(std::ostream& os, const FlatMaterial& material) noexcept { os << material.surface_color << '\n'; return os.good(); } bool do_serialize(std::ostream& os, const ReflectiveMaterial& material) noexcept { os << material.reflectivity << '\n'; return os.good(); } bool do_serialize(std::ostream& os, const DiffuseMaterial& material) noexcept { os << material.surface_color << '\n'; return os.good(); } std::optional do_deserialize(std::istream& is, Raychel::DeserializationTag) noexcept { Raychel::color c{}; if (!(is >> c)) return std::nullopt; return DiffuseMaterial{c}; } std::optional do_deserialize(std::istream& is, Raychel::DeserializationTag) noexcept { Raychel::color c{}; if (!(is >> c)) return std::nullopt; return FlatMaterial{c}; } std::optional do_deserialize(std::istream& is, Raychel::DeserializationTag) noexcept { Raychel::color c{}; if (!(is >> c)) return std::nullopt; return ReflectiveMaterial{c}; } template T_> constexpr Raychel::basic_color lerp(const Raychel::basic_color& a, const Raychel::basic_color& b, T_ x) { return (b * x) + (a * (1.0 - x)); } int main() { Logger::setMinimumLogLevel(Logger::LogLevel::debug); using namespace Raychel; #if 0 Scene scene; build_cornell_box(scene); std::ofstream out_stream{"cornell_box.txt"}; serialize_scene(scene, out_stream); #endif #if 0 std::ifstream in_file{"cornell_box.txt"}; auto scene = deserialize_scene( in_file, object_deserializers, Rotate<>, Sphere, Box>(), material_deserializers()); #endif #if 1 Scene scene; //scene.add_object(Translate{Sphere{.75}, vec3{0, -.25, 0}}, TransparentMaterial{color{1, .65, .45}, 1.5, .05}); //scene.add_object(Translate{Plane{vec3{0, 1, 0}}, vec3{0, -1.01, 0}}, DiffuseMaterial{color{1}}); //scene.add_object(Translate{Box{vec3{.1, 2, 2}}, vec3{-2, 0, 0}}, FlatMaterial{color{10}}); scene.add_object(Translate{Sphere{.5}, vec3{-3.5, 2.5, -1.5}}, FlatMaterial{(color_from_hex(0x2FE3E0)) * 10}); scene.add_object(Translate{Sphere{.5}, vec3{2.5, 2.5, 1.5}}, FlatMaterial{color_from_hex(0xD01C1F) * 10}); scene.add_object(Translate{Sphere{.05}, vec3{0, .8, 0}}, FlatMaterial{color{1, .75, .5625} * 5}); scene.add_object( Difference{Translate{Sphere{.5}, vec3{0, .85, 0}}, Rounded{Box{vec3{1, 1, 1}}, 0.1}}, DiffuseMaterial{color{1, 1, 1}}); scene.set_background_function([](const RenderData& data) { /* if (data.direction.z() < -0.99) { return color_from_hex(0xfdd835) * 15; } return lerp( color_from_hex(0xFFC922), color_from_hex(0x87CEEB), std::pow(std::abs(data.direction.y()) + 0.2, 1.0 / 3.0)) * 0.75;*/ (void)data; return color{}; }); #endif for (const auto& obj : scene.objects()) { obj.unsafe_impl()->debug_log(); } const auto rendered_image = render_scene( scene, Camera{ .transform = {.offset = vec3{0, 2.5, -2.5}, .rotation = rotate_around(vec3{1, 0, 0}, quarter_pi)}, .zoom = 1.0}, RenderOptions{ .output_size = Size2D{1920, 1080} / 2U, .max_ray_steps = 4096, .max_recursion_depth = 100, .samples_per_pixel = 1U << 10U, }); write_framebuffer("out.ppm", fat_pixels_to_regular(rendered_image)); return 0; }