diff --git a/code/photonmapping/cpp/.gitignore b/code/photonmapping/cpp/.gitignore new file mode 100644 index 00000000..d6a6547e --- /dev/null +++ b/code/photonmapping/cpp/.gitignore @@ -0,0 +1,3 @@ +photon_mapping +*.ppm +*.jpg \ No newline at end of file diff --git a/code/photonmapping/cpp/Plane.h b/code/photonmapping/cpp/Plane.h new file mode 100644 index 00000000..75b789bb --- /dev/null +++ b/code/photonmapping/cpp/Plane.h @@ -0,0 +1,27 @@ +#ifndef PLANE_H + +class Plane { +public: + Vector3 point; // A point on the plane + Vector3 normal; // Normal to the plane + Vector3 color; + + Plane(const Vector3& p, const Vector3& n, const Vector3& col) + : point(p), normal(n.normalize()), color(col) {} + + bool intersect(const Vector3& ray_origin, const Vector3& ray_direction, double& t, Vector3& hit_point, Vector3& hit_normal) const { + double denom = normal.dot(ray_direction); + if (std::abs(denom) > 1e-6) { + t = (point - ray_origin).dot(normal) / denom; + if (t >= 1e-4) { + hit_point = ray_origin + ray_direction * t; + hit_normal = normal; + return true; + } + } + return false; + } +}; + + +#endif \ No newline at end of file diff --git a/code/photonmapping/cpp/Sphere.h b/code/photonmapping/cpp/Sphere.h new file mode 100644 index 00000000..38c2d223 --- /dev/null +++ b/code/photonmapping/cpp/Sphere.h @@ -0,0 +1,31 @@ +#ifndef SPHERE_H +#define SPHERE_H + +#include "Vector3.h" + +class Sphere { +public: + Vector3 center; + double radius; + Vector3 color; + + Sphere(const Vector3& c, double r, const Vector3& col) + : center(c), radius(r), color(col) {} + + bool intersect(const Vector3& ray_origin, const Vector3& ray_direction, double& t, Vector3& hit_point, Vector3& normal) const { + Vector3 oc = ray_origin - center; + double a = ray_direction.dot(ray_direction); + double b = 2.0 * oc.dot(ray_direction); + double c = oc.dot(oc) - radius * radius; + double discriminant = b * b - 4 * a * c; + if (discriminant > 0) { + t = (-b - std::sqrt(discriminant)) / (2.0 * a); + hit_point = ray_origin + ray_direction * t; + normal = (hit_point - center).normalize(); + return true; + } + return false; + } +}; + +#endif // SPHERE_H \ No newline at end of file diff --git a/code/photonmapping/cpp/Vector3.h b/code/photonmapping/cpp/Vector3.h new file mode 100644 index 00000000..cf19b746 --- /dev/null +++ b/code/photonmapping/cpp/Vector3.h @@ -0,0 +1,23 @@ +#ifndef VECTOR3_H +#define VECTOR3_H + +#include + +class Vector3 { +public: + double x, y, z; + + Vector3(double x_=0, double y_=0, double z_=0): x(x_), y(y_), z(z_) {} + + Vector3 operator + (const Vector3& v) const { return Vector3(x+v.x, y+v.y, z+v.z); } + Vector3 operator - (const Vector3& v) const { return Vector3(x-v.x, y-v.y, z-v.z); } + Vector3 operator * (double scalar) const { return Vector3(x*scalar, y*scalar, z*scalar); } + Vector3 operator / (double scalar) const { return Vector3(x/scalar, y/scalar, z/scalar); } + Vector3 operator - () const { return Vector3(-x, -y, -z); } + Vector3 operator * (const Vector3& v) const { return Vector3(x*v.x, y*v.y, z*v.z); } + double dot(const Vector3& v) const { return x*v.x + y*v.y + z*v.z; } + double norm() const { return std::sqrt(x*x + y*y + z*z); } + Vector3 normalize() const { double n = norm(); return Vector3(x/n, y/n, z/n); } +}; + +#endif // VECTOR3_H \ No newline at end of file diff --git a/code/photonmapping/cpp/compile.sh b/code/photonmapping/cpp/compile.sh index 5f567f3f..19b163a4 100755 --- a/code/photonmapping/cpp/compile.sh +++ b/code/photonmapping/cpp/compile.sh @@ -1 +1 @@ -g++ -std=c++11 -O2 main.cpp -o photon_mapping \ No newline at end of file +g++ -O2 main.cpp -o photon_mapping \ No newline at end of file diff --git a/code/photonmapping/cpp/main.cpp b/code/photonmapping/cpp/main.cpp index 6a6cd629..64c6ad37 100644 --- a/code/photonmapping/cpp/main.cpp +++ b/code/photonmapping/cpp/main.cpp @@ -4,94 +4,24 @@ #include #include #include +#include +#include +#include "Vector3.h" +#include "Sphere.h" +#include "Plane.h" -// Define basic vector operations -class Vector3 { -public: - double x, y, z; - - Vector3(double x_=0, double y_=0, double z_=0): x(x_), y(y_), z(z_) {} - - Vector3 operator + (const Vector3& v) const { return Vector3(x+v.x, y+v.y, z+v.z); } - Vector3 operator - (const Vector3& v) const { return Vector3(x-v.x, y-v.y, z-v.z); } - Vector3 operator * (double scalar) const { return Vector3(x*scalar, y*scalar, z*scalar); } - Vector3 operator / (double scalar) const { return Vector3(x/scalar, y/scalar, z/scalar); } // Added operator/ - Vector3 operator - () const { return Vector3(-x, -y, -z); } // Added unary minus operator - Vector3 operator * (const Vector3& v) const { return Vector3(x*v.x, y*v.y, z*v.z); } // Component-wise multiplication - double dot(const Vector3& v) const { return x*v.x + y*v.y + z*v.z; } - double norm() const { return std::sqrt(x*x + y*y + z*z); } - Vector3 normalize() const { double n = norm(); return Vector3(x/n, y/n, z/n); } -}; - -typedef Vector3 Color; // Alias for RGB color // Define the photon struct Photon { Vector3 position; Vector3 direction; - Color power; + Vector3 power; - Photon(const Vector3& pos, const Vector3& dir, const Color& pow) + Photon(const Vector3& pos, const Vector3& dir, const Vector3& pow) : position(pos), direction(dir), power(pow) {} }; -// Define a simple sphere -class Sphere { -public: - Vector3 center; - double radius; - Color color; - - Sphere(const Vector3& c, double r, const Color& col) - : center(c), radius(r), color(col) {} - - bool intersect(const Vector3& ray_origin, const Vector3& ray_direction, double& t, Vector3& hit_point, Vector3& normal) const { - // Solve quadratic equation for intersection - Vector3 oc = ray_origin - center; - double a = ray_direction.dot(ray_direction); - double b = 2.0 * oc.dot(ray_direction); - double c = oc.dot(oc) - radius * radius; - double discriminant = b*b - 4*a*c; - if (discriminant < 0) { - return false; // No intersection - } else { - double sqrt_discriminant = std::sqrt(discriminant); - double t0 = (-b - sqrt_discriminant) / (2.0 * a); - double t1 = (-b + sqrt_discriminant) / (2.0 * a); - t = (t0 < t1 && t0 > 1e-4) ? t0 : t1; - if (t < 1e-4) { - return false; - } - hit_point = ray_origin + ray_direction * t; - normal = (hit_point - center).normalize(); - return true; - } - } -}; - // Define a simple plane -class Plane { -public: - Vector3 point; // A point on the plane - Vector3 normal; // Normal to the plane - Color color; - - Plane(const Vector3& p, const Vector3& n, const Color& col) - : point(p), normal(n.normalize()), color(col) {} - - bool intersect(const Vector3& ray_origin, const Vector3& ray_direction, double& t, Vector3& hit_point, Vector3& hit_normal) const { - double denom = normal.dot(ray_direction); - if (std::abs(denom) > 1e-6) { - t = (point - ray_origin).dot(normal) / denom; - if (t >= 1e-4) { - hit_point = ray_origin + ray_direction * t; - hit_normal = normal; - return true; - } - } - return false; - } -}; // Random number generators std::mt19937 rng; @@ -118,29 +48,57 @@ Vector3 random_hemisphere_direction(const Vector3& normal) { std::vector photon_map; std::vector objects; // Pointers to objects std::vector object_types; // 0 for Sphere, 1 for Plane +int ray_number = 0; +int photon_number = 0; + // Scene setup -Sphere sphere(Vector3(0, 0, -5), 1.0, Color(1, 0, 0)); // Red sphere -Plane plane(Vector3(0, -1, 0), Vector3(0, 1, 0), Color(0.5, 0.5, 0.5)); // Gray plane - +Sphere sphere(Vector3(0, 0, -5), 1.0, Vector3(1, 0, 0)); // Red sphere +Plane plane(Vector3(0, -1, 0), Vector3(0, 1, 0), Vector3(0.5, 0.5, 0.5)); // Gray plane // Light source Vector3 light_position(-5, 5, -5); -Color light_power(1000.0, 1000.0, 1000.0); // Intense white light, will scale in code +Vector3 light_power(1000.0, 1000.0, 1000.0); // Intense white light, will scale in code -// Parameters -int num_photons = 10000; // Number of photons to emit -int max_depth = 5; // Maximum number of bounces -double gather_radius = 0.5; // Radius for radiance estimation // Functions -void emit_photons(); -void trace_photon(Photon photon, int depth); -Color trace_ray(const Vector3& ray_origin, const Vector3& ray_direction); -Color compute_direct_light(const Vector3& point, const Vector3& normal); -Color estimate_radiance(const Vector3& point, const Vector3& normal); +void emit_photons(const int num_photons, const int max_depth); +void trace_photon(Photon photon, int depth, const int max_depth); +Vector3 trace_ray(const Vector3& ray_origin, const Vector3& ray_direction, const double gather_radius); +Vector3 compute_direct_light(const Vector3& point, const Vector3& normal); +Vector3 estimate_radiance(const Vector3& point, const Vector3& normal, const double gather_radius); // Main execution -int main() { +int main(int argc, char* argv[]) { + auto start = std::chrono::high_resolution_clock::now(); // Start timer + int num_photons = 10000; + int max_depth = 5; + double gather_radius = 0.5; + + if (argc > 1) { + num_photons = std::atoi(argv[1]); + if (num_photons <= 0) { + std::cerr << "Invalid number of photons. Using default: 10000" << std::endl; + num_photons = 10000; + } + } + if (argc > 2) { + max_depth = std::atoi(argv[2]); + if (max_depth < 0) { + std::cerr << "Invalid max_depth. Using default: 5" << std::endl; + max_depth = 5; + } + } + if (argc > 3) { + gather_radius = std::atof(argv[3]); + if (gather_radius <= 0) { + std::cerr << "Invalid gather_radius. Using default: 0.5" << std::endl; + gather_radius = 0.5; + } + } + std::cout << "Number of photons: " << num_photons << std::endl; + std::cout << "Max depth: " << max_depth << std::endl; + std::cout << "Gather radius: " << gather_radius << std::endl; + // Seed random number generator rng.seed(std::random_device()()); @@ -149,26 +107,26 @@ int main() { objects.push_back(&sphere); object_types.push_back(0); objects.push_back(&plane); object_types.push_back(1); - emit_photons(); - std::cout << "Photons stored in photon map: " << photon_map.size() << std::endl; + emit_photons(num_photons, max_depth); + const int photons_in_map = photon_map.size(); std::cout << "Rendering image..." << std::endl; int width = 200; int height = 100; - std::vector image(width * height); + std::vector image(width * height); double aspect_ratio = double(width) / height; double fov = M_PI / 3.0; // 60 degrees field of view for (int y = 0; y < height; ++y) { - for (int x = 0; x < width; ++x) { + for (int x = 0; x < width; ++x) { // Convert pixel coordinate to camera ray double px = (2 * (x + 0.5) / double(width) - 1) * tan(fov / 2.0) * aspect_ratio; double py = (1 - 2 * (y + 0.5) / double(height)) * tan(fov / 2.0); Vector3 ray_origin(0, 0, 0); Vector3 ray_direction = Vector3(px, py, -1).normalize(); - Color color = trace_ray(ray_origin, ray_direction); - image[y * width + x] = Color( + Vector3 color = trace_ray(ray_origin, ray_direction, gather_radius); + image[y * width + x] = Vector3( std::min(color.x, 1.0), std::min(color.y, 1.0), std::min(color.z, 1.0) @@ -191,21 +149,29 @@ int main() { ofs << r << " " << g << " " << b << "\n"; } ofs.close(); + std::cout << "Image generated" << std::endl; + std::cout << "width: " << width << std::endl; + std::cout << "height: " << width << std::endl; + std::cout << "photons in map: " << photons_in_map << std::endl; + std::cout << "count of photons: " << photon_number << std::endl; + std::cout << "count of rays: " << ray_number << std::endl; std::cout << "Image saved to render.ppm" << std::endl; + std::chrono::duration elapsed = std::chrono::high_resolution_clock::now() - start; + std::cout << "Execution time: " << elapsed.count() << " seconds" << std::endl; return 0; } -void emit_photons() { - Color per_photon_power = light_power / num_photons; // Scale light power per photon +void emit_photons(const int num_photons, const int max_depth) { + Vector3 per_photon_power = light_power / num_photons; // Scale light power per photon for (int i = 0; i < num_photons; ++i) { - // Emit photons in random directions from the light source Vector3 direction = random_unit_vector(); Photon photon(light_position, direction, per_photon_power); - trace_photon(photon, 0); + trace_photon(photon, 0, max_depth); } } -void trace_photon(Photon photon, int depth) { +void trace_photon(Photon photon, int depth, const int max_depth) { + photon_number++; if (depth > max_depth) { return; } @@ -241,16 +207,17 @@ void trace_photon(Photon photon, int depth) { photon.direction = new_direction; // Absorb some power photon.power = photon.power * 0.8; // Simple absorption - trace_photon(photon, depth + 1); + trace_photon(photon, depth + 1, max_depth); } } -Color trace_ray(const Vector3& ray_origin, const Vector3& ray_direction) { +Vector3 trace_ray(const Vector3& ray_origin, const Vector3& ray_direction, const double gather_radius) { + ray_number++; double closest_t = std::numeric_limits::infinity(); void* hit_object = nullptr; int hit_type = -1; Vector3 hit_point, normal; - Color obj_color; + Vector3 obj_color; // Find the nearest intersection for (size_t i = 0; i < objects.size(); ++i) { double t; @@ -274,15 +241,15 @@ Color trace_ray(const Vector3& ray_origin, const Vector3& ray_direction) { } } if (hit_object) { - Color direct_light = compute_direct_light(hit_point, normal); - Color indirect_light = estimate_radiance(hit_point, normal); + Vector3 direct_light = compute_direct_light(hit_point, normal); + Vector3 indirect_light = estimate_radiance(hit_point, normal, gather_radius); return obj_color * (direct_light + indirect_light); // Component-wise multiplication } else { - return Color(0, 0, 0); // Background color + return Vector3(0, 0, 0); // Background color } } -Color compute_direct_light(const Vector3& point, const Vector3& normal) { +Vector3 compute_direct_light(const Vector3& point, const Vector3& normal) { // Simple Lambertian reflection from light source Vector3 direction_to_light = (light_position - point).normalize(); // Shadow ray @@ -306,7 +273,7 @@ Color compute_direct_light(const Vector3& point, const Vector3& normal) { } } if (in_shadow) { - return Color(0, 0, 0); + return Vector3(0, 0, 0); } else { double intensity = std::max(0.0, normal.dot(direction_to_light)); double distance2 = (light_position - point).dot(light_position - point); @@ -314,9 +281,9 @@ Color compute_direct_light(const Vector3& point, const Vector3& normal) { } } -Color estimate_radiance(const Vector3& point, const Vector3& normal) { +Vector3 estimate_radiance(const Vector3& point, const Vector3& normal, const double gather_radius) { // Gather photons within the gather_radius - Color accumulated_power(0.0, 0.0, 0.0); + Vector3 accumulated_power(0.0, 0.0, 0.0); for (const auto& photon : photon_map) { double distance = (photon.position - point).norm(); if (distance < gather_radius) {