├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── CourierNewB.h ├── README.md ├── ascii3d.cpp ├── ascii_art.cpp ├── ascii_art.h ├── cmake └── FindLIBIGL.cmake ├── image2ascii.cpp ├── include ├── Camera.h ├── DirectionalLight.h ├── Light.h ├── Material.h ├── Object.h ├── Plane.h ├── PointLight.h ├── Ray.h ├── Sphere.h ├── Triangle.h ├── TriangleSoup.h ├── blinn_phong_shading.h ├── dirname.h ├── first_hit.h ├── json.hpp ├── raycolor.h ├── readSTL.h ├── read_json.h ├── reflect.h ├── viewing_ray.h └── write_ppm.h ├── max-schmeling.jpg ├── spy.cpp ├── src ├── DirectionalLight.cpp ├── Plane.cpp ├── PointLight.cpp ├── Sphere.cpp ├── Triangle.cpp ├── TriangleSoup.cpp ├── blinn_phong_shading.cpp ├── first_hit.cpp ├── raycolor.cpp ├── reflect.cpp ├── viewing_ray.cpp └── write_ppm.cpp ├── suzanne-ascii3d.gif ├── suzanne.obj └── terminal_size.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Compiled Dynamic libraries 12 | *.so 13 | *.dylib 14 | *.dll 15 | 16 | # Fortran module files 17 | *.mod 18 | 19 | # Compiled Static libraries 20 | *.lai 21 | *.la 22 | *.a 23 | *.lib 24 | 25 | # Executables 26 | *.exe 27 | *.out 28 | *.app 29 | 30 | *.un~ 31 | *.swo 32 | *.swp 33 | 34 | build/* 35 | build*/* 36 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "libigl"] 2 | path = libigl 3 | url = https://github.com/libigl/libigl/ 4 | [submodule "stb"] 5 | path = stb 6 | url = https://github.com/nothings/stb 7 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1) 2 | project(ascii3d) 3 | 4 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) 5 | 6 | # libigl 7 | #option(LIBIGL_USE_STATIC_LIBRARY "Use Static Library" ON) 8 | option(LIBIGL_WITH_EMBREE "Use Embree" ON) 9 | 10 | find_package(LIBIGL REQUIRED QUIET) 11 | 12 | add_library(ascii ascii_art.cpp) 13 | target_link_libraries(ascii) 14 | 15 | add_executable(image2ascii image2ascii.cpp) 16 | target_link_libraries(image2ascii ascii) 17 | 18 | add_executable(spy spy.cpp) 19 | target_link_libraries(spy ascii igl::core) 20 | 21 | file(GLOB SRC_CPP src/*.cpp) 22 | add_executable(ascii3d ascii3d.cpp ${SRC_CPP}) 23 | target_include_directories(ascii3d PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) 24 | target_link_libraries(ascii3d ascii igl::core igl::embree) 25 | -------------------------------------------------------------------------------- /CourierNewB.h: -------------------------------------------------------------------------------- 1 | const int CourierNewB_w = 5; 2 | const int CourierNewB_h = 10; 3 | const int CourierNewB_count = 95; 4 | const char CourierNewB_str[96] = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; 5 | unsigned char CourierNewB[CourierNewB_count][CourierNewB_h][CourierNewB_w] = 6 | {{ 7 | {255,255,255,255,255}, 8 | {255,255,255,255,255}, 9 | {255,255,255,255,255}, 10 | {255,255,255,255,255}, 11 | {255,255,255,255,255}, 12 | {255,255,255,255,255}, 13 | {255,255,255,255,255}, 14 | {255,255,255,255,255}, 15 | {255,255,255,255,255}, 16 | {255,255,255,255,255}}, 17 | { 18 | {255,248,240,255,255}, 19 | {255,150, 70,255,255}, 20 | {255,145, 57,255,255}, 21 | {255,167, 82,255,255}, 22 | {255,220,181,255,255}, 23 | {255,213,165,255,255}, 24 | {255,244,231,255,255}, 25 | {255,255,255,255,255}, 26 | {255,255,255,255,255}, 27 | {255,255,255,255,255}}, 28 | { 29 | {255,255,255,255,255}, 30 | {226,143,177,174,255}, 31 | {221, 93,153,141,255}, 32 | {246,199,223,216,255}, 33 | {255,255,255,255,255}, 34 | {255,255,255,255,255}, 35 | {255,255,255,255,255}, 36 | {255,255,255,255,255}, 37 | {255,255,255,255,255}, 38 | {255,255,255,255,255}}, 39 | { 40 | {255,215,223,224,255}, 41 | {255, 94,122,141,255}, 42 | {193, 36, 53, 81,247}, 43 | {205, 52, 68,119,253}, 44 | {142, 22, 31, 93,255}, 45 | {208, 71, 80,181,255}, 46 | {228,141,144,225,255}, 47 | {255,255,255,255,255}, 48 | {255,255,255,255,255}, 49 | {255,255,255,255,255}}, 50 | { 51 | {255,241,208,255,255}, 52 | {242, 94, 31,147,255}, 53 | {162, 70,187,140,255}, 54 | {234,109, 86,159,255}, 55 | {159,178,198, 49,243}, 56 | {153, 46, 33,153,255}, 57 | {255,200,120,255,255}, 58 | {255,253,249,255,255}, 59 | {255,255,255,255,255}, 60 | {255,255,255,255,255}}, 61 | { 62 | {255,234,252,255,255}, 63 | {195,122,149,255,255}, 64 | {174,152,124,214,251}, 65 | {216, 84, 68,159,253}, 66 | {218,144,127,144,255}, 67 | {255,190,128,136,255}, 68 | {255,255,219,245,255}, 69 | {255,255,255,255,255}, 70 | {255,255,255,255,255}, 71 | {255,255,255,255,255}}, 72 | { 73 | {255,255,255,255,255}, 74 | {255,226,150,208,255}, 75 | {255, 88,101,189,255}, 76 | {255, 60,155,226,244}, 77 | {185, 73, 64, 42,233}, 78 | {209, 70, 50, 46,232}, 79 | {255,233,220,237,251}, 80 | {255,255,255,255,255}, 81 | {255,255,255,255,255}, 82 | {255,255,255,255,255}}, 83 | { 84 | {255,250,246,255,255}, 85 | {255,188,109,255,255}, 86 | {255,200,119,255,255}, 87 | {255,242,220,255,255}, 88 | {255,255,255,255,255}, 89 | {255,255,255,255,255}, 90 | {255,255,255,255,255}, 91 | {255,255,255,255,255}, 92 | {255,255,255,255,255}, 93 | {255,255,255,255,255}}, 94 | { 95 | {255,255,255,245,255}, 96 | {255,255,179,117,255}, 97 | {255,255, 73,188,255}, 98 | {255,224, 58,247,255}, 99 | {255,217, 60,251,255}, 100 | {255,247, 60,216,255}, 101 | {255,255,141,116,255}, 102 | {255,255,247,211,255}, 103 | {255,255,255,255,255}, 104 | {255,255,255,255,255}}, 105 | { 106 | {250,250,255,255,255}, 107 | {207, 97,255,255,255}, 108 | {255, 71,190,255,255}, 109 | {255,137,121,255,255}, 110 | {255,147,112,255,255}, 111 | {255, 93,165,255,255}, 112 | {215, 64,244,255,255}, 113 | {235,219,255,255,255}, 114 | {255,255,255,255,255}, 115 | {255,255,255,255,255}}, 116 | { 117 | {255,254,249,255,255}, 118 | {246,188,123,243,254}, 119 | {176, 40, 11,112,252}, 120 | {247, 70, 42,188,255}, 121 | {247,213,230,225,255}, 122 | {255,255,255,255,255}, 123 | {255,255,255,255,255}, 124 | {255,255,255,255,255}, 125 | {255,255,255,255,255}, 126 | {255,255,255,255,255}}, 127 | { 128 | {255,255,255,255,255}, 129 | {255,230,194,255,255}, 130 | {255,195, 99,255,255}, 131 | {141, 71, 39,107,222}, 132 | {212,139, 72,199,242}, 133 | {255,205,126,255,255}, 134 | {255,252,248,255,255}, 135 | {255,255,255,255,255}, 136 | {255,255,255,255,255}, 137 | {255,255,255,255,255}}, 138 | { 139 | {255,255,255,255,255}, 140 | {255,255,255,255,255}, 141 | {255,255,255,255,255}, 142 | {255,255,255,255,255}, 143 | {255,253,220,250,255}, 144 | {255,198, 80,254,255}, 145 | {255,145,183,255,255}, 146 | {255,233,254,255,255}, 147 | {255,255,255,255,255}, 148 | {255,255,255,255,255}}, 149 | { 150 | {255,255,255,255,255}, 151 | {255,255,255,255,255}, 152 | {255,255,255,255,255}, 153 | {193,116,118,143,251}, 154 | {224,187,188,200,253}, 155 | {255,255,255,255,255}, 156 | {255,255,255,255,255}, 157 | {255,255,255,255,255}, 158 | {255,255,255,255,255}, 159 | {255,255,255,255,255}}, 160 | { 161 | {255,255,255,255,255}, 162 | {255,255,255,255,255}, 163 | {255,255,255,255,255}, 164 | {255,255,255,255,255}, 165 | {255,252,247,255,255}, 166 | {255,183,102,255,255}, 167 | {255,244,230,255,255}, 168 | {255,255,255,255,255}, 169 | {255,255,255,255,255}, 170 | {255,255,255,255,255}}, 171 | { 172 | {255,255,253,175,248}, 173 | {255,255,193, 78,255}, 174 | {255,255, 89,181,255}, 175 | {255,204, 73,255,255}, 176 | {255,100,167,255,255}, 177 | {212, 66,250,255,255}, 178 | {146,158,255,255,255}, 179 | {247,254,255,255,255}, 180 | {255,255,255,255,255}, 181 | {255,255,255,255,255}}, 182 | { 183 | {255,227,203,255,255}, 184 | {218, 66, 68,144,255}, 185 | {127,154,245, 68,244}, 186 | {109,182,255, 74,233}, 187 | {131,145,236, 69,246}, 188 | {226, 70, 61,156,255}, 189 | {255,238,221,255,255}, 190 | {255,255,255,255,255}, 191 | {255,255,255,255,255}, 192 | {255,255,255,255,255}}, 193 | { 194 | {255,244,234,255,255}, 195 | {178, 47,107,255,255}, 196 | {242,169, 99,255,255}, 197 | {255,194, 99,255,255}, 198 | {255,189, 97,255,255}, 199 | {178, 57, 38,112,251}, 200 | {244,229,232,233,255}, 201 | {255,255,255,255,255}, 202 | {255,255,255,255,255}, 203 | {255,255,255,255,255}}, 204 | { 205 | {255,217,202,255,255}, 206 | {168, 63, 78,125,255}, 207 | {177,229,226, 59,251}, 208 | {255,235, 66,159,255}, 209 | {209, 57,158,255,255}, 210 | { 81, 29, 81,102,252}, 211 | {239,232,227,233,254}, 212 | {255,255,255,255,255}, 213 | {255,255,255,255,255}, 214 | {255,255,255,255,255}}, 215 | { 216 | {255,217,200,252,255}, 217 | {184, 68, 83,113,255}, 218 | {237,237,200, 74,252}, 219 | {255,197, 38, 92,255}, 220 | {246,255,238, 55,227}, 221 | {147, 78, 78,108,253}, 222 | {248,215,217,255,255}, 223 | {255,255,255,255,255}, 224 | {255,255,255,255,255}, 225 | {255,255,255,255,255}}, 226 | { 227 | {255,255,242,247,255}, 228 | {255,233, 32,163,255}, 229 | {255,118, 26,156,255}, 230 | {199, 75,130,150,255}, 231 | {120, 44, 39, 87,255}, 232 | {247,196, 33, 88,255}, 233 | {255,253,233,239,255}, 234 | {255,255,255,255,255}, 235 | {255,255,255,255,255}, 236 | {255,255,255,255,255}}, 237 | { 238 | {250,231,227,239,255}, 239 | {185, 47, 83,150,255}, 240 | {172, 62,156,230,255}, 241 | {210,104,124, 78,248}, 242 | {238,255,255, 65,224}, 243 | {143, 74, 74,111,255}, 244 | {253,218,217,255,255}, 245 | {255,255,255,255,255}, 246 | {255,255,255,255,255}, 247 | {255,255,255,255,255}}, 248 | { 249 | {255,255,219,205,252}, 250 | {255,144, 71,105,235}, 251 | {202, 56,189,251,255}, 252 | {162, 18, 81, 89,248}, 253 | {179, 69,255, 72,208}, 254 | {245, 88, 72, 89,246}, 255 | {255,248,213,248,255}, 256 | {255,255,255,255,255}, 257 | {255,255,255,255,255}, 258 | {255,255,255,255,255}}, 259 | { 260 | {242,228,228,238,254}, 261 | {116, 59, 70, 64,250}, 262 | {226,250,186, 92,255}, 263 | {255,255,108,168,255}, 264 | {255,239, 65,235,255}, 265 | {255,200,115,255,255}, 266 | {255,250,250,255,255}, 267 | {255,255,255,255,255}, 268 | {255,255,255,255,255}, 269 | {255,255,255,255,255}}, 270 | { 271 | {255,222,201,253,255}, 272 | {194, 68, 81,118,255}, 273 | {145,122,205, 67,251}, 274 | {197, 29, 41,105,255}, 275 | {118,145,237, 62,241}, 276 | {191, 68, 77,116,255}, 277 | {255,233,217,255,255}, 278 | {255,255,255,255,255}, 279 | {255,255,255,255,255}, 280 | {255,255,255,255,255}}, 281 | { 282 | {255,230,201,254,255}, 283 | {224, 68, 72,122,255}, 284 | {155,124,255, 54,228}, 285 | {209, 68,111, 14,210}, 286 | {255,206,120, 51,236}, 287 | {203, 85, 74,182,255}, 288 | {248,214,240,255,255}, 289 | {255,255,255,255,255}, 290 | {255,255,255,255,255}, 291 | {255,255,255,255,255}}, 292 | { 293 | {255,255,255,255,255}, 294 | {255,255,255,255,255}, 295 | {255,219,180,255,255}, 296 | {255,198,136,255,255}, 297 | {255,255,255,255,255}, 298 | {255,183,102,255,255}, 299 | {255,244,230,255,255}, 300 | {255,255,255,255,255}, 301 | {255,255,255,255,255}, 302 | {255,255,255,255,255}}, 303 | { 304 | {255,255,255,255,255}, 305 | {255,255,255,255,255}, 306 | {255,249,170,239,255}, 307 | {255,246,121,229,255}, 308 | {255,252,235,255,255}, 309 | {255,180,111,255,255}, 310 | {255,156,219,255,255}, 311 | {255,251,255,255,255}, 312 | {255,255,255,255,255}, 313 | {255,255,255,255,255}}, 314 | { 315 | {255,255,255,255,255}, 316 | {255,255,252,168,225}, 317 | {255,187, 71, 99,235}, 318 | { 87, 39,204,255,255}, 319 | {186, 80,102,214,255}, 320 | {255,255,163, 70,213}, 321 | {255,255,255,248,249}, 322 | {255,255,255,255,255}, 323 | {255,255,255,255,255}, 324 | {255,255,255,255,255}}, 325 | { 326 | {255,255,255,255,255}, 327 | {255,255,255,255,255}, 328 | {160,144,146,143,216}, 329 | {137,117,119,116,207}, 330 | {108, 85, 87, 83,194}, 331 | {254,253,253,253,255}, 332 | {255,255,255,255,255}, 333 | {255,255,255,255,255}, 334 | {255,255,255,255,255}, 335 | {255,255,255,255,255}}, 336 | { 337 | {255,255,255,255,255}, 338 | {169,221,255,255,255}, 339 | {157, 59,134,244,255}, 340 | {255,247,102, 31,196}, 341 | {244,147, 69,137,237}, 342 | {107,108,229,255,255}, 343 | {242,255,255,255,255}, 344 | {255,255,255,255,255}, 345 | {255,255,255,255,255}, 346 | {255,255,255,255,255}}, 347 | { 348 | {255,255,255,255,255}, 349 | {220,106, 87,185,255}, 350 | {170,129,194, 56,244}, 351 | {255,237, 96,106,255}, 352 | {255,224,154,255,255}, 353 | {255,204,147,255,255}, 354 | {255,242,226,255,255}, 355 | {255,255,255,255,255}, 356 | {255,255,255,255,255}, 357 | {255,255,255,255,255}}, 358 | { 359 | {255,247,234,255,255}, 360 | {232,107,111,184,255}, 361 | {151,175,161,108,255}, 362 | {145,125,106,122,255}, 363 | {144,153,100,117,255}, 364 | {178,137,202,200,255}, 365 | {254,155,124,207,255}, 366 | {255,255,255,255,255}, 367 | {255,255,255,255,255}, 368 | {255,255,255,255,255}}, 369 | { 370 | {255,255,255,255,255}, 371 | {174, 93,137,255,255}, 372 | {224, 51, 27,215,255}, 373 | {212, 73,125,115,255}, 374 | {116, 47, 76, 45,236}, 375 | { 26,132,211, 29,119}, 376 | {232,241,249,232,239}, 377 | {255,255,255,255,255}, 378 | {255,255,255,255,255}, 379 | {255,255,255,255,255}}, 380 | { 381 | {255,255,255,255,255}, 382 | {116, 99,102,181,255}, 383 | {103,108,181, 54,235}, 384 | {145, 45, 72, 46,240}, 385 | {135,132,239, 87,162}, 386 | { 67, 54, 83, 73,211}, 387 | {234,230,227,245,255}, 388 | {255,255,255,255,255}, 389 | {255,255,255,255,255}, 390 | {255,255,255,255,255}}, 391 | { 392 | {255,255,255,255,255}, 393 | {233,112, 89,116,227}, 394 | { 89,137,208, 63,195}, 395 | { 71,254,255,246,250}, 396 | { 66,216,255,223,234}, 397 | {175, 64, 85, 68,213}, 398 | {255,236,210,242,255}, 399 | {255,255,255,255,255}, 400 | {255,255,255,255,255}, 401 | {255,255,255,255,255}}, 402 | { 403 | {255,255,255,255,255}, 404 | {116, 98,111,213,255}, 405 | { 77,139,165, 65,237}, 406 | {104,193,255, 98,186}, 407 | {100,187,254, 72,204}, 408 | { 60, 60, 68,130,255}, 409 | {235,229,235,255,255}, 410 | {255,255,255,255,255}, 411 | {255,255,255,255,255}, 412 | {255,255,255,255,255}}, 413 | { 414 | {255,255,255,255,255}, 415 | {117,100,105,108,237}, 416 | {106,101,147, 78,226}, 417 | {149, 33, 53,243,255}, 418 | {138,123,173,147,208}, 419 | { 69, 53, 89, 37,194}, 420 | {234,230,227,232,251}, 421 | {255,255,255,255,255}, 422 | {255,255,255,255,255}, 423 | {255,255,255,255,255}}, 424 | { 425 | {255,255,255,255,255}, 426 | {147, 97,107, 95,176}, 427 | {167, 63,155,114,138}, 428 | {209, 36, 32,202,255}, 429 | {199, 84,168,229,255}, 430 | {118, 33,112,249,255}, 431 | {238,231,232,254,255}, 432 | {255,255,255,255,255}, 433 | {255,255,255,255,255}, 434 | {255,255,255,255,255}}, 435 | { 436 | {255,255,255,255,255}, 437 | {236,114, 88,109,225}, 438 | { 93,138,218, 96,202}, 439 | { 71,252,211,191,226}, 440 | { 68,221,144, 28,153}, 441 | {162, 67, 95, 64,215}, 442 | {255,234,211,241,255}, 443 | {255,255,255,255,255}, 444 | {255,255,255,255,255}, 445 | {255,255,255,255,255}}, 446 | { 447 | {255,255,255,255,255}, 448 | {138,124,172, 99,219}, 449 | {112,119,214, 48,231}, 450 | {144, 43, 75, 54,248}, 451 | {135,134,221, 69,244}, 452 | { 71, 84,160, 28,185}, 453 | {235,235,242,232,246}, 454 | {255,255,255,255,255}, 455 | {255,255,255,255,255}, 456 | {255,255,255,255,255}}, 457 | { 458 | {255,255,255,255,255}, 459 | {186, 96, 98,131,252}, 460 | {232,137, 67,211,254}, 461 | {255,195,104,255,255}, 462 | {255,188, 98,255,255}, 463 | {176, 57, 38,113,251}, 464 | {243,229,232,234,255}, 465 | {255,255,255,255,255}, 466 | {255,255,255,255,255}, 467 | {255,255,255,255,255}}, 468 | { 469 | {255,255,255,255,255}, 470 | {255,177, 96, 96,161}, 471 | {255,228,153, 56,219}, 472 | {226,255,218, 89,255}, 473 | { 78,228,204, 83,255}, 474 | {114, 70, 65,170,255}, 475 | {255,222,228,255,255}, 476 | {255,255,255,255,255}, 477 | {255,255,255,255,255}, 478 | {255,255,255,255,255}}, 479 | { 480 | {255,255,255,255,255}, 481 | {114,114,183, 90,198}, 482 | {104,102, 89, 96,236}, 483 | {147, 15, 46,250,255}, 484 | {135,125,134,113,255}, 485 | { 65, 65,224, 64,153}, 486 | {234,232,253,242,240}, 487 | {255,255,255,255,255}, 488 | {255,255,255,255,255}, 489 | {255,255,255,255,255}}, 490 | { 491 | {255,255,255,255,255}, 492 | {132, 94,139,255,255}, 493 | {190, 46,200,255,255}, 494 | {242, 78,251,251,249}, 495 | {236, 73,253,155,157}, 496 | {108, 33, 78, 43,158}, 497 | {234,232,228,230,247}, 498 | {255,255,255,255,255}, 499 | {255,255,255,255,255}, 500 | {255,255,255,255,255}}, 501 | { 502 | {255,255,255,255,255}, 503 | {101,211,255,125,157}, 504 | { 19, 91,201, 0,155}, 505 | { 73, 57, 54, 52,177}, 506 | { 81,151, 92,118,167}, 507 | { 30,137,214, 42,113}, 508 | {232,239,250,231,240}, 509 | {255,255,255,255,255}, 510 | {255,255,255,255,255}, 511 | {255,255,255,255,255}}, 512 | { 513 | {255,255,255,255,255}, 514 | { 99,197,188, 91,186}, 515 | { 67, 31,209, 53,198}, 516 | {110, 76,112, 89,221}, 517 | { 99,189, 85, 21,224}, 518 | { 55, 90,206, 47,226}, 519 | {235,233,255,242,252}, 520 | {255,255,255,255,255}, 521 | {255,255,255,255,255}, 522 | {255,255,255,255,255}}, 523 | { 524 | {255,255,255,255,255}, 525 | {234,112, 89,185,255}, 526 | { 86,142,197, 63,205}, 527 | { 71,255,255,147,135}, 528 | { 64,218,255, 96,167}, 529 | {177, 64, 73,107,252}, 530 | {255,234,218,255,255}, 531 | {255,255,255,255,255}, 532 | {255,255,255,255,255}, 533 | {255,255,255,255,255}}, 534 | { 535 | {255,255,255,255,255}, 536 | {160, 96, 99,148,251}, 537 | {185, 56,207, 84,161}, 538 | {223, 56,151, 69,188}, 539 | {219, 49,142,201,255}, 540 | {136, 33,106,246,255}, 541 | {240,231,231,253,255}, 542 | {255,255,255,255,255}, 543 | {255,255,255,255,255}, 544 | {255,255,255,255,255}}, 545 | { 546 | {255,255,255,255,255}, 547 | {235,113, 89,187,255}, 548 | { 87,141,196, 62,207}, 549 | { 72,255,255,145,137}, 550 | { 65,231,255,104,165}, 551 | {168, 60, 85,106,249}, 552 | {224, 21, 21, 81,189}, 553 | {241,211,222,200,246}, 554 | {255,255,255,255,255}, 555 | {255,255,255,255,255}}, 556 | { 557 | {255,255,255,255,255}, 558 | {116, 99,104,190,255}, 559 | {105,114,189, 51,234}, 560 | {146, 60, 81, 95,252}, 561 | {138,111,125, 90,253}, 562 | { 66, 63,240,112,121}, 563 | {234,232,253,252,238}, 564 | {255,255,255,255,255}, 565 | {255,255,255,255,255}, 566 | {255,255,255,255,255}}, 567 | { 568 | {255,255,255,255,255}, 569 | {235,113, 89,131,249}, 570 | {124,108,201, 75,240}, 571 | {201, 76,102,168,255}, 572 | {170,212,200, 51,220}, 573 | { 96, 60, 85, 90,243}, 574 | {248,229,213,251,255}, 575 | {255,255,255,255,255}, 576 | {255,255,255,255,255}, 577 | {255,255,255,255,255}}, 578 | { 579 | {255,255,255,255,255}, 580 | {127, 98,101, 99,212}, 581 | { 60,122, 80, 83,171}, 582 | {199,187,110,219,227}, 583 | {255,185, 99,255,255}, 584 | {218, 64, 36,157,255}, 585 | {250,230,231,240,255}, 586 | {255,255,255,255,255}, 587 | {255,255,255,255,255}, 588 | {255,255,255,255,255}}, 589 | { 590 | {255,255,255,255,255}, 591 | {111,128,171, 93,188}, 592 | { 71,153,226, 48,202}, 593 | { 99,199,255, 82,223}, 594 | { 99,176,255, 67,228}, 595 | {198, 67, 69,124,255}, 596 | {255,236,218,255,255}, 597 | {255,255,255,255,255}, 598 | {255,255,255,255,255}, 599 | {255,255,255,255,255}}, 600 | { 601 | {255,255,255,255,255}, 602 | { 92,160,216, 96,149}, 603 | { 58,160,243, 41,183}, 604 | {176,110,199, 91,255}, 605 | {249, 76, 60,185,255}, 606 | {255,149, 59,254,255}, 607 | {255,252,244,255,255}, 608 | {255,255,255,255,255}, 609 | {255,255,255,255,255}, 610 | {255,255,255,255,255}}, 611 | { 612 | {255,255,255,255,255}, 613 | { 98,151,210, 93,163}, 614 | { 50,162,170, 79,154}, 615 | { 92, 82, 33, 82,202}, 616 | { 93, 32, 95, 14,228}, 617 | {128,103,204, 50,244}, 618 | {246,247,255,241,255}, 619 | {255,255,255,255,255}, 620 | {255,255,255,255,255}, 621 | {255,255,255,255,255}}, 622 | { 623 | {255,255,255,255,255}, 624 | {111,157,221, 92,200}, 625 | {144, 67,135, 67,236}, 626 | {255,105, 13,239,255}, 627 | {211, 72, 88,136,255}, 628 | { 54, 93,191, 15,173}, 629 | {233,241,248,232,244}, 630 | {255,255,255,255,255}, 631 | {255,255,255,255,255}, 632 | {255,255,255,255,255}}, 633 | { 634 | {255,255,255,255,255}, 635 | {112,158,218, 92,199}, 636 | {142, 73,153, 62,237}, 637 | {255,107, 35,227,255}, 638 | {255,189, 94,255,255}, 639 | {220, 66, 35,154,255}, 640 | {250,230,231,240,255}, 641 | {255,255,255,255,255}, 642 | {255,255,255,255,255}, 643 | {255,255,255,255,255}}, 644 | { 645 | {255,255,255,255,255}, 646 | {186, 95,103,124,251}, 647 | {132,119, 84, 95,255}, 648 | {240,161, 78,247,255}, 649 | {214, 61,221,124,230}, 650 | {115, 36, 81, 44,229}, 651 | {242,231,227,235,253}, 652 | {255,255,255,255,255}, 653 | {255,255,255,255,255}, 654 | {255,255,255,255,255}}, 655 | { 656 | {255,251,231,242,255}, 657 | {255,200, 35,165,255}, 658 | {255,187, 99,255,255}, 659 | {255,188,102,255,255}, 660 | {255,188,101,255,255}, 661 | {255,187,105,255,255}, 662 | {255,193, 33,180,255}, 663 | {255,243,193,221,255}, 664 | {255,255,255,255,255}, 665 | {255,255,255,255,255}}, 666 | { 667 | {171,197,255,255,255}, 668 | {182, 83,255,255,255}, 669 | {255, 77,198,255,255}, 670 | {255,174, 94,255,255}, 671 | {255,253, 70,210,255}, 672 | {255,255,160,100,255}, 673 | {255,255,244,120,246}, 674 | {255,255,255,255,255}, 675 | {255,255,255,255,255}, 676 | {255,255,255,255,255}}, 677 | { 678 | {251,230,243,255,255}, 679 | {227, 62,110,255,255}, 680 | {255,190, 97,255,255}, 681 | {255,192, 99,255,255}, 682 | {255,191, 99,255,255}, 683 | {255,198, 99,255,255}, 684 | {234, 77, 97,255,255}, 685 | {245,192,221,255,255}, 686 | {255,255,255,255,255}, 687 | {255,255,255,255,255}}, 688 | { 689 | {255,254,245,255,255}, 690 | {255,139, 63,246,255}, 691 | {177, 75,124, 97,255}, 692 | {204,227,255,188,250}, 693 | {255,255,255,255,255}, 694 | {255,255,255,255,255}, 695 | {255,255,255,255,255}, 696 | {255,255,255,255,255}, 697 | {255,255,255,255,255}, 698 | {255,255,255,255,255}}, 699 | { 700 | {255,255,255,255,255}, 701 | {255,255,255,255,255}, 702 | {255,255,255,255,255}, 703 | {255,255,255,255,255}, 704 | {255,255,255,255,255}, 705 | {255,255,255,255,255}, 706 | {255,255,255,255,255}, 707 | {141,145,145,142,166}, 708 | {140,143,143,141,164}, 709 | {255,255,255,255,255}}, 710 | { 711 | {255,219,239,255,255}, 712 | {255,204,136,251,255}, 713 | {255,255,246,252,255}, 714 | {255,255,255,255,255}, 715 | {255,255,255,255,255}, 716 | {255,255,255,255,255}, 717 | {255,255,255,255,255}, 718 | {255,255,255,255,255}, 719 | {255,255,255,255,255}, 720 | {255,255,255,255,255}}, 721 | { 722 | {255,255,255,255,255}, 723 | {255,255,255,255,255}, 724 | {223,143,138,222,255}, 725 | {202, 64, 40, 88,255}, 726 | { 95,115,109, 72,255}, 727 | {108, 81, 71, 35,200}, 728 | {253,215,231,234,247}, 729 | {255,255,255,255,255}, 730 | {255,255,255,255,255}, 731 | {255,255,255,255,255}}, 732 | { 733 | {233,251,255,255,255}, 734 | { 44,209,255,255,255}, 735 | {100,116,129,199,255}, 736 | { 96, 71,161, 64,204}, 737 | { 93,174,255,115,152}, 738 | { 43, 53, 84, 80,232}, 739 | {234,233,213,248,255}, 740 | {255,255,255,255,255}, 741 | {255,255,255,255,255}, 742 | {255,255,255,255,255}}, 743 | { 744 | {255,255,255,255,255}, 745 | {255,255,255,255,255}, 746 | {251,159,124,162,242}, 747 | {127, 96,163, 49,210}, 748 | { 75,205,255,219,235}, 749 | {175, 64, 88, 73,207}, 750 | {255,233,209,235,255}, 751 | {255,255,255,255,255}, 752 | {255,255,255,255,255}, 753 | {255,255,255,255,255}}, 754 | { 755 | {255,255,246,234,253}, 756 | {255,255,201, 47,228}, 757 | {240,142,140, 62,223}, 758 | { 93,122,137, 29,227}, 759 | { 60,245,255, 61,222}, 760 | {140, 70, 78, 25,140}, 761 | {255,223,220,237,240}, 762 | {255,255,255,255,255}, 763 | {255,255,255,255,255}, 764 | {255,255,255,255,255}}, 765 | { 766 | {255,255,255,255,255}, 767 | {255,255,255,255,255}, 768 | {242,152,136,218,255}, 769 | { 86, 64, 86, 39,230}, 770 | { 35, 86,116, 88,216}, 771 | {150, 73, 94, 87,227}, 772 | {255,228,212,238,255}, 773 | {255,255,255,255,255}, 774 | {255,255,255,255,255}, 775 | {255,255,255,255,255}}, 776 | { 777 | {255,255,238,234,253}, 778 | {255,180, 71, 80,200}, 779 | {213, 49, 96,159,247}, 780 | {214, 53,108,171,253}, 781 | {255,101,196,255,255}, 782 | {164, 36, 58,123,255}, 783 | {241,231,229,235,255}, 784 | {255,255,255,255,255}, 785 | {255,255,255,255,255}, 786 | {255,255,255,255,255}}, 787 | { 788 | {255,255,255,255,255}, 789 | {255,255,255,255,255}, 790 | {237,138,142,153,204}, 791 | { 88,127,122, 16,199}, 792 | { 60,243,232, 62,249}, 793 | {146, 72, 70, 46,246}, 794 | {254,169,130, 73,252}, 795 | {240,117,107,207,255}, 796 | {255,255,255,255,255}, 797 | {255,255,255,255,255}}, 798 | { 799 | {233,247,255,255,255}, 800 | { 60,167,255,255,255}, 801 | {140, 83,132,209,255}, 802 | {142, 61,144, 70,251}, 803 | {135,154,255, 71,242}, 804 | { 71, 88,175, 27,184}, 805 | {235,237,244,232,246}, 806 | {255,255,255,255,255}, 807 | {255,255,255,255,255}, 808 | {255,255,255,255,255}}, 809 | { 810 | {255,242,241,255,255}, 811 | {255,172,163,255,255}, 812 | {221,137,191,255,255}, 813 | {227,100, 96,255,255}, 814 | {255,195, 98,255,255}, 815 | {150, 56, 40, 96,239}, 816 | {239,229,232,231,253}, 817 | {255,255,255,255,255}, 818 | {255,255,255,255,255}, 819 | {255,255,255,255,255}}, 820 | { 821 | {255,253,234,252,255}, 822 | {255,252,114,241,255}, 823 | {211,139,138,206,255}, 824 | {212,148, 85,123,255}, 825 | {255,255,168,125,255}, 826 | {255,255,166,123,255}, 827 | {229,195, 96,147,255}, 828 | {190, 94,134,246,255}, 829 | {255,255,255,255,255}, 830 | {255,255,255,255,255}}, 831 | { 832 | {235,244,255,255,255}, 833 | { 88,122,255,255,255}, 834 | {175,113,179,154,248}, 835 | {182, 48, 50,151,250}, 836 | {182, 17, 78,241,255}, 837 | { 88,131,122, 40,201}, 838 | {235,245,249,230,247}, 839 | {255,255,255,255,255}, 840 | {255,255,255,255,255}, 841 | {255,255,255,255,255}}, 842 | { 843 | {248,229,243,255,255}, 844 | {204, 53,113,255,255}, 845 | {255,188, 98,255,255}, 846 | {255,191,101,255,255}, 847 | {255,187, 98,255,255}, 848 | {150, 56, 40, 96,239}, 849 | {239,229,232,231,253}, 850 | {255,255,255,255,255}, 851 | {255,255,255,255,255}, 852 | {255,255,255,255,255}}, 853 | { 854 | {255,255,255,255,255}, 855 | {255,255,255,255,255}, 856 | {153,143,157,144,244}, 857 | { 35, 98, 51, 73,174}, 858 | { 82,181,109,134,161}, 859 | { 37,135, 72,113,106}, 860 | {233,242,237,242,239}, 861 | {255,255,255,255,255}, 862 | {255,255,255,255,255}, 863 | {255,255,255,255,255}}, 864 | { 865 | {255,255,255,255,255}, 866 | {255,255,255,255,255}, 867 | {173,155,127,207,255}, 868 | {100, 57,147, 70,250}, 869 | {143,150,255, 72,240}, 870 | { 74, 84,191, 32,196}, 871 | {235,236,247,233,248}, 872 | {255,255,255,255,255}, 873 | {255,255,255,255,255}, 874 | {255,255,255,255,255}}, 875 | { 876 | {255,255,255,255,255}, 877 | {255,255,255,255,255}, 878 | {248,153,127,214,255}, 879 | {110,108,154, 64,222}, 880 | { 62,221,255, 86,179}, 881 | {165, 65, 76, 96,246}, 882 | {255,231,216,255,255}, 883 | {255,255,255,255,255}, 884 | {255,255,255,255,255}, 885 | {255,255,255,255,255}}, 886 | { 887 | {255,255,255,255,255}, 888 | {255,255,255,255,255}, 889 | {153,158,125,199,255}, 890 | { 50, 71,162, 64,203}, 891 | {101,154,255,101,158}, 892 | {106, 49, 74, 96,241}, 893 | { 72,135,227,255,255}, 894 | {103,107,232,255,255}, 895 | {255,255,255,255,255}, 896 | {255,255,255,255,255}}, 897 | { 898 | {255,255,255,255,255}, 899 | {255,255,255,255,255}, 900 | {243,144,139,158,185}, 901 | { 97,119,140, 11,165}, 902 | { 59,224,248, 57,224}, 903 | {168, 66, 76, 35,225}, 904 | {255,243,198, 51,185}, 905 | {255,255,158, 91,158}, 906 | {255,255,255,255,255}, 907 | {255,255,255,255,255}}, 908 | { 909 | {255,255,255,255,255}, 910 | {255,255,255,255,255}, 911 | {199,155,178,132,230}, 912 | {203, 25, 72,140,209}, 913 | {255, 77,219,255,255}, 914 | {135, 32, 64,148,255}, 915 | {238,232,228,239,255}, 916 | {255,255,255,255,255}, 917 | {255,255,255,255,255}, 918 | {255,255,255,255,255}}, 919 | { 920 | {255,255,255,255,255}, 921 | {255,255,255,255,255}, 922 | {243,155,131,178,255}, 923 | {159, 41,106, 97,254}, 924 | {192,126,113, 92,246}, 925 | {122, 67, 88, 83,246}, 926 | {248,224,213,251,255}, 927 | {255,255,255,255,255}, 928 | {255,255,255,255,255}, 929 | {255,255,255,255,255}}, 930 | { 931 | {255,255,255,255,255}, 932 | {255,151,249,255,255}, 933 | {196, 32,125,161,250}, 934 | {197, 39,128,164,251}, 935 | {255, 74,239,255,243}, 936 | {255,112, 72, 74,177}, 937 | {255,252,214,230,255}, 938 | {255,255,255,255,255}, 939 | {255,255,255,255,255}, 940 | {255,255,255,255,255}}, 941 | { 942 | {255,255,255,255,255}, 943 | {255,255,255,255,255}, 944 | {155,217,188,162,251}, 945 | { 83,148,188, 48,242}, 946 | {142,144,250, 69,241}, 947 | {188, 58, 73, 31,196}, 948 | {255,225,225,236,247}, 949 | {255,255,255,255,255}, 950 | {255,255,255,255,255}, 951 | {255,255,255,255,255}}, 952 | { 953 | {255,255,255,255,255}, 954 | {255,255,255,255,255}, 955 | {148,174,206,143,195}, 956 | { 80, 90,187, 23,195}, 957 | {232, 80,109,149,255}, 958 | {255,135, 50,245,255}, 959 | {255,251,243,255,255}, 960 | {255,255,255,255,255}, 961 | {255,255,255,255,255}, 962 | {255,255,255,255,255}}, 963 | { 964 | {255,255,255,255,255}, 965 | {255,255,255,255,255}, 966 | {149,206,249,150,198}, 967 | { 47,116,130, 45,170}, 968 | {135, 22, 22, 55,246}, 969 | {189, 66,137, 92,255}, 970 | {252,245,253,245,255}, 971 | {255,255,255,255,255}, 972 | {255,255,255,255,255}, 973 | {255,255,255,255,255}}, 974 | { 975 | {255,255,255,255,255}, 976 | {255,255,255,255,255}, 977 | {168,173,213,144,231}, 978 | {153, 43, 68, 82,237}, 979 | {242, 68, 24,183,255}, 980 | { 71, 68,146, 19,190}, 981 | {234,240,248,232,246}, 982 | {255,255,255,255,255}, 983 | {255,255,255,255,255}, 984 | {255,255,255,255,255}}, 985 | { 986 | {255,255,255,255,255}, 987 | {255,255,255,255,255}, 988 | {158,188,228,146,214}, 989 | { 81,113,215, 24,211}, 990 | {223, 80,116,140,255}, 991 | {255,127, 35,243,255}, 992 | {208, 88,118,255,255}, 993 | {121, 91,154,255,255}, 994 | {255,255,255,255,255}, 995 | {255,255,255,255,255}}, 996 | { 997 | {255,255,255,255,255}, 998 | {255,255,255,255,255}, 999 | {209,142,149,163,251}, 1000 | {159, 96, 28,103,255}, 1001 | {245,108,120,225,253}, 1002 | {154, 15, 75, 62,239}, 1003 | {242,233,228,234,254}, 1004 | {255,255,255,255,255}, 1005 | {255,255,255,255,255}, 1006 | {255,255,255,255,255}}, 1007 | { 1008 | {255,255,244,249,255}, 1009 | {255,227, 70,216,255}, 1010 | {255,193, 93,255,255}, 1011 | {255,135,108,255,255}, 1012 | {255,117,112,255,255}, 1013 | {255,193, 99,255,255}, 1014 | {255,215, 57,224,255}, 1015 | {255,255,215,239,255}, 1016 | {255,255,255,255,255}, 1017 | {255,255,255,255,255}}, 1018 | { 1019 | {255,252,248,255,255}, 1020 | {255,200,122,255,255}, 1021 | {255,189, 96,255,255}, 1022 | {255,190, 99,255,255}, 1023 | {255,190, 99,255,255}, 1024 | {255,189, 98,255,255}, 1025 | {255,192,104,255,255}, 1026 | {255,245,229,255,255}, 1027 | {255,255,255,255,255}, 1028 | {255,255,255,255,255}}, 1029 | { 1030 | {255,240,255,255,255}, 1031 | {255, 99,159,255,255}, 1032 | {255,179,104,255,255}, 1033 | {255,197, 67,242,255}, 1034 | {255,201, 55,234,255}, 1035 | {255,187,105,255,255}, 1036 | {255,104,133,255,255}, 1037 | {254,210,247,255,255}, 1038 | {255,255,255,255,255}, 1039 | {255,255,255,255,255}}, 1040 | { 1041 | {255,255,255,255,255}, 1042 | {255,255,255,255,255}, 1043 | {255,239,255,255,255}, 1044 | {151, 50,122,122,233}, 1045 | {203,226,131,151,255}, 1046 | {255,255,255,255,255}, 1047 | {255,255,255,255,255}, 1048 | {255,255,255,255,255}, 1049 | {255,255,255,255,255}, 1050 | {255,255,255,255,255}}}; 1051 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ASCII ART 2 | 3 | C++ implementation of image to ASCII art conversion. 4 | 5 | The main functionality is in `ascii_art` in `ascii_art.h`. For general inputs 6 | I use `stb_image_resize.h` to resize the input. Otherwise, the only dependency 7 | is the standard library. 8 | 9 | There are three executable demos exposing this: 10 | 11 | ## `image2ascii` 12 | 13 | Convert an input image into ASCII art 14 | 15 | For example, if you issue: 16 | 17 | ./image2ascii ../max-schmeling.jpg 18 | 19 | You'll see: 20 | 21 | ``` 22 | 23 | 24 | ______ 25 | __qMMMMMMMMMMp___ 26 | _qMMMMMMMMMMMMMMMQQp_ 27 | _QMMMMMMMMMMQMMMMMMMQMQ_ 28 | _QMMMMMMMMMMMMMMMMMMMMMMQy 29 | qMMMMMME``` `""""#WMMMMMMQ#_ 30 | qMMMMMQE@` `@MMMMp 31 | qMMMMME@"` _@MMMp 32 | _qQMMMME"`__p_____ ___MW^ 33 | _qMD?,3ME"`@@@7_QMM@ qQMMMQ 34 | @MM qM@` 7 `` -"7WE@@ 35 | "QMp _E@` " -@ 36 | _QMM_ Q@_ "` @ 37 | qMMMMQpj@@ -__;) `_ 38 | @MMMMMMQp@" ` @__ _' 39 | @MMMMMMQ@_` ,=--,,,_@" j 40 | `7MMMMQMpjj` _qp_@-`j@_ 41 | "MMMMpMQpp__`````_pqp@@@`"-,_ _qMMMMMQp_ 42 | @MMMQp"7MQp___;_jQF```` `"--,__ _pQqqMMMMMMMMMMp_ 43 | MMMMM_ `7WWWMW^ __g@@@=--u__ MMMMMMMMMMMMMMMMp 44 | _QMMMMMMp__ _,.- -"@@@@"` `"@@_ MMMMMMMMMMMMMQMMp 45 | _qMMMMMMMMMMQ_ ``` ``"`` `MQp_ dMMMQQMMMMMQQQMMMp 46 | _qMMMMMMMMMMMMMp ,,` ``` `TMMp_MMMMQQ@QMMMMMMMMQ 47 | _qQMMMMMMMMMMMMMMMMp-"" ` "MMMMMMMQ@""@MMMMMMMMp 48 | __g@@qQMMMMMMMMMMMMMMMMMp _QMMMMMMQQp_@QQMMMMMM" 49 | _q@@@@@MMMMMMMMMQMMMMMMMMMp;. "MMMMMMQMMMQMQMMMMMM# 50 | #@@@@@qMMMMMMMMQQQQqQQMMM# MMMMMMMMMQMQMQMMMMM` 51 | _qQ@@@_qMMMMMMMMMMMQMMQQQME ` _` `9MMMMMMMMMMMMQMMMM 52 | gQ@@@qMMMMMMMMMMMMMMMMMW7`_p_` _p_ qMMQMMMMMQMMMMMMF 53 | q#M@@qMMMQMMQQMQMMMMMW" `=Q__ _jQp `MMMMQMQMMMQQME" 54 | qpE@@@MMMMMQMQMMMQQMb J@@` `@MMQ_ 7MMQMMMMMWQMM_ 55 | Q#Q@@@@@MMQ@FMQMMQMD j@@-` `"WWMQ_ `MMQ``7T7QMEMp 56 | QMQQQ@@qMME@j-:`WWE `@_` Mp_ q#M@";"@@@@@Mp 57 | qMpQQ@_QQQ@__j"` _@ _@@_ ____ 9Qp_ q#@@;`"@@@"jMMp 58 | "M#QE@QMME@@_ - -j@_ `j@@@` "@@@ ; `JMQp_ q#@`` _@@@@Q# 59 | q#Q@qMQQy""`_ ;@qp@`` "@@@@` `@QMMp_ qM@````""@Q@@Q# 60 | qMQQMME@j_-`@"%_qMp___ __; @"`"` ``` __@QMMMQp jqQ@__ ```_@QQQ# 61 | #QQMMEp@@"-"-`@@#ME@@_@@"` `;``` ```""@@MMMMMMp__#p@"`__j@@@QM# 62 | qMQMMME@_`""-_`j@#MMQ@@@j"` `;"` ```"@QMMMMMMMMpFMQp@j@@Q@@@Q# 63 | QMMMQM@@-@%""- jqMMQQ@@@@@@` `_`` ``"@Q"MMMMMMMMMQpQQ_QW@@@@Q` 64 | qMMMQQ@p@@j_ "-_@qQMMQ@@@@@@__- `--` ```"@Q_`@MMMMMMMMMQQQMMp@@@ 65 | _QMMMQQQ@@__""%_j@MMMMQQQ@@@@@@_` "``` ` ``""@Qp "MMMMMMMMMMMME"` 66 | qMMMQQQQ@@@_@"__@QQMQQMMQ@@@@@@____";`` ```"_@g_ `@MMMMMMMMMME` 67 | QMQQMQQQ@@@@@j@jQQKMMMQQQQQQ@@@@@@@j@___` ```` ___pqMQQ# `?MMMMMMME@ 68 | MMMMMMMQ@@@@j@@QQ# qMMQQQMQQQQQ@@@@_@p@@_ _______pQQMQMMMMMEMy `"7T"` 69 | "MMMQE@@@@@@@qQQK qMMQQQQQQQQQQ____QQppQqMMQMMQMMMMMM#QQpQWQQM#_ 70 | MMQp@@@@@@qQW" "MMMMMMMMMQQMQQQQQMEEMMMMQQQqqQWQ#QMQMMMMQQQQQp 71 | `MMMQQQ___QF` qMMMQMMMM#QMpQQQMQM##QMMQQQQMMMMM#QQQpQQMMMMMMQp_ 72 | 7MMMMQMMW" qMQQMQQMMMMMMp#MMQMMMMMQMMQQQMQMMMMQMMMMMMMMMMMMp 73 | `7WWF" qMMMQQQQQQQQQQQQQQQQQMMMMMMMMMMMMMMMMQQMMMMMMMMMMp_ 74 | qMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMQQMMQMMMMMMMMMMMM#_ 75 | _QMMMMMMMMMMMMMMQMMMMMMMMMMMMMMMMMMMMMMMMMMMMMQMMMMMMp 76 | qMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMQQQMMMMMMMMMMMMMMMMMMM_ 77 | QMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMQQMMMMMM# 78 | _QMMMMMMMMMMMMMMMMQMMMMMMMMMMMMMMMMMMMMMMMMMMQMMMMMMMMMM 79 | ``` 80 | 81 | ## `spy` 82 | 83 | Visualize a matrix similar to MATLAB's `spy` command, but as ASCII art. This 84 | command constructs the cotangent Laplacian of an input triangle mesh and 85 | visualizes this sparse matrix to the terminal: 86 | 87 | For example, if you issue: 88 | 89 | ./spy ../suzanne.obj 90 | 91 | You'll see: 92 | 93 | ``` 94 | ', _, ,.i . . ~`7^*==,i,_____ . i .__ `*=<___ .__ j,i. 95 | ', u ^"`77"`**~==CkiQ______ "``*~=k,___ 96 | ', `7W~~8W2,Q`j?j___" ' __" 7=JTQ@j_"" _ 97 | ', _ _ ``7"*>~=wci, j _ _ 7^%wmi 98 | ', `^*=a___,_u, , "w___, . 99 | ', `^v=L__` ` __ ," `^=_]"_ 100 | ', `^6AQ_g_ ^k__ 101 | ', `7*=aQ_x m_+ ` 102 | ', j& *-~ `7*w,_,- * ~ _ ^qp" - s"- 103 | ', `7*=upp_^ ` ^wx= 104 | ', __ `7^=uQ_^ 7wQt 105 | ', ``7">~~= A* _ " 117 | '!) ""7p } " - \ 0_<_, - \y 120 | _ _j b ___jQ< q\ 121 | \"p ) U_ ] j~ - ` ~5_y ^_ 122 | _ ; % - V d ; ~ / TR__ _, _,, 7 123 | b J_ " `Wg_ l \_" 124 | j ;1 b s ?WQ _ T,_ 125 | M ?__ ) " jp \. 126 | )[ \ j "y, ` Mp u _ \ ^ , r 127 | }b p " _* Mp_y V/ f 128 | q) # p _JR_ :_> y _ " 129 | ({ p ) ` < 9p ~ _ p` p ^ 130 | d " 3 [ " 8p___ b_C 131 | d y d -5Q` "Q 132 | _Rp =0 _ r .-"`Rg @ _ 133 | p ! p ) ? AQ E 134 | j # C q [ `_Y` Bp y^ y_6 135 | `# ? :p " ?` : 9p . @/ 136 | d y J3 p y* ?_ y 137 | V ?y 7Pe__ ', 138 | M__ " _ \ "` L7x__ ', 139 | l) k _ _Mm_ ', 140 | `4 ~ \ \ >~ ` "^e__ ', 141 | _F C\ _ , - .<=*q_ ', 142 | \.L _- \ \ _,- 7^m_ ', 143 | I _ j\ _ jw^"<_ ', 144 | ) L _ . \ `?<_ . ^` ', 145 | y Gg _ _ JD<_ ` ` ', 146 | Q) = fb \ =. .R[7w_ ', 147 | 'q "/_ *` " `b&_ ', 148 | ['L ")!p ( ` , ^ Te_ ', 149 | U v"_ p < ` P<__ ', 150 | j"p - T_ l L~ `^`~>q_ ', 151 | ``` 152 | 153 | ## `ascii3d` 154 | 155 | Ray trace an input 3D triangle mesh or 3D scene as ASCII art. 156 | 157 | For example if you issue, 158 | 159 | ./ascii3d ../suzanne.obj 160 | 161 | You'll see: 162 | 163 | ``` 164 | _____mmmmmmmmmm_____ 165 | __p@@@@"""""""""""""""@@@m__ 166 | _g@@@"`___`` `` ``___`"@@@p_ 167 | _d@@@@_j"` `%_ _/` `"@_@@@@@_ 168 | /@@@@j@` @_ _@ `%@j@@@h 169 | j@j@"` _;````";_ "_ _@ _;"````;_ `"@@@k 170 | -'___ _ _@` __@"`__, ,_ `- `""` ;" _ ,,__`"%__ `@_ _ ___'- 171 | , _QQQQp_^#@@"`` j@_-``-_% ` ` ;_-``"_@@ ```"@@"_qQQQQ__ \ 172 | @ j `7QpQ@@_ @@" j_ _) `@@ _j@QQp@` % _ 173 | ) |`` @@@Qp@@_;_"@@____@@ @@____y@/_;_j@qQE@@ ``j j 174 | @ ,``` @@@@QQp@__@_"R@@@`- `@@@R"_@"_jqQW@@@@ ```, j` 175 | "__~__"@@qQMQQp@j_@@""` `""@@__jqQMMQ@@@"__-"jF 176 | `%m___"@WMMMMMQQQpp____ ____jQQQQMMMMMK@"___p=" 177 | `77777``7FWMMQQQQQp__ __ __jQQQQQMMWF7```7777` 178 | 7WMQMME__ _pQMMQMW^ 179 | 3ME@"@mjjpF"@MMK` 180 | @@" "@@ 181 | _@@` `"@_ 182 | (@" `@] 183 | #@` `@@ 184 | ``````````````````````````` _@@` `"@p ``````````````````````````` 185 | ``````````````_________________q@"` _q__p_ `"@p_________________`````````````` 186 | ````"""""""%%@@@@@q@@___"_``_@____@p@@@@@@%""""""""``` 187 | ```````Wp__pppppppp__q#"``````` 188 | ``````77777`````` 189 | ``` 190 | 191 | This ray-tracer is **interactive** 192 | 193 | Try hitting the `hjklJK` keys to interact with the 3D scene: 194 | 195 | ![](suzanne-ascii3d.gif) 196 | 197 | ## Compilation 198 | 199 | mkdir build 200 | cd build 201 | cmake ../ -DCMAKE_BUILD_TYPE=Release 202 | make 203 | 204 | Demos depends on libigl, stb, Eigen, Embree. 205 | 206 | ## Note 207 | 208 | This implementation is hard-coded to work best with Courier New Bold. 209 | -------------------------------------------------------------------------------- /ascii3d.cpp: -------------------------------------------------------------------------------- 1 | #include "ascii_art.h" 2 | #include "terminal_size.h" 3 | //#include "Camera.h" 4 | //#include 5 | // 6 | // 7 | //int main(int argc, char *argv[]) 8 | //{ 9 | // Eigen::MatrixXd V; 10 | // Eigen::MatrixXi F; 11 | // igl::read_triangle_mesh(argv[1],V,F); 12 | // 13 | // const Eigen::RowVector3d center = 14 | // 0.5*(V.colwise().maxCoeff() + V.colwise().minCoeff()); 15 | // V.rowwise() -= center; 16 | // V /= V.rowwise().norm().maxCoeff(); 17 | // 18 | // Camera camera; 19 | // e = Eigen::RowVector3d(0,0,3); 20 | // 21 | //} 22 | 23 | #include "Object.h" 24 | #include "Camera.h" 25 | #include "Light.h" 26 | #include "read_json.h" 27 | #include "write_ppm.h" 28 | #include "viewing_ray.h" 29 | #include "raycolor.h" 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | // https://stackoverflow.com/questions/421860/capture-characters-from-standard-input-without-waiting-for-enter-to-be-pressed 39 | #include 40 | #include 41 | char getch() 42 | { 43 | char buf = 0; 44 | struct termios old = {0}; 45 | if (tcgetattr(0, &old) < 0) 46 | perror("tcsetattr()"); 47 | old.c_lflag &= ~ICANON; 48 | old.c_lflag &= ~ECHO; 49 | old.c_cc[VMIN] = 1; 50 | old.c_cc[VTIME] = 0; 51 | if (tcsetattr(0, TCSANOW, &old) < 0) 52 | perror("tcsetattr ICANON"); 53 | if (read(0, &buf, 1) < 0) 54 | perror ("read()"); 55 | old.c_lflag |= ICANON; 56 | old.c_lflag |= ECHO; 57 | if (tcsetattr(0, TCSADRAIN, &old) < 0) 58 | perror ("tcsetattr ~ICANON"); 59 | return (buf); 60 | } 61 | 62 | #define STB_IMAGE_WRITE_IMPLEMENTATION 63 | #include "stb/stb_image_write.h" 64 | 65 | int main(int argc, char * argv[]) 66 | { 67 | Camera camera; 68 | std::vector< std::shared_ptr > objects; 69 | std::vector< std::shared_ptr > lights; 70 | { 71 | std::string path = argv[1]; 72 | std::string extension; 73 | { 74 | std::string dir,base,file; 75 | igl::pathinfo(path,dir,base,extension,file); 76 | } 77 | if(extension == "json") 78 | { 79 | // Read a camera and scene description from given .json file 80 | read_json( 81 | argc<=1?"../data/sphere-and-plane.json":argv[1], 82 | camera, 83 | objects, 84 | lights); 85 | }else 86 | { 87 | 88 | 89 | // Otherwise, assume it's a mesh format 90 | std::shared_ptr soup(new TriangleSoup()); 91 | if(!igl::read_triangle_mesh(path,soup->V,soup->F)) { return EXIT_FAILURE; } 92 | const Eigen::RowVector3d center = 93 | 0.5*(soup->V.colwise().maxCoeff() + soup->V.colwise().minCoeff()); 94 | soup->V.rowwise() -= center; 95 | soup->V /= 2.0*soup->V.rowwise().norm().maxCoeff(); 96 | igl::per_vertex_normals(soup->V,soup->F,soup->N); 97 | //igl::per_face_normals(soup->V,soup->F,soup->N); 98 | soup->ei.init(soup->V.cast(),soup->F); 99 | objects.push_back(soup); 100 | // Default lights and floor and materials and camera 101 | std::shared_ptr plane(new Plane()); 102 | plane->point = soup->V.colwise().minCoeff().transpose(); 103 | plane->normal = Eigen::Vector3d(0,1,0); 104 | objects.push_back(plane); 105 | std::shared_ptr plane_mat(new Material()); 106 | plane_mat->ka = Eigen::Vector3d(0.7,0.7,0.7); 107 | plane_mat->kd = Eigen::Vector3d(0.2,0.2,0.2); 108 | plane_mat->ks = Eigen::Vector3d(0.0,0.0,0.0); 109 | plane_mat->km = Eigen::Vector3d(0.0,0.0,0.0); 110 | plane_mat->phong_exponent = 20; 111 | plane->material = plane_mat; 112 | std::shared_ptr soup_mat(new Material()); 113 | soup_mat->ka = Eigen::Vector3d(0.1,0.1,0.1); 114 | soup_mat->kd = Eigen::Vector3d(0.3,0.3,0.3); 115 | soup_mat->ks = Eigen::Vector3d(0.8,0.8,0.8); 116 | soup_mat->km = Eigen::Vector3d(0.0,0.0,0.0); 117 | soup_mat->phong_exponent = 1000; 118 | soup->material = soup_mat; 119 | const Eigen::MatrixXd light_directions = 120 | (Eigen::MatrixXd(3,3)<<0,-0.4,-1,-0.2,-0.2,-1,0.2,-0.2,-1).finished(); 121 | for(int l = 0;l light(new DirectionalLight()); 124 | light->I = Eigen::Vector3d(1,1,1); 125 | light->d = light_directions.row(l).transpose(); 126 | lights.push_back(light); 127 | } 128 | camera.e = Eigen::Vector3d(0,0,6); 129 | camera.v = Eigen::Vector3d(0,1,0); 130 | camera.w = -Eigen::Vector3d(0,0,-1); 131 | camera.u = camera.v.cross(camera.w); 132 | camera.height = 0.5; 133 | camera.d = 3; 134 | } 135 | } 136 | 137 | #include "CourierNewB.h" 138 | int target_cols,target_rows; 139 | terminal_size(target_cols,target_rows); 140 | 141 | Eigen::Matrix 142 | S(target_rows*CourierNewB_h,target_cols*CourierNewB_w); 143 | camera.width = camera.height*S.cols()/S.rows(); 144 | 145 | char * out_ascii = new char[target_cols * target_rows]; 146 | 147 | const auto draw_frame = [&]() 148 | { 149 | // For each pixel (i,j) 150 | for(unsigned i=0; i dir_light = std::dynamic_pointer_cast (light); 196 | if(dir_light) 197 | { 198 | dir_light->d = (Eigen::AngleAxisd(angle,axis)*dir_light->d).eval(); 199 | } 200 | } 201 | 202 | }; 203 | 204 | while(true) 205 | { 206 | draw_frame(); 207 | bool exit = false; 208 | const char key = getch(); 209 | switch(key) 210 | { 211 | case 'Q': 212 | case 'q': 213 | exit = true; 214 | break; 215 | case 'h': 216 | orbit( 1./60.*M_PI,Eigen::Vector3d::UnitY()); 217 | break; 218 | case 'l': 219 | orbit(-1./60.*M_PI,Eigen::Vector3d::UnitY()); 220 | break; 221 | case 'j': 222 | orbit(-1./60.*M_PI,camera.u); 223 | break; 224 | case 'k': 225 | orbit( 1./60.*M_PI,camera.u); 226 | break; 227 | case 'J': 228 | camera.e *= 0.80; 229 | break; 230 | case 'K': 231 | camera.e *= 1.25; 232 | break; 233 | default: 234 | printf("Unknown key: %c (%d)\n",key,int(key)); 235 | break; 236 | } 237 | if(exit) break; 238 | } 239 | 240 | 241 | delete[] out_ascii; 242 | } 243 | -------------------------------------------------------------------------------- /ascii_art.cpp: -------------------------------------------------------------------------------- 1 | #include "ascii_art.h" 2 | #define STB_IMAGE_RESIZE_IMPLEMENTATION 3 | #include "stb/stb_image_resize.h" 4 | 5 | 6 | void ascii_art( 7 | const unsigned char * in_pixels, 8 | const int in_w, 9 | const int in_h, 10 | /*const*/ int target_cols, 11 | /*const*/ int target_rows, 12 | unsigned char ** resize_pixels, 13 | int * resize_w, 14 | int * resize_h, 15 | char ** out_ascii, 16 | int * out_cols, 17 | int * out_rows, 18 | unsigned char ** out_pixels, 19 | int * out_w, 20 | int * out_h) 21 | { 22 | #include "CourierNewB.h" 23 | const double aspect_ratio = double(CourierNewB_w)/double(CourierNewB_h); 24 | if(target_rows == -1) 25 | { 26 | assert(target_cols > 0); 27 | target_rows = int(double(target_cols)*double(in_h)/double(in_w)*aspect_ratio); 28 | } 29 | else if(target_cols == -1) 30 | { 31 | assert(target_rows > 0); 32 | target_cols = int(double(target_rows)*double(in_w)/double(in_h)/aspect_ratio); 33 | } 34 | *resize_w = target_cols*CourierNewB_w; 35 | *resize_h = target_rows*CourierNewB_h; 36 | 37 | *resize_pixels = new unsigned char[*resize_w* *resize_h]; 38 | // Resize input to be perfectly the correct size 39 | stbir_resize_uint8( 40 | in_pixels,in_w,in_h,0, 41 | *resize_pixels,*resize_w,*resize_h,0,1); 42 | 43 | *out_w = *resize_w; 44 | *out_h = *resize_h; 45 | *out_pixels = new unsigned char[*out_w* *out_h]; 46 | 47 | *out_cols = target_cols; 48 | *out_rows = target_rows; 49 | *out_ascii = new char[*out_cols * *out_rows]; 50 | return ascii_art( 51 | *resize_pixels,*resize_w,*resize_h, 52 | *out_ascii,*out_cols,*out_rows,*out_pixels); 53 | } 54 | 55 | void ascii_art( 56 | const unsigned char * resize_pixels, 57 | const int resize_w, 58 | const int resize_h, 59 | char * out_ascii, 60 | const int out_cols, 61 | const int out_rows, 62 | unsigned char * out_pixels) 63 | { 64 | #include "CourierNewB.h" 65 | typedef int Scalar; 66 | const Scalar MAX_ERR = 255*255*CourierNewB_h*CourierNewB_w; 67 | // Loop over window)s 68 | // ut 69 | for(int y = 0;y < out_rows;y++) 70 | { 71 | for(int x = 0;x < out_cols;x++) 72 | { 73 | Scalar best_err = MAX_ERR; 74 | int best_z = -1; 75 | for(int z = 0;z=0); 100 | out_ascii[x+out_cols*y] = CourierNewB_str[best_z]; 101 | // loop within pixels of window 102 | for(int ci = 0;ci < CourierNewB_h;ci++) 103 | { 104 | for(int cj = 0;cj < CourierNewB_w;cj++) 105 | { 106 | const int i = ci + y*CourierNewB_h; 107 | const int j = cj + x*CourierNewB_w; 108 | unsigned char c = CourierNewB[best_z][ci][cj]; 109 | if(out_pixels) out_pixels[j + resize_w*i] = c; 110 | } 111 | } 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /ascii_art.h: -------------------------------------------------------------------------------- 1 | #ifndef ASCII_ART_H 2 | #define ASCII_ART_H 3 | // Inputs: 4 | // in_pixels pointer to grayscale input image 5 | // in_w width of input 6 | // in_h height of input 7 | // target_cols number of columns of output (-1 indicates maintain aspect ratio) 8 | // target_rows number of rows of output (-1 indicates maintain aspect ratio) 9 | // Outputs: 10 | // resize_pixels pointer to grayscale resized input image 11 | // resize_w width of resized input 12 | // resize_h height of resized input 13 | // out_ascii out_cols by out_rows array of ascii characters 14 | // out_cols number of output columns (==target_cols, unless target_cols=-1) 15 | // out_rows number of output columns (==target_rows, unless target_rows=-1) 16 | // out_pixels pointer to grayscale output (using cached text images) 17 | // out_w width of output 18 | // out_h height of output 19 | void ascii_art( 20 | const unsigned char * in_pixels, 21 | const int in_w, 22 | const int in_h, 23 | /*const*/ int target_cols, 24 | /*const*/ int target_rows, 25 | unsigned char ** resize_pixels, 26 | int * resize_w, 27 | int * resize_h, 28 | char ** out_ascii, 29 | int * out_cols, 30 | int * out_rows, 31 | unsigned char ** out_pixels, 32 | int * out_w, 33 | int * out_h); 34 | // Version with assuming pre-allocated output memory and perfect sizes. 35 | void ascii_art( 36 | const unsigned char * in_pixels, 37 | const int in_w, 38 | const int in_h, 39 | char * out_ascii, 40 | const int out_cols, 41 | const int out_rows, 42 | unsigned char * out_pixels); 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /cmake/FindLIBIGL.cmake: -------------------------------------------------------------------------------- 1 | # - Try to find the LIBIGL library 2 | # Once done this will define 3 | # 4 | # LIBIGL_FOUND - system has LIBIGL 5 | # LIBIGL_INCLUDE_DIR - **the** LIBIGL include directory 6 | if(LIBIGL_FOUND) 7 | return() 8 | endif() 9 | 10 | find_path(LIBIGL_INCLUDE_DIR igl/readOBJ.h 11 | HINTS 12 | ${LIBIGL_DIR} 13 | ENV LIBIGL_DIR 14 | PATHS 15 | ${CMAKE_SOURCE_DIR}/../.. 16 | ${CMAKE_SOURCE_DIR}/.. 17 | ${CMAKE_SOURCE_DIR} 18 | ${CMAKE_SOURCE_DIR}/libigl 19 | ${CMAKE_SOURCE_DIR}/../libigl 20 | ${CMAKE_SOURCE_DIR}/../../libigl 21 | /usr 22 | /usr/local 23 | /usr/local/igl/libigl 24 | PATH_SUFFIXES include 25 | ) 26 | 27 | include(FindPackageHandleStandardArgs) 28 | find_package_handle_standard_args(LIBIGL 29 | "\nlibigl not found --- You can download it using:\n\tgit clone https://github.com/libigl/libigl.git ${CMAKE_SOURCE_DIR}/../libigl" 30 | LIBIGL_INCLUDE_DIR) 31 | mark_as_advanced(LIBIGL_INCLUDE_DIR) 32 | 33 | list(APPEND CMAKE_MODULE_PATH "${LIBIGL_INCLUDE_DIR}/../cmake") 34 | include(libigl) 35 | -------------------------------------------------------------------------------- /image2ascii.cpp: -------------------------------------------------------------------------------- 1 | #include "ascii_art.h" 2 | #include "terminal_size.h" 3 | // https://stackoverflow.com/questions/23369503/get-size-of-terminal-window-rows-columns 4 | #include //ioctl() and TIOCGWINSZ 5 | #include // for STDOUT_FILENO 6 | #include 7 | #include 8 | 9 | void print_image( 10 | unsigned char * in_pixels, 11 | int in_w, 12 | int in_h, 13 | int in_n) 14 | { 15 | printf("input is %d by %d by %d \n",in_w,in_h,in_n); 16 | for(int i = 0;i1) 44 | { 45 | // Convert to grayscale (in place) 46 | assert(in_n>=3); 47 | for(int i = 0;i 6 | 7 | struct Camera 8 | { 9 | // Origin or "eye" 10 | Eigen::Vector3d e; 11 | // orthonormal frame so that -w is the viewing direction. 12 | Eigen::Vector3d u,v,w; 13 | // image plane distance / focal length 14 | double d; 15 | // width and height of image plane 16 | double width, height; 17 | }; 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /include/DirectionalLight.h: -------------------------------------------------------------------------------- 1 | #ifndef DIRECTIONALLIGHT_H 2 | #define DIRECTIONALLIGHT_H 3 | #include "Light.h" 4 | #include 5 | class DirectionalLight : public Light 6 | { 7 | public: 8 | // Direction _from_ light toward scene. 9 | Eigen::Vector3d d; 10 | // Given a query point return the direction _toward_ the Light. 11 | // 12 | // Input: 13 | // q 3D query point in space 14 | // Outputs: 15 | // d 3D direction from point toward light as a vector. 16 | // max_t parametric distance from q along d to light (may be inf) 17 | void direction( 18 | const Eigen::Vector3d & q, Eigen::Vector3d & d, double & max_t) const; 19 | }; 20 | #endif 21 | 22 | 23 | -------------------------------------------------------------------------------- /include/Light.h: -------------------------------------------------------------------------------- 1 | #ifndef LIGHT_H 2 | #define LIGHT_H 3 | #include 4 | class Light 5 | { 6 | public: 7 | // Color (intensities) 8 | Eigen::Vector3d I; 9 | // https://stackoverflow.com/questions/461203/when-to-use-virtual-destructors 10 | virtual ~Light() {}; 11 | // Given a query point return the direction _toward_ the Light. 12 | // 13 | // Input: 14 | // q 3D query point in space 15 | // Outputs: 16 | // d 3D direction from point toward light as a vector. 17 | // max_t parametric distance from q along d to light (may be inf) 18 | virtual void direction( 19 | const Eigen::Vector3d & q, 20 | Eigen::Vector3d & d, 21 | double & max_t) const =0; 22 | }; 23 | #endif 24 | -------------------------------------------------------------------------------- /include/Material.h: -------------------------------------------------------------------------------- 1 | #ifndef MATERIAL_H 2 | #define MATERIAL_H 3 | #include 4 | 5 | // Blinn-Phong Approximate Shading Material Parameters 6 | struct Material 7 | { 8 | // Ambient, Diffuse, Specular, Mirror Color 9 | Eigen::Vector3d ka,kd,ks,km; 10 | // Phong exponent 11 | double phong_exponent; 12 | }; 13 | #endif 14 | -------------------------------------------------------------------------------- /include/Object.h: -------------------------------------------------------------------------------- 1 | #ifndef OBJECT_H 2 | #define OBJECT_H 3 | 4 | #include "Material.h" 5 | #include 6 | #include 7 | 8 | struct Ray; 9 | class Object 10 | { 11 | public: 12 | std::shared_ptr material; 13 | // https://stackoverflow.com/questions/461203/when-to-use-virtual-destructors 14 | virtual ~Object() {} 15 | // Intersect object with ray. 16 | // 17 | // Inputs: 18 | // Ray ray to intersect with 19 | // min_t minimum parametric distance to consider 20 | // Outputs: 21 | // t first intersection at ray.origin + t * ray.direction 22 | // n surface normal at point of intersection 23 | // Returns iff there a first intersection is found. 24 | // 25 | // The funny = 0 just ensures that this function is defined (as a no-op) 26 | virtual bool intersect( 27 | const Ray & ray, const double min_t, double & t, Eigen::Vector3d & n) const = 0; 28 | }; 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /include/Plane.h: -------------------------------------------------------------------------------- 1 | #ifndef PLANE_H 2 | #define PLANE_H 3 | 4 | #include "Object.h" 5 | #include 6 | 7 | class Plane : public Object 8 | { 9 | public: 10 | // Point on plane 11 | Eigen::Vector3d point; 12 | // Normal of plane 13 | Eigen::Vector3d normal; 14 | // Intersect plane with ray. 15 | // 16 | // Inputs: 17 | // Ray ray to intersect with 18 | // min_t minimum parametric distance to consider 19 | // Outputs: 20 | // t first intersection at ray.origin + t * ray.direction 21 | // n surface normal at point of intersection 22 | // Returns iff there a first intersection is found. 23 | bool intersect( 24 | const Ray & ray, const double min_t, double & t, Eigen::Vector3d & n) const; 25 | }; 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /include/PointLight.h: -------------------------------------------------------------------------------- 1 | #ifndef POINTLIGHT_H 2 | #define POINTLIGHT_H 3 | #include "Light.h" 4 | #include 5 | class PointLight : public Light 6 | { 7 | public: 8 | Eigen::Vector3d p; 9 | // Given a query point return the direction _toward_ the Light. 10 | // 11 | // Input: 12 | // q 3D query point in space 13 | // Outputs: 14 | // d 3D direction from point toward light as a vector. 15 | // max_t parametric distance from q along d to light (may be inf) 16 | void direction( 17 | const Eigen::Vector3d & q, Eigen::Vector3d & d, double & max_t) const; 18 | }; 19 | #endif 20 | 21 | -------------------------------------------------------------------------------- /include/Ray.h: -------------------------------------------------------------------------------- 1 | #ifndef RAY_H 2 | #define RAY_H 3 | 4 | #include 5 | 6 | struct Ray 7 | { 8 | Eigen::Vector3d origin; 9 | // Not necessarily unit-length direction vector. (It is often useful to have 10 | // non-unit length so that origin+t*direction lands on a special point when 11 | // t=1.) 12 | Eigen::Vector3d direction; 13 | }; 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /include/Sphere.h: -------------------------------------------------------------------------------- 1 | #ifndef SPHERE_H 2 | #define SPHERE_H 3 | 4 | #include "Sphere.h" 5 | #include "Object.h" 6 | #include 7 | 8 | class Sphere : public Object 9 | { 10 | public: 11 | Eigen::Vector3d center; 12 | double radius; 13 | public: 14 | // Intersect sphere with ray. 15 | // 16 | // Inputs: 17 | // Ray ray to intersect with 18 | // min_t minimum parametric distance to consider 19 | // Outputs: 20 | // t first intersection at ray.origin + t * ray.direction 21 | // n surface normal at point of intersection 22 | // Returns iff there a first intersection is found. 23 | bool intersect( 24 | const Ray & ray, const double min_t, double & t, Eigen::Vector3d & n) const; 25 | }; 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /include/Triangle.h: -------------------------------------------------------------------------------- 1 | #ifndef TRIANGLE_H 2 | #define TRIANGLE_H 3 | 4 | #include "Object.h" 5 | #include 6 | 7 | class Triangle : public Object 8 | { 9 | public: 10 | // A triangle has three corners 11 | std::tuple< Eigen::Vector3d, Eigen::Vector3d, Eigen::Vector3d> corners; 12 | // Intersect a triangle with ray. 13 | // 14 | // Inputs: 15 | // Ray ray to intersect with 16 | // min_t minimum parametric distance to consider 17 | // Outputs: 18 | // t first intersection at ray.origin + t * ray.direction 19 | // n surface normal at point of intersection 20 | // Returns iff there a first intersection is found. 21 | bool intersect( 22 | const Ray & ray, const double min_t, double & t, Eigen::Vector3d & n) const; 23 | }; 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /include/TriangleSoup.h: -------------------------------------------------------------------------------- 1 | #ifndef TRIANGLE_SOUP_H 2 | #define TRIANGLE_SOUP_H 3 | 4 | #include "Object.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | // Forward declaration 11 | class TriangleSoup : public Object 12 | { 13 | public: 14 | igl::embree::EmbreeIntersector ei; 15 | Eigen::MatrixXd V; 16 | Eigen::MatrixXi F; 17 | Eigen::MatrixXd N; 18 | 19 | // Intersect a triangle soup with ray. 20 | // 21 | // Inputs: 22 | // Ray ray to intersect with 23 | // min_t minimum parametric distance to consider 24 | // Outputs: 25 | // t first intersection at ray.origin + t * ray.direction 26 | // n surface normal at point of intersection 27 | // Returns iff there a first intersection is found. 28 | bool intersect( 29 | const Ray & ray, const double min_t, double & t, Eigen::Vector3d & n) const; 30 | }; 31 | 32 | #endif 33 | 34 | -------------------------------------------------------------------------------- /include/blinn_phong_shading.h: -------------------------------------------------------------------------------- 1 | #ifndef BLINN_PHONG_SHADING_H 2 | #define BLINN_PHONG_SHADING_H 3 | #include "Ray.h" 4 | #include "Light.h" 5 | #include "Object.h" 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | // Given a ray and its hit in the scene, return the Blinn-Phong shading 12 | // contribution over all _visible_ light sources (e.g., take into account 13 | // shadows). Use a hard-coded value of ia=0.1 for ambient light. 14 | // 15 | // Inputs: 16 | // ray incoming ray 17 | // hit_id index into objects of the object just hit by ray 18 | // t _parametric_ distance along ray to hit 19 | // n unit surface normal at hit 20 | // objects list of objects in the scene 21 | // lights list of lights in the scene 22 | // Returns shaded color collected by this ray as rgb 3-vector 23 | Eigen::Vector3d blinn_phong_shading( 24 | const Ray & ray, 25 | const int & hit_id, 26 | const double & t, 27 | const Eigen::Vector3d & n, 28 | const std::vector< std::shared_ptr > & objects, 29 | const std::vector > & lights); 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /include/dirname.h: -------------------------------------------------------------------------------- 1 | // This file is part of libigl, a simple c++ geometry processing library. 2 | // 3 | // Copyright (C) 2013 Alec Jacobson 4 | // 5 | // This Source Code Form is subject to the terms of the Mozilla Public License 6 | // v. 2.0. If a copy of the MPL was not distributed with this file, You can 7 | // obtain one at http://mozilla.org/MPL/2.0/. 8 | #ifndef IGL_DIRNAME_H 9 | #define IGL_DIRNAME_H 10 | #define IGL_INLINE inline 11 | #include 12 | 13 | namespace igl 14 | { 15 | // Function like PHP's dirname: /etc/passwd --> /etc, 16 | // Input: 17 | // path string containing input path 18 | // Returns string containing dirname (see php's dirname) 19 | // 20 | // See also: basename, pathinfo 21 | IGL_INLINE std::string dirname(const std::string & path); 22 | } 23 | // 24 | // This file is part of libigl, a simple c++ geometry processing library. 25 | // 26 | // Copyright (C) 2013 Alec Jacobson 27 | // 28 | // This Source Code Form is subject to the terms of the Mozilla Public License 29 | // v. 2.0. If a copy of the MPL was not distributed with this file, You can 30 | // obtain one at http://mozilla.org/MPL/2.0/. 31 | 32 | #include 33 | 34 | IGL_INLINE std::string igl::dirname(const std::string & path) 35 | { 36 | if(path == "") 37 | { 38 | return std::string(""); 39 | } 40 | #if defined (WIN32) 41 | char del('\\'); 42 | #else 43 | char del('/'); 44 | #endif 45 | // http://stackoverflow.com/questions/5077693/dirnamephp-similar-function-in-c 46 | std::string::const_reverse_iterator last_slash = 47 | std::find( 48 | path.rbegin(), 49 | path.rend(),del); 50 | if( last_slash == path.rend() ) 51 | { 52 | // No slashes found 53 | return std::string("."); 54 | }else if(1 == (last_slash.base() - path.begin())) 55 | { 56 | // Slash is first char 57 | return std::string(&del); 58 | }else if(path.end() == last_slash.base() ) 59 | { 60 | // Slash is last char 61 | std::string redo = std::string(path.begin(),path.end()-1); 62 | return igl::dirname(redo); 63 | } 64 | return std::string(path.begin(),last_slash.base()-1); 65 | } 66 | 67 | 68 | 69 | #endif 70 | 71 | -------------------------------------------------------------------------------- /include/first_hit.h: -------------------------------------------------------------------------------- 1 | #ifndef FIRST_HIT_H 2 | #define FIRST_HIT_H 3 | 4 | #include "Ray.h" 5 | #include "Object.h" 6 | #include 7 | #include 8 | #include 9 | 10 | // Find the first (visible) hit given a ray and a collection of scene objects 11 | // 12 | // Inputs: 13 | // ray ray along which to search 14 | // min_t minimum t value to consider (for viewing rays, this is typically at 15 | // least the _parametric_ distance of the image plane to the camera) 16 | // objects list of objects (shapes) in the scene 17 | // Outputs: 18 | // hit_id index into objects of object with first hit 19 | // t _parametric_ distance along ray so that ray.origin+t*ray.direction is 20 | // the hit location 21 | // n surface normal at hit location 22 | // Returns true iff a hit was found 23 | bool first_hit( 24 | const Ray & ray, 25 | const double min_t, 26 | const std::vector< std::shared_ptr > & objects, 27 | int & hit_id, 28 | double & t, 29 | Eigen::Vector3d & n); 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /include/raycolor.h: -------------------------------------------------------------------------------- 1 | #ifndef RAYCOLOR_H 2 | #define RAYCOLOR_H 3 | #include "Ray.h" 4 | #include "Object.h" 5 | #include "Light.h" 6 | #include 7 | #include 8 | 9 | // Shoot a ray into a lit scene and collect color information. 10 | // 11 | // Inputs: 12 | // ray ray along which to search 13 | // min_t minimum t value to consider (for viewing rays, this is typically at 14 | // least the _parametric_ distance of the image plane to the camera) 15 | // objects list of objects (shapes) in the scene 16 | // lights list of lights in the scene 17 | // num_recursive_calls how many times has raycolor been called already 18 | // Outputs: 19 | // rgb collected color 20 | // Returns true iff a hit was found 21 | bool raycolor( 22 | const Ray & ray, 23 | const double min_t, 24 | const std::vector< std::shared_ptr > & objects, 25 | const std::vector< std::shared_ptr > & lights, 26 | const int num_recursive_calls, 27 | Eigen::Vector3d & rgb); 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /include/readSTL.h: -------------------------------------------------------------------------------- 1 | // This file is part of libigl, a simple c++ geometry processing library. 2 | // 3 | // Copyright (C) 2013 Alec Jacobson 4 | // 5 | // This Source Code Form is subject to the terms of the Mozilla Public License 6 | // v. 2.0. If a copy of the MPL was not distributed with this file, You can 7 | // obtain one at http://mozilla.org/MPL/2.0/. 8 | #ifndef IGL_READSTL_H 9 | #define IGL_READSTL_H 10 | # define IGL_INLINE inline 11 | 12 | #ifndef IGL_NO_EIGEN 13 | # include 14 | #endif 15 | #include 16 | #include 17 | #include 18 | 19 | namespace igl 20 | { 21 | // Inputs: 22 | // stl_file pointer to already opened .stl file 23 | // Outputs: 24 | // stl_file closed file 25 | template 26 | IGL_INLINE bool readSTL( 27 | FILE * stl_file, 28 | std::vector > & V, 29 | std::vector > & F, 30 | std::vector > & N); 31 | template 32 | IGL_INLINE bool readSTL( 33 | const std::string & filename, 34 | std::vector > & V, 35 | std::vector > & F, 36 | std::vector > & N); 37 | } 38 | 39 | #include 40 | 41 | template 42 | IGL_INLINE bool igl::readSTL( 43 | const std::string & filename, 44 | std::vector > & V, 45 | std::vector > & F, 46 | std::vector > & N) 47 | { 48 | using namespace std; 49 | // Should test for ascii 50 | 51 | // Open file, and check for error 52 | FILE * stl_file = fopen(filename.c_str(),"rb"); 53 | if(NULL==stl_file) 54 | { 55 | fprintf(stderr,"IOError: %s could not be opened...\n", 56 | filename.c_str()); 57 | return false; 58 | } 59 | return readSTL(stl_file,V,F,N); 60 | } 61 | 62 | template 63 | IGL_INLINE bool igl::readSTL( 64 | FILE * stl_file, 65 | std::vector > & V, 66 | std::vector > & F, 67 | std::vector > & N) 68 | { 69 | using namespace std; 70 | //stl_file = freopen(NULL,"rb",stl_file); 71 | if(NULL==stl_file) 72 | { 73 | fprintf(stderr,"IOError: stl file could not be reopened as binary (1) ...\n"); 74 | return false; 75 | } 76 | 77 | V.clear(); 78 | F.clear(); 79 | N.clear(); 80 | 81 | 82 | // Specifically 80 character header 83 | char header[80]; 84 | char solid[80]; 85 | bool is_ascii = true; 86 | if(fread(header,1,80,stl_file) != 80) 87 | { 88 | cerr<<"IOError: too short (1)."<(buf); 106 | fseek(stl_file,0,SEEK_END); 107 | int file_size = ftell(stl_file); 108 | if(file_size == 80 + 4 + (4*12 + 2) * num_faces) 109 | { 110 | is_ascii = false; 111 | }else 112 | { 113 | is_ascii = true; 114 | } 115 | } 116 | 117 | if(is_ascii) 118 | { 119 | // Rewind to end of header 120 | //stl_file = fopen(filename.c_str(),"r"); 121 | //stl_file = freopen(NULL,"r",stl_file); 122 | fseek(stl_file, 0, SEEK_SET); 123 | if(NULL==stl_file) 124 | { 125 | fprintf(stderr,"IOError: stl file could not be reopened as ascii ...\n"); 126 | return false; 127 | } 128 | // Read 80 header 129 | // Eat file name 130 | #ifndef IGL_LINE_MAX 131 | # define IGL_LINE_MAX 2048 132 | #endif 133 | char name[IGL_LINE_MAX]; 134 | if(NULL==fgets(name,IGL_LINE_MAX,stl_file)) 135 | { 136 | cerr<<"IOError: ascii too short (2)."< n(3); 145 | double nd[3]; 146 | ret = fscanf(stl_file,"%s %s %lg %lg %lg",facet,normal,nd,nd+1,nd+2); 147 | if(string("endsolid") == facet) 148 | { 149 | break; 150 | } 151 | if(ret != 5 || 152 | !(string("facet") == facet || 153 | string("faced") == facet) || 154 | string("normal") != normal) 155 | { 156 | cout<<"facet: "< f; 172 | while(true) 173 | { 174 | char word[IGL_LINE_MAX]; 175 | int ret = fscanf(stl_file,"%s",word); 176 | if(ret == 1 && string("endloop") == word) 177 | { 178 | break; 179 | }else if(ret == 1 && string("vertex") == word) 180 | { 181 | vector v(3); 182 | double vd[3]; 183 | int ret = fscanf(stl_file,"%lg %lg %lg",vd,vd+1,vd+2); 184 | if(ret != 3) 185 | { 186 | cerr<<"IOError: bad format (3)."<(3,0)); 230 | N.resize(num_tri,vector(3,0)); 231 | F.resize(num_tri,vector(3,0)); 232 | for(int t = 0;t<(int)num_tri;t++) 233 | { 234 | // Read normal 235 | float n[3]; 236 | if(fread(n,sizeof(float),3,stl_file)!=3) 237 | { 238 | cerr<<"IOError: bad format (8)."<, Eigen::Matrix, Eigen::Matrix >(std::basic_string, std::allocator > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); 278 | // generated by autoexplicit.sh 279 | template bool igl::readSTL, Eigen::Matrix, Eigen::Matrix >(std::basic_string, std::allocator > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); 280 | // generated by autoexplicit.sh 281 | template bool igl::readSTL, Eigen::Matrix, Eigen::Matrix >(std::basic_string, std::allocator > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); 282 | // generated by autoexplicit.sh 283 | template bool igl::readSTL, Eigen::Matrix, Eigen::Matrix >(std::basic_string, std::allocator > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); 284 | template bool igl::readSTL, Eigen::Matrix, Eigen::Matrix >(std::basic_string, std::allocator > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); 285 | template bool igl::readSTL, Eigen::Matrix, Eigen::Matrix >(std::basic_string, std::allocator > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); 286 | #endif 287 | 288 | 289 | #endif 290 | 291 | 292 | -------------------------------------------------------------------------------- /include/read_json.h: -------------------------------------------------------------------------------- 1 | #ifndef READ_JSON_H 2 | #define READ_JSON_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | // Forward declaration 9 | struct Object; 10 | struct Light; 11 | 12 | // Read a scene description from a .json file 13 | // 14 | // Input: 15 | // filename path to .json file 16 | // Output: 17 | // camera camera looking at the scene 18 | // objects list of shared pointers to objects 19 | // lights list of shared pointers to lights 20 | inline bool read_json( 21 | const std::string & filename, 22 | Camera & camera, 23 | std::vector > & objects, 24 | std::vector > & lights); 25 | 26 | // Implementation 27 | 28 | #include 29 | #include "readSTL.h" 30 | #include "dirname.h" 31 | #include "Object.h" 32 | #include "Sphere.h" 33 | #include "Plane.h" 34 | #include "Triangle.h" 35 | #include "TriangleSoup.h" 36 | #include "Light.h" 37 | #include "PointLight.h" 38 | #include "DirectionalLight.h" 39 | #include "Material.h" 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | 48 | inline bool read_json( 49 | const std::string & filename, 50 | Camera & camera, 51 | std::vector > & objects, 52 | std::vector > & lights) 53 | { 54 | // Heavily borrowing from 55 | // https://github.com/yig/graphics101-raycasting/blob/master/parser.cpp 56 | using json = nlohmann::json; 57 | 58 | std::ifstream infile( filename ); 59 | if( !infile ) return false; 60 | json j; 61 | infile >> j; 62 | 63 | 64 | // parse a vector 65 | auto parse_Vector3d = [](const json & j) -> Eigen::Vector3d 66 | { 67 | return Eigen::Vector3d(j[0],j[1],j[2]); 68 | }; 69 | // parse camera 70 | auto parse_camera = 71 | [&parse_Vector3d](const json & j, Camera & camera) 72 | { 73 | assert(j["type"] == "perspective" && "Only handling perspective cameras"); 74 | camera.d = j["focal_length"].get(); 75 | camera.e = parse_Vector3d(j["eye"]); 76 | camera.v = parse_Vector3d(j["up"]).normalized(); 77 | camera.w = -parse_Vector3d(j["look"]).normalized(); 78 | camera.u = camera.v.cross(camera.w); 79 | camera.height = j["height"].get(); 80 | camera.width = j["width"].get(); 81 | }; 82 | parse_camera(j["camera"],camera); 83 | 84 | // Parse materials 85 | std::unordered_map > materials; 86 | auto parse_materials = [&parse_Vector3d]( 87 | const json & j, 88 | std::unordered_map > & materials) 89 | { 90 | materials.clear(); 91 | for(const json & jmat : j) 92 | { 93 | std::string name = jmat["name"]; 94 | std::shared_ptr material(new Material()); 95 | material->ka = parse_Vector3d(jmat["ka"]); 96 | material->kd = parse_Vector3d(jmat["kd"]); 97 | material->ks = parse_Vector3d(jmat["ks"]); 98 | material->km = parse_Vector3d(jmat["km"]); 99 | material->phong_exponent = jmat["phong_exponent"]; 100 | materials[name] = material; 101 | } 102 | }; 103 | parse_materials(j["materials"],materials); 104 | 105 | auto parse_lights = [&parse_Vector3d]( 106 | const json & j, 107 | std::vector > & lights) 108 | { 109 | lights.clear(); 110 | for(const json & jlight : j) 111 | { 112 | if(jlight["type"] == "directional") 113 | { 114 | std::shared_ptr light(new DirectionalLight()); 115 | light->d = parse_Vector3d(jlight["direction"]).normalized(); 116 | light->I = parse_Vector3d(jlight["color"]); 117 | lights.push_back(light); 118 | }else if(jlight["type"] == "point") 119 | { 120 | std::shared_ptr light(new PointLight()); 121 | light->p = parse_Vector3d(jlight["position"]); 122 | light->I = parse_Vector3d(jlight["color"]); 123 | lights.push_back(light); 124 | } 125 | } 126 | }; 127 | parse_lights(j["lights"],lights); 128 | 129 | auto parse_objects = [&parse_Vector3d,&filename,&materials]( 130 | const json & j, 131 | std::vector > & objects) 132 | { 133 | objects.clear(); 134 | for(const json & jobj : j) 135 | { 136 | if(jobj["type"] == "sphere") 137 | { 138 | std::shared_ptr sphere(new Sphere()); 139 | sphere->center = parse_Vector3d(jobj["center"]); 140 | sphere->radius = jobj["radius"].get(); 141 | objects.push_back(sphere); 142 | }else if(jobj["type"] == "plane") 143 | { 144 | std::shared_ptr plane(new Plane()); 145 | plane->point = parse_Vector3d(jobj["point"]); 146 | plane->normal = parse_Vector3d(jobj["normal"]).normalized(); 147 | objects.push_back(plane); 148 | }else if(jobj["type"] == "triangle") 149 | { 150 | std::shared_ptr tri(new Triangle()); 151 | tri->corners = std::make_tuple( 152 | parse_Vector3d(jobj["corners"][0]), 153 | parse_Vector3d(jobj["corners"][1]), 154 | parse_Vector3d(jobj["corners"][2])); 155 | objects.push_back(tri); 156 | }else if(jobj["type"] == "soup") 157 | { 158 | std::shared_ptr soup(new TriangleSoup()); 159 | { 160 | #if defined(WIN32) || defined(_WIN32) 161 | #define PATH_SEPARATOR std::string("\\") 162 | #else 163 | #define PATH_SEPARATOR std::string("/") 164 | #endif 165 | const std::string stl_path = jobj["stl"]; 166 | igl::read_triangle_mesh( 167 | igl::dirname(filename)+ 168 | PATH_SEPARATOR + 169 | stl_path, 170 | soup->V,soup->F); 171 | } 172 | igl::per_vertex_normals(soup->V,soup->F,soup->N); 173 | //igl::per_face_normals(soup->V,soup->F,soup->N); 174 | soup->ei.init(soup->V.cast(),soup->F); 175 | objects.push_back(soup); 176 | } 177 | //objects.back()->material = default_material; 178 | if(jobj.count("material")) 179 | { 180 | if(materials.count(jobj["material"])) 181 | { 182 | objects.back()->material = materials[jobj["material"]]; 183 | } 184 | } 185 | } 186 | }; 187 | parse_objects(j["objects"],objects); 188 | 189 | return true; 190 | } 191 | 192 | #endif 193 | -------------------------------------------------------------------------------- /include/reflect.h: -------------------------------------------------------------------------------- 1 | #ifndef REFLECT_H 2 | #define REFLECT_H 3 | #include 4 | // Reflect an incoming ray into an out going ray 5 | // 6 | // Inputs: 7 | // in incoming _unit_ ray direction 8 | // n surface _unit_ normal about which to reflect 9 | // Returns outward _unit_ ray direction 10 | Eigen::Vector3d reflect(const Eigen::Vector3d & in, const Eigen::Vector3d & n); 11 | #endif 12 | -------------------------------------------------------------------------------- /include/viewing_ray.h: -------------------------------------------------------------------------------- 1 | #ifndef VIEWING_RAY_H 2 | #define VIEWING_RAY_H 3 | 4 | #include "Ray.h" 5 | #include "Camera.h" 6 | 7 | // Construct a viewing ray given a camera and subscripts to a pixel 8 | // 9 | // Inputs: 10 | // camera Perspective camera object 11 | // i pixel row index 12 | // j pixel column index 13 | // width number of pixels width of image 14 | // height number of pixels height of image 15 | // Outputs: 16 | // ray viewing ray starting at camera shooting through pixel. When t=1, then 17 | // ray.origin + t*ray.direction should land exactly on the center of the 18 | // pixel (i,j) 19 | void viewing_ray( 20 | const Camera & camera, 21 | const int i, 22 | const int j, 23 | const int width, 24 | const int height, 25 | Ray & ray); 26 | #endif 27 | -------------------------------------------------------------------------------- /include/write_ppm.h: -------------------------------------------------------------------------------- 1 | #ifndef WRITE_PPM_H 2 | #define WRITE_PPM_H 3 | 4 | #include 5 | #include 6 | 7 | // Write an rgb or grayscale image to a .ppm file. 8 | // 9 | // Inputs: 10 | // filename path to .ppm file as string 11 | // data width*heigh*num_channels array of image intensity data 12 | // width image width (i.e., number of columns) 13 | // height image height (i.e., number of rows) 14 | // num_channels number of channels (e.g., for rgb 3, for grayscale 1) 15 | // Returns true on success, false on failure (e.g., can't open file) 16 | bool write_ppm( 17 | const std::string & filename, 18 | const std::vector & data, 19 | const int width, 20 | const int height, 21 | const int num_channels); 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /max-schmeling.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alecjacobson/ascii3d/1da60511cb30fadf0cfb2c073629d5722e544426/max-schmeling.jpg -------------------------------------------------------------------------------- /spy.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "ascii_art.h" 3 | #include "terminal_size.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | void spy( 10 | const Eigen::SparseMatrix & L, 11 | const int w, 12 | const int h, 13 | Eigen::Matrix & S) 14 | { 15 | Eigen::MatrixXd Sd = Eigen::MatrixXd::Zero(h,w); 16 | // Iterate over outside of A 17 | for(int k=0; k::InnerIterator it (L,k); it; ++it) 21 | { 22 | Sd(it.row()*h/L.rows(),it.col()*w/L.cols()) = 1; 23 | } 24 | } 25 | const double m = Sd.maxCoeff(); 26 | S = (255.0 - Sd.array()*255.0/m).cast(); 27 | }; 28 | 29 | 30 | 31 | int main(int argc, char *argv[]) 32 | { 33 | Eigen::MatrixXd V; 34 | Eigen::MatrixXi F; 35 | igl::read_triangle_mesh(argv[1],V,F); 36 | Eigen::SparseMatrix L; 37 | igl::cotmatrix(V,F,L); 38 | 39 | int target_cols; 40 | int target_rows; 41 | terminal_size(L.cols(),L.rows(),target_cols,target_rows); 42 | Eigen::Matrix S; 43 | 44 | #include "CourierNewB.h" 45 | spy(L,target_cols*CourierNewB_w,target_rows*CourierNewB_h,S); 46 | 47 | //unsigned char * out_pixels = new unsigned char[S.cols() * S.rows()]; 48 | char * out_ascii = new char[target_cols * target_rows]; 49 | 50 | ascii_art(S.data(),S.cols(),S.rows(),out_ascii,target_cols,target_rows,NULL); 51 | 52 | for(int i = 0;i 3 | 4 | void DirectionalLight::direction( 5 | const Eigen::Vector3d & q, Eigen::Vector3d & d, double & max_t) const 6 | { 7 | // q is ignored. 8 | d = -this->d.normalized(); 9 | max_t = std::numeric_limits::infinity(); 10 | } 11 | 12 | -------------------------------------------------------------------------------- /src/Plane.cpp: -------------------------------------------------------------------------------- 1 | #include "Plane.h" 2 | #include "Ray.h" 3 | #include 4 | bool Plane::intersect( 5 | const Ray & ray, const double min_t, double & t, Eigen::Vector3d & n) const 6 | { 7 | // plane equation (q-p)⋅n = 0 8 | // subsititue (o+t*d-p)⋅n = 0 9 | // solve for -(o-p)⋅n/(d⋅n) 10 | t = -(ray.origin - point).dot(normal) / (ray.direction.dot(normal)); 11 | if(t>=min_t) 12 | { 13 | n = normal; 14 | return true; 15 | } 16 | return false; 17 | } 18 | 19 | -------------------------------------------------------------------------------- /src/PointLight.cpp: -------------------------------------------------------------------------------- 1 | #include "PointLight.h" 2 | 3 | void PointLight::direction( 4 | const Eigen::Vector3d & q, Eigen::Vector3d & d, double & max_t) const 5 | { 6 | d = p-q; 7 | max_t = 1.0; 8 | } 9 | -------------------------------------------------------------------------------- /src/Sphere.cpp: -------------------------------------------------------------------------------- 1 | #include "Sphere.h" 2 | #include "Ray.h" 3 | 4 | bool Sphere::intersect( 5 | const Ray & ray, const double min_t, double & t, Eigen::Vector3d & n) const 6 | { 7 | const double A = ray.direction.squaredNorm(); 8 | const Eigen::Vector3d disp = ray.origin - center; 9 | const double B = 2.*ray.direction.dot(disp); 10 | const double C = disp.squaredNorm() - radius*radius; 11 | double discriminant = B*B - 4.*A*C; 12 | if(discriminant <= 0) 13 | { 14 | return false; 15 | } 16 | // smaller of the two solutions 17 | t = (-B - sqrt(discriminant))/(2.*A); 18 | if(t < min_t) 19 | { 20 | // we're inside the sphere. Use larger option. 21 | t = (-B + sqrt(discriminant))/(2.*A); 22 | } 23 | if(t < min_t) 24 | { 25 | // we're still too close 26 | return false; 27 | } 28 | const Eigen::Vector3d hit = ray.origin + t * ray.direction; 29 | n = (hit-center)/radius; 30 | return true; 31 | } 32 | 33 | -------------------------------------------------------------------------------- /src/Triangle.cpp: -------------------------------------------------------------------------------- 1 | #include "Triangle.h" 2 | #include "Ray.h" 3 | #include 4 | #include 5 | 6 | bool Triangle::intersect( 7 | const Ray & ray, const double min_t, double & t, Eigen::Vector3d & n) const 8 | { 9 | const auto A = std::get<0>(corners); 10 | const auto B = std::get<1>(corners); 11 | const auto C = std::get<2>(corners); 12 | //// e + td = a + β(b − a) + γ(c − a), 13 | //// td + β(b − a) + γ(c − a) = a-e 14 | //// [d , (b − a) , (c − a)] [t;β;γ] = a-e 15 | //Eigen::Matrix3d M; 16 | //M << ray.direction , B-A, C-A; 17 | //const Eigen::Vector3d t_beta_gamma = M.inverse() * (A - ray.origin); 18 | //t = t_beta_gamma(0); 19 | //const double beta = t_beta_gamma(1); 20 | //const double gamma = t_beta_gamma(2); 21 | 22 | 23 | // Using Marschner and Shirley's notation 24 | const double a = A(0)-B(0); 25 | const double b = A(1)-B(1); 26 | const double c = A(2)-B(2); 27 | const double d = A(0)-C(0); 28 | const double e = A(1)-C(1); 29 | const double f = A(2)-C(2); 30 | const double g = ray.direction(0); 31 | const double h = ray.direction(1); 32 | const double i = ray.direction(2); 33 | const double j = A(0)-ray.origin(0); 34 | const double k = A(1)-ray.origin(1); 35 | const double l = A(2)-ray.origin(2); 36 | 37 | const double M = a*(e*i-h*f) + b*(g*f-d*i) + c*(d*h - e*g); 38 | t = -(f*(a*k-j*b)+e*(j*c-a*l) + d*(b*l - k*c) )/M; 39 | 40 | if(t1) 46 | { 47 | return false; 48 | } 49 | const double beta = (j*(e*i - h*f) +k*(g*f-d*i) + l*(d*h-e*g))/M; 50 | if(beta<0 || beta>(1.0-gamma)) 51 | { 52 | return false; 53 | } 54 | // Could further be replaced with computations above 55 | n = ((A-B).cross(A-C)).normalized(); 56 | return true; 57 | } 58 | 59 | 60 | -------------------------------------------------------------------------------- /src/TriangleSoup.cpp: -------------------------------------------------------------------------------- 1 | #include "TriangleSoup.h" 2 | #include "Triangle.h" 3 | #include "first_hit.h" 4 | #include 5 | bool TriangleSoup::intersect( 6 | const Ray & ray, const double min_t, double & t, Eigen::Vector3d & n) const 7 | { 8 | igl::Hit hit; 9 | if(!ei.intersectRay( 10 | ray.origin.cast().transpose(), 11 | ray.direction.cast().transpose(), 12 | hit, 13 | 1e-4)) 14 | { 15 | return false; 16 | } 17 | t = hit.t; 18 | if(N.rows() == F.rows()) 19 | { 20 | n = N.row(hit.id).transpose(); 21 | }else 22 | { 23 | n = 24 | ((1.0-hit.u-hit.v)*N.row(F(hit.id,0))+ 25 | hit.u*N.row(F(hit.id,1))+ 26 | hit.v*N.row(F(hit.id,2))).normalized().transpose(); 27 | } 28 | 29 | return true; 30 | } 31 | -------------------------------------------------------------------------------- /src/blinn_phong_shading.cpp: -------------------------------------------------------------------------------- 1 | #include "blinn_phong_shading.h" 2 | // Hint: 3 | #include "first_hit.h" 4 | #include 5 | 6 | Eigen::Vector3d blinn_phong_shading( 7 | const Ray & ray, 8 | const int & hit_id, 9 | const double & t, 10 | const Eigen::Vector3d & n, 11 | const std::vector< std::shared_ptr > & objects, 12 | const std::vector > & lights) 13 | { 14 | const double epsilon = 1e-6; 15 | const auto material = objects[hit_id]->material; 16 | // Point of hit 17 | const Eigen::Vector3d p = ray.origin + t*ray.direction; 18 | const double ia = 1.0; 19 | Eigen::Vector3d rgb = ia*material->ka; 20 | for(auto light : lights) 21 | { 22 | // In a hard shadow? 23 | Ray shadow_ray; 24 | shadow_ray.origin = p; 25 | double max_shadow_t; 26 | light->direction(p,shadow_ray.direction,max_shadow_t); 27 | { 28 | int shadow_hit_id; 29 | double shadow_t; 30 | Eigen::Vector3d shadow_n; 31 | // getting self hits... 32 | if(!first_hit(shadow_ray,epsilon,objects,shadow_hit_id,shadow_t,shadow_n) || shadow_t > max_shadow_t ) 33 | { 34 | // light directxion 35 | const Eigen::Vector3d l = shadow_ray.direction.normalized(); 36 | const Eigen::Vector3d h = 37 | (l-ray.direction.normalized()).normalized(); 38 | rgb.array() += 39 | material->kd.array() * light->I.array() * 40 | std::max(0.0,n.dot(l)) + 41 | material->ks.array() * light->I.array() * 42 | std::pow(std::max(0.0,n.dot(h)),material->phong_exponent); 43 | } 44 | } 45 | } 46 | return rgb; 47 | } 48 | -------------------------------------------------------------------------------- /src/first_hit.cpp: -------------------------------------------------------------------------------- 1 | #include "first_hit.h" 2 | 3 | bool first_hit( 4 | const Ray & ray, 5 | const double min_t, 6 | const std::vector< std::shared_ptr > & objects, 7 | int & hit_id, 8 | double & t, 9 | Eigen::Vector3d & n) 10 | { 11 | t = std::numeric_limits::max(); 12 | bool found_any = false; 13 | for(int i = 0;iintersect(ray,min_t,t_i,n_i) && t_i < t) 18 | { 19 | t = t_i; 20 | n = n_i; 21 | hit_id = i; 22 | found_any = true; 23 | } 24 | } 25 | return found_any; 26 | } 27 | -------------------------------------------------------------------------------- /src/raycolor.cpp: -------------------------------------------------------------------------------- 1 | #include "raycolor.h" 2 | #include "first_hit.h" 3 | #include "blinn_phong_shading.h" 4 | #include "reflect.h" 5 | 6 | bool raycolor( 7 | const Ray & ray, 8 | const double min_t, 9 | const std::vector< std::shared_ptr > & objects, 10 | const std::vector< std::shared_ptr > & lights, 11 | const int num_recursive_calls, 12 | Eigen::Vector3d & rgb) 13 | { 14 | const double epsilon = 1e-6; 15 | int hit_id; 16 | double t; 17 | Eigen::Vector3d n; 18 | if(first_hit(ray,min_t,objects,hit_id,t,n)) 19 | { 20 | rgb = blinn_phong_shading(ray,hit_id,t,n,objects,lights); 21 | return true; 22 | } 23 | return false; 24 | } 25 | -------------------------------------------------------------------------------- /src/reflect.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | Eigen::Vector3d reflect(const Eigen::Vector3d & in, const Eigen::Vector3d & n) 4 | { 5 | // 6 | // Λ 7 | // Λ | / 8 | // \ | / 9 | // out \ |n / in 10 | // \ | / 11 | // \ | / 12 | // \|v 13 | // ========O======== 14 | // 15 | // OR equivalently... 16 | // 17 | // Λ 18 | // Λ | 19 | // \ | 20 | // out \ |n 21 | // \ | 22 | // \ | 23 | // \| 24 | // ========O======== 25 | // /| 26 | // / | 27 | // in / | (in⋅n)n 28 | // / | 29 | // / | 30 | // v v 31 | return in - 2.0*(in.dot(n))*n; 32 | } 33 | -------------------------------------------------------------------------------- /src/viewing_ray.cpp: -------------------------------------------------------------------------------- 1 | #include "viewing_ray.h" 2 | 3 | void viewing_ray( 4 | const Camera & camera, 5 | const int i, 6 | const int j, 7 | const int width, 8 | const int height, 9 | Ray & ray) 10 | { 11 | double u = -camera.width*0.5 + camera.width*(j+0.5)/double(width); 12 | // We want +y pointing up 13 | double v = -(-camera.height*0.5 + camera.height*(i+0.5)/double(height)); 14 | ray.origin = camera.e; 15 | ray.direction = -camera.d*camera.w + u*camera.u + v*camera.v; 16 | } 17 | -------------------------------------------------------------------------------- /src/write_ppm.cpp: -------------------------------------------------------------------------------- 1 | #include "write_ppm.h" 2 | #include 3 | #include 4 | #include 5 | 6 | bool write_ppm( 7 | const std::string & filename, 8 | const std::vector & data, 9 | const int width, 10 | const int height, 11 | const int num_channels) 12 | { 13 | assert( 14 | (num_channels == 3 || num_channels ==1 ) && 15 | ".ppm only supports RGB or grayscale images"); 16 | std::ofstream s(filename); 17 | if(!s.is_open()) 18 | { 19 | std::cerr<<"Failed to open "< //ioctl() and TIOCGWINSZ 5 | #include // for STDOUT_FILENO 6 | 7 | // Given an in_w by in_h image determine how many columns and rows of text will 8 | // fit to the current terminal screen. 9 | // 10 | // Inputs: 11 | // in_w width of input image 12 | // in_h height of input image 13 | // Outputs: 14 | // target_cols fit-to-screen number of columns 15 | // target_rows fit-to-screen number of rows 16 | // Returns true iff could determine screen size 17 | bool terminal_size( 18 | const int in_w, 19 | const int in_h, 20 | int & target_cols, 21 | int & target_rows) 22 | { 23 | // Get size of terminal window 24 | struct winsize size; 25 | ioctl(STDOUT_FILENO, TIOCGWINSZ, &size); 26 | // Minus one for the command line 27 | int nrows = size.ws_row-1; 28 | int ncols = size.ws_col; 29 | 30 | if(ncols == 0 || nrows == 0) 31 | { 32 | target_cols = 80; 33 | // Let ascii_art figure out the number of rows to use 34 | target_rows = -1; 35 | return false; 36 | } 37 | 38 | #include "CourierNewB.h" 39 | const double aspect_ratio = double(CourierNewB_w)/double(CourierNewB_h); 40 | target_cols = ncols; 41 | target_rows = int(double(target_cols)*double(in_h)/double(in_w)*aspect_ratio); 42 | if(target_rows>nrows) 43 | { 44 | target_rows = nrows; 45 | target_cols = int(double(target_rows)*double(in_w)/double(in_h)/aspect_ratio); 46 | } 47 | return true; 48 | } 49 | 50 | bool terminal_size( 51 | int & target_cols, 52 | int & target_rows) 53 | { 54 | // Get size of terminal window 55 | struct winsize size; 56 | ioctl(STDOUT_FILENO, TIOCGWINSZ, &size); 57 | // Minus one for the command line 58 | target_rows = size.ws_row-1; 59 | target_cols = size.ws_col; 60 | if(target_cols == 0 || target_rows == 0) 61 | { 62 | target_cols = 80; 63 | // Let ascii_art figure out the number of rows to use 64 | target_rows = -1; 65 | return false; 66 | } 67 | return true; 68 | } 69 | #endif 70 | --------------------------------------------------------------------------------