|
| 1 | +{ |
| 2 | + "cells": [ |
| 3 | + { |
| 4 | + "cell_type": "code", |
| 5 | + "execution_count": 1, |
| 6 | + "id": "72cd5c0c-3a52-43a5-a714-0baee1f7464c", |
| 7 | + "metadata": { |
| 8 | + "trusted": true, |
| 9 | + "vscode": { |
| 10 | + "languageId": "c++" |
| 11 | + } |
| 12 | + }, |
| 13 | + "outputs": [], |
| 14 | + "source": [ |
| 15 | + "// Adapted from smallpt, a Path Tracer by Kevin Beason, 2008\n", |
| 16 | + "#include <math.h>\n", |
| 17 | + "#include <stdlib.h>\n", |
| 18 | + "#include <vector>\n", |
| 19 | + "\n", |
| 20 | + "struct Vec {\n", |
| 21 | + " double x, y, z; // position, also color (r,g,b)\n", |
| 22 | + " Vec(double x_ = 0, double y_ = 0, double z_ = 0) {\n", |
| 23 | + " x = x_;\n", |
| 24 | + " y = y_;\n", |
| 25 | + " z = z_;\n", |
| 26 | + " }\n", |
| 27 | + " Vec operator+(const Vec &b) const { return Vec(x + b.x, y + b.y, z + b.z); }\n", |
| 28 | + " Vec operator-(const Vec &b) const { return Vec(x - b.x, y - b.y, z - b.z); }\n", |
| 29 | + " Vec operator*(double b) const { return Vec(x * b, y * b, z * b); }\n", |
| 30 | + " Vec mult(const Vec &b) const { return Vec(x * b.x, y * b.y, z * b.z); }\n", |
| 31 | + " Vec &norm() { return *this = *this * (1 / sqrt(x * x + y * y + z * z)); }\n", |
| 32 | + " double dot(const Vec &b) const { return x * b.x + y * b.y + z * b.z; }\n", |
| 33 | + " Vec operator%(Vec &b) { // cross\n", |
| 34 | + " return Vec(y * b.z - z * b.y, z * b.x - x * b.z, x * b.y - y * b.x);\n", |
| 35 | + " }\n", |
| 36 | + "};\n", |
| 37 | + "struct Ray {\n", |
| 38 | + " Vec o, d;\n", |
| 39 | + " Ray(Vec o_, Vec d_) : o(o_), d(d_) {}\n", |
| 40 | + "};\n", |
| 41 | + "enum Refl_t { DIFF, SPEC, REFR }; // material types, used in radiance()\n", |
| 42 | + "struct Sphere {\n", |
| 43 | + " double rad; // radius\n", |
| 44 | + " Vec p, e, c; // position, emission, color\n", |
| 45 | + " Refl_t refl; // reflection type (DIFFuse, SPECular, REFRactive)\n", |
| 46 | + " Sphere(double rad_, Vec p_, Vec e_, Vec c_, Refl_t refl_)\n", |
| 47 | + " : rad(rad_), p(p_), e(e_), c(c_), refl(refl_) {}\n", |
| 48 | + " double intersect(const Ray &r) const { // returns distance, 0 if nohit\n", |
| 49 | + " Vec op = p - r.o; // Solve t^2*d.d + 2*t*(o-p).d + (o-p).(o-p)-R^2 = 0\n", |
| 50 | + " double t, eps = 1e-4, b = op.dot(r.d), det = b * b - op.dot(op) + rad * rad;\n", |
| 51 | + " if (det < 0)\n", |
| 52 | + " return 0;\n", |
| 53 | + " else\n", |
| 54 | + " det = sqrt(det);\n", |
| 55 | + " return (t = b - det) > eps ? t : ((t = b + det) > eps ? t : 0);\n", |
| 56 | + " }\n", |
| 57 | + "};" |
| 58 | + ] |
| 59 | + }, |
| 60 | + { |
| 61 | + "cell_type": "code", |
| 62 | + "execution_count": 2, |
| 63 | + "id": "acc1ec30-5f0a-446f-a066-3dc3369ed39e", |
| 64 | + "metadata": { |
| 65 | + "trusted": true, |
| 66 | + "vscode": { |
| 67 | + "languageId": "c++" |
| 68 | + } |
| 69 | + }, |
| 70 | + "outputs": [], |
| 71 | + "source": [ |
| 72 | + "std::vector<Sphere> spheres = {\n", |
| 73 | + " // Scene: radius, position, emission, color, material\n", |
| 74 | + " Sphere(1e5, Vec(1e5 + 1, 40.8, 81.6), Vec(), Vec(.75, .25, .25),\n", |
| 75 | + " DIFF), // Left\n", |
| 76 | + " Sphere(1e5, Vec(-1e5 + 99, 40.8, 81.6), Vec(), Vec(.25, .25, .75),\n", |
| 77 | + " DIFF), // Rght\n", |
| 78 | + " Sphere(1e5, Vec(50, 40.8, 1e5), Vec(), Vec(.75, .75, .75), DIFF), // Back\n", |
| 79 | + " Sphere(1e5, Vec(50, 40.8, -1e5 + 170), Vec(), Vec(), DIFF), // Frnt\n", |
| 80 | + " Sphere(1e5, Vec(50, 1e5, 81.6), Vec(), Vec(.75, .75, .75), DIFF), // Botm\n", |
| 81 | + " Sphere(1e5, Vec(50, -1e5 + 81.6, 81.6), Vec(), Vec(.75, .75, .75),\n", |
| 82 | + " DIFF), // Top\n", |
| 83 | + " Sphere(16.5, Vec(27, 16.5, 47), Vec(), Vec(1, 1, 1) * .999, SPEC), // Mirr\n", |
| 84 | + " Sphere(16.5, Vec(73, 16.5, 78), Vec(), Vec(1, 1, 1) * .999, REFR), // Glas\n", |
| 85 | + " Sphere(600, Vec(50, 681.6 - .27, 81.6), Vec(12, 12, 12), Vec(),\n", |
| 86 | + " DIFF) // Lite\n", |
| 87 | + "};\n", |
| 88 | + "\n", |
| 89 | + "inline double clamp(double x) { return x < 0 ? 0 : x > 1 ? 1 : x; }\n", |
| 90 | + "inline int toInt(double x) { return int(pow(clamp(x), 1 / 2.2) * 255 + .5); }\n", |
| 91 | + "inline bool intersect(const Ray &r, double &t, int &id) {\n", |
| 92 | + " double n = spheres.size(), d, inf = t = 1e20;\n", |
| 93 | + " for (int i = int(n); i--;)\n", |
| 94 | + " if ((d = spheres[i].intersect(r)) && d < t) {\n", |
| 95 | + " t = d;\n", |
| 96 | + " id = i;\n", |
| 97 | + " }\n", |
| 98 | + " return t < inf;\n", |
| 99 | + "}\n" |
| 100 | + ] |
| 101 | + }, |
| 102 | + { |
| 103 | + "cell_type": "code", |
| 104 | + "execution_count": 3, |
| 105 | + "id": "cf0fd047-c5da-4606-aeae-4358d2064f10", |
| 106 | + "metadata": { |
| 107 | + "trusted": true, |
| 108 | + "vscode": { |
| 109 | + "languageId": "c++" |
| 110 | + } |
| 111 | + }, |
| 112 | + "outputs": [], |
| 113 | + "source": [ |
| 114 | + "Vec radiance(const Ray &r, int depth, unsigned short *Xi) {\n", |
| 115 | + " double t; // distance to intersection\n", |
| 116 | + " int id = 0; // id of intersected object\n", |
| 117 | + " if (!intersect(r, t, id))\n", |
| 118 | + " return Vec(); // if miss, return black\n", |
| 119 | + " const Sphere &obj = spheres[id]; // the hit object\n", |
| 120 | + " Vec x = r.o + r.d * t, n = (x - obj.p).norm(),\n", |
| 121 | + " nl = n.dot(r.d) < 0 ? n : n * -1, f = obj.c;\n", |
| 122 | + " double p = f.x > f.y && f.x > f.z ? f.x : f.y > f.z ? f.y : f.z; // max refl\n", |
| 123 | + " if (++depth > 5) {\n", |
| 124 | + " if (erand48(Xi) < p) {\n", |
| 125 | + " f = f * (1 / p);\n", |
| 126 | + " } else {\n", |
| 127 | + " return obj.e; // R.R.\n", |
| 128 | + " }\n", |
| 129 | + " }\n", |
| 130 | + " if (obj.refl == DIFF) { // Ideal DIFFUSE reflection\n", |
| 131 | + " double r1 = 2 * M_PI * erand48(Xi), r2 = erand48(Xi), r2s = sqrt(r2);\n", |
| 132 | + " Vec w = nl, u = ((fabs(w.x) > .1 ? Vec(0, 1) : Vec(1)) % w).norm(),\n", |
| 133 | + " v = w % u;\n", |
| 134 | + " Vec d = (u * cos(r1) * r2s + v * sin(r1) * r2s + w * sqrt(1 - r2)).norm();\n", |
| 135 | + " return obj.e + f.mult(radiance(Ray(x, d), depth, Xi));\n", |
| 136 | + " } else if (obj.refl == SPEC) // Ideal SPECULAR reflection\n", |
| 137 | + " return obj.e +\n", |
| 138 | + " f.mult(radiance(Ray(x, r.d - n * 2 * n.dot(r.d)), depth, Xi));\n", |
| 139 | + " Ray reflRay(x, r.d - n * 2 * n.dot(r.d)); // Ideal dielectric REFRACTION\n", |
| 140 | + " bool into = n.dot(nl) > 0; // Ray from outside going in?\n", |
| 141 | + " double nc = 1, nt = 1.5, nnt = into ? nc / nt : nt / nc, ddn = r.d.dot(nl),\n", |
| 142 | + " cos2t;\n", |
| 143 | + " if ((cos2t = 1 - nnt * nnt * (1 - ddn * ddn)) <\n", |
| 144 | + " 0) // Total internal reflection\n", |
| 145 | + " return obj.e + f.mult(radiance(reflRay, depth, Xi));\n", |
| 146 | + " Vec tdir =\n", |
| 147 | + " (r.d * nnt - n * ((into ? 1 : -1) * (ddn * nnt + sqrt(cos2t)))).norm();\n", |
| 148 | + " double a = nt - nc, b = nt + nc, R0 = a * a / (b * b),\n", |
| 149 | + " c = 1 - (into ? -ddn : tdir.dot(n));\n", |
| 150 | + " double Re = R0 + (1 - R0) * c * c * c * c * c, Tr = 1 - Re, P = .25 + .5 * Re,\n", |
| 151 | + " RP = Re / P, TP = Tr / (1 - P);\n", |
| 152 | + " return obj.e +\n", |
| 153 | + " f.mult(depth > 2\n", |
| 154 | + " ? (erand48(Xi) < P ? // Russian roulette\n", |
| 155 | + " radiance(reflRay, depth, Xi) * RP\n", |
| 156 | + " : radiance(Ray(x, tdir), depth, Xi) * TP)\n", |
| 157 | + " : radiance(reflRay, depth, Xi) * Re +\n", |
| 158 | + " radiance(Ray(x, tdir), depth, Xi) * Tr);\n", |
| 159 | + "}" |
| 160 | + ] |
| 161 | + }, |
| 162 | + { |
| 163 | + "cell_type": "code", |
| 164 | + "execution_count": 4, |
| 165 | + "id": "70a01b15-3b76-4008-b674-0cc05b0342dd", |
| 166 | + "metadata": { |
| 167 | + "trusted": true, |
| 168 | + "vscode": { |
| 169 | + "languageId": "c++" |
| 170 | + } |
| 171 | + }, |
| 172 | + "outputs": [], |
| 173 | + "source": [ |
| 174 | + "#include <SDL2/SDL.h>\n", |
| 175 | + "#include <string>\n", |
| 176 | + "#include <fstream>\n", |
| 177 | + "#include <sstream>\n", |
| 178 | + "#include <iostream>\n", |
| 179 | + "#include \"nlohmann/json.hpp\"\n", |
| 180 | + "#include \"xeus/xbase64.hpp\"\n", |
| 181 | + "#include \"xcpp/xdisplay.hpp\"\n", |
| 182 | + "\n", |
| 183 | + "namespace nl = nlohmann;\n", |
| 184 | + "\n", |
| 185 | + "namespace im\n", |
| 186 | + "{\n", |
| 187 | + " struct image\n", |
| 188 | + " { \n", |
| 189 | + " inline image(const std::string& filename)\n", |
| 190 | + " {\n", |
| 191 | + " std::ifstream fin(filename, std::ios::binary); \n", |
| 192 | + " m_buffer << fin.rdbuf();\n", |
| 193 | + " }\n", |
| 194 | + " \n", |
| 195 | + " std::stringstream m_buffer;\n", |
| 196 | + " };\n", |
| 197 | + " \n", |
| 198 | + " nl::json mime_bundle_repr(const image& i)\n", |
| 199 | + " {\n", |
| 200 | + " auto bundle = nl::json::object();\n", |
| 201 | + " bundle[\"image/bmp\"] = xeus::base64encode(i.m_buffer.str());\n", |
| 202 | + " return bundle;\n", |
| 203 | + " }\n", |
| 204 | + "}\n", |
| 205 | + "\n", |
| 206 | + "// Function to save SDL surface as BMP persistently\n", |
| 207 | + "void save_bmp_to_filesystem(SDL_Surface* surface, const std::string& filename)\n", |
| 208 | + "{\n", |
| 209 | + " if (surface)\n", |
| 210 | + " {\n", |
| 211 | + " if (SDL_SaveBMP(surface, filename.c_str()) == 0)\n", |
| 212 | + " {\n", |
| 213 | + " std::cout << \"[DEBUG] Surface saved to \" << filename << std::endl;\n", |
| 214 | + " }\n", |
| 215 | + " else\n", |
| 216 | + " {\n", |
| 217 | + " std::cerr << \"[ERROR] Failed to save BMP to \" << filename << \": \" << SDL_GetError() << std::endl;\n", |
| 218 | + " }\n", |
| 219 | + " }\n", |
| 220 | + " else\n", |
| 221 | + " {\n", |
| 222 | + " std::cerr << \"[ERROR] Surface is null, cannot save BMP.\" << std::endl;\n", |
| 223 | + " }\n", |
| 224 | + "}\n", |
| 225 | + "\n", |
| 226 | + "// Function to encode an SDL surface and render it in Jupyter\n", |
| 227 | + "void render_sdl_surface_to_jupyter(SDL_Surface* surface, const std::string& filename)\n", |
| 228 | + "{\n", |
| 229 | + "\n", |
| 230 | + " if (!surface)\n", |
| 231 | + " {\n", |
| 232 | + " std::cerr << \"[ERROR] Surface is null\" << std::endl;\n", |
| 233 | + " return;\n", |
| 234 | + " }\n", |
| 235 | + "\n", |
| 236 | + " std::cout << \"[DEBUG] Surface created successfully\" << std::endl;\n", |
| 237 | + "\n", |
| 238 | + " im::image output(filename);\n", |
| 239 | + " xcpp::display(output);\n", |
| 240 | + " \n", |
| 241 | + " // Cleanup\n", |
| 242 | + " SDL_FreeSurface(surface);\n", |
| 243 | + " std::cout << \"[DEBUG] Surface freed\" << std::endl;\n", |
| 244 | + " remove(filename.c_str());\n", |
| 245 | + "}" |
| 246 | + ] |
| 247 | + }, |
| 248 | + { |
| 249 | + "cell_type": "code", |
| 250 | + "execution_count": null, |
| 251 | + "id": "2fd6e42b-6e84-46e5-9197-0e0c2e054ad1", |
| 252 | + "metadata": { |
| 253 | + "trusted": true, |
| 254 | + "vscode": { |
| 255 | + "languageId": "c++" |
| 256 | + } |
| 257 | + }, |
| 258 | + "outputs": [], |
| 259 | + "source": [ |
| 260 | + "int main() {\n", |
| 261 | + " // Debug: Initialization\n", |
| 262 | + " std::cout << \"[DEBUG] Initializing SDL\" << std::endl;\n", |
| 263 | + "\n", |
| 264 | + " if (SDL_Init(SDL_INIT_EVENTS) != 0) {\n", |
| 265 | + " std::cerr << \"[ERROR] SDL_Init failed: \" << SDL_GetError() << std::endl;\n", |
| 266 | + " return 1;\n", |
| 267 | + " }\n", |
| 268 | + " std::cout << \"[DEBUG] SDL initialized successfully\" << std::endl;\n", |
| 269 | + "\n", |
| 270 | + " // Define width, height, and samples\n", |
| 271 | + " int w = 320, h = 240, samps = 16; // # samples\n", |
| 272 | + " std::cout << \"[DEBUG] Dimensions: \" << w << \"x\" << h << \", Samples: \" << samps << std::endl;\n", |
| 273 | + "\n", |
| 274 | + " // Create an off-screen surface\n", |
| 275 | + " SDL_Surface* surface = SDL_CreateRGBSurfaceWithFormat(0, w, h, 32, SDL_PIXELFORMAT_RGBA32);\n", |
| 276 | + " if (!surface) {\n", |
| 277 | + " std::cerr << \"[ERROR] Failed to create SDL surface: \" << SDL_GetError() << std::endl;\n", |
| 278 | + " SDL_Quit();\n", |
| 279 | + " return 1;\n", |
| 280 | + " }\n", |
| 281 | + " std::cout << \"[DEBUG] SDL surface created successfully\" << std::endl;\n", |
| 282 | + "\n", |
| 283 | + " // Prepare the camera and pixel buffer\n", |
| 284 | + " Ray cam(Vec(50, 52, 295.6), Vec(0, -0.042612, -1).norm()); // cam pos, dir\n", |
| 285 | + " Vec cx = Vec(w * .5135 / h), cy = (cx % cam.d).norm() * .5135, r,\n", |
| 286 | + " *c = new Vec[w * h];\n", |
| 287 | + "\n", |
| 288 | + " std::cout << \"[DEBUG] The image should be on your screen soon\" << std::endl;\n", |
| 289 | + " // Render the scene\n", |
| 290 | + " for (int y = 0; y < h; y++) { // Loop over image rows\n", |
| 291 | + " unsigned short x, Xi[3] = {0, 0, static_cast<unsigned short>(y * y * y)};\n", |
| 292 | + " for (x = 0; x < w; x++) { // Loop cols\n", |
| 293 | + " int sy, i = (h - y - 1) * w + x;\n", |
| 294 | + " for (sy = 0; sy < 2; sy++) { // 2x2 subpixel rows\n", |
| 295 | + " int sx;\n", |
| 296 | + " for (sx = 0; sx < 2; sx++, r = Vec()) { // 2x2 subpixel cols\n", |
| 297 | + " for (int s = 0; s < samps; s++) { // Subpixel sampling\n", |
| 298 | + " double r1 = 2 * erand48(Xi),\n", |
| 299 | + " dx = r1 < 1 ? sqrt(r1) - 1 : 1 - sqrt(2 - r1);\n", |
| 300 | + " double r2 = 2 * erand48(Xi),\n", |
| 301 | + " dy = r2 < 1 ? sqrt(r2) - 1 : 1 - sqrt(2 - r2);\n", |
| 302 | + " Vec d = cx * (((sx + .5 + dx) / 2 + x) / w - .5) +\n", |
| 303 | + " cy * (((sy + .5 + dy) / 2 + y) / h - .5) + cam.d;\n", |
| 304 | + " r = r + radiance(Ray(cam.o + d * 140, d.norm()), 0, Xi) * (1. / samps);\n", |
| 305 | + " }\n", |
| 306 | + " c[i] = c[i] + Vec(clamp(r.x), clamp(r.y), clamp(r.z)) * .25;\n", |
| 307 | + " }\n", |
| 308 | + " }\n", |
| 309 | + " }\n", |
| 310 | + " }\n", |
| 311 | + "\n", |
| 312 | + " // Map the pixel buffer to the SDL surface\n", |
| 313 | + " uint8_t* pixels = (uint8_t*)surface->pixels;\n", |
| 314 | + " for (int i = 0; i < w * h; i++) {\n", |
| 315 | + " uint32_t* pixel_ptr = (uint32_t*)pixels + i;\n", |
| 316 | + " *pixel_ptr = SDL_MapRGBA(surface->format, \n", |
| 317 | + " toInt(c[i].x), // Red\n", |
| 318 | + " toInt(c[i].y), // Green\n", |
| 319 | + " toInt(c[i].z), // Blue\n", |
| 320 | + " 255); // Alpha\n", |
| 321 | + " }\n", |
| 322 | + " std::cout << \"[DEBUG] Mapped pixel buffer to surface\" << std::endl;\n", |
| 323 | + "\n", |
| 324 | + " // Save the BMP to the filesystem\n", |
| 325 | + " const std::string bmp_filename = \"render.bmp\";\n", |
| 326 | + " save_bmp_to_filesystem(surface, bmp_filename);\n", |
| 327 | + "\n", |
| 328 | + " // Render the surface to Jupyter\n", |
| 329 | + " render_sdl_surface_to_jupyter(surface, bmp_filename);\n", |
| 330 | + "\n", |
| 331 | + " // Cleanup\n", |
| 332 | + " delete[] c;\n", |
| 333 | + " SDL_Quit();\n", |
| 334 | + " std::cout << \"[DEBUG] Exiting main\" << std::endl;\n", |
| 335 | + "\n", |
| 336 | + " return 0;\n", |
| 337 | + "}\n", |
| 338 | + "\n", |
| 339 | + "main();" |
| 340 | + ] |
| 341 | + } |
| 342 | + ], |
| 343 | + "metadata": { |
| 344 | + "kernelspec": { |
| 345 | + "display_name": "C++20", |
| 346 | + "language": "cpp", |
| 347 | + "name": "xcpp20" |
| 348 | + }, |
| 349 | + "language_info": { |
| 350 | + "codemirror_mode": "text/x-c++src", |
| 351 | + "file_extension": ".cpp", |
| 352 | + "mimetype": "text/x-c++src", |
| 353 | + "name": "C++", |
| 354 | + "version": "20" |
| 355 | + } |
| 356 | + }, |
| 357 | + "nbformat": 4, |
| 358 | + "nbformat_minor": 5 |
| 359 | +} |
0 commit comments