diff --git a/CMakeLists.txt b/CMakeLists.txt index e151704..f4e6029 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,6 +43,9 @@ target_link_libraries(cornell_box PUBLIC t) add_executable(teapot src/teapot.cpp) target_link_libraries(teapot PUBLIC t) +add_executable(teapot2 src/teapot2.cpp) +target_link_libraries(teapot2 PUBLIC t) + add_executable(triangle src/triangle.cpp) target_link_libraries(triangle PUBLIC t) diff --git a/README.md b/README.md index f72133f..f85c0a0 100644 --- a/README.md +++ b/README.md @@ -105,6 +105,8 @@ ctest original Utah teapot (3488 tris) with normal material, with an FPS counter at the top-left (might not be visible due to the output being constantly overwritten); useful for testing performance. +- [`teapot2.cpp`](src/teapot2.cpp): a blue Utah teapot with ambient lighting and + a point light traveling in a circle. Showcases specular highlights; - [`triangle.cpp`](src/triangle.cpp): a simple triangle printed to the terminal screen. Demonstrates how to set up a custom 3D geometry. diff --git a/src/teapot.cpp b/src/teapot.cpp index 6c67a9b..2f5bf26 100644 --- a/src/teapot.cpp +++ b/src/teapot.cpp @@ -12,10 +12,8 @@ int main() { mesh.translate(0, -1.3, 0); - // const auto width = 80; - // const auto height = 24; - const auto width = 213; - const auto height = 56; + const auto width = 80; + const auto height = 24; const auto fwidth = static_cast(width); const auto fheight = static_cast(height); @@ -63,8 +61,6 @@ int main() { auto g = renderTarget.texture.image[index + 1]; auto b = renderTarget.texture.image[index + 2]; - const auto luminance = t::Color(r, g, b).luminance(); - r = std::clamp(r, 0.0, 1.0); g = std::clamp(g, 0.0, 1.0); b = std::clamp(b, 0.0, 1.0); diff --git a/src/teapot2.cpp b/src/teapot2.cpp new file mode 100644 index 0000000..25b6fab --- /dev/null +++ b/src/teapot2.cpp @@ -0,0 +1,103 @@ +#include "t.hpp" +#include +#include +#include + +using namespace t; + +int main() { + auto teapot = UtahTeapot(); + // auto material = NormalColor(); + auto material = BlinnPhong(Color(0, 0, 244), Color(255, 255, 255), 128); + auto mesh = Mesh(teapot, material); + + auto ambient = AmbientLight(Color(64, 64, 64), 1); + auto light = PointLight(Color(255, 255, 255), 1); + light.translate(0, 1, 0); + + mesh.translate(0, -1.3, 0); + + const auto width = 80; + const auto height = 24; + const auto fwidth = static_cast(width); + const auto fheight = static_cast(height); + + auto scene = Scene(); + auto camera = PerspectiveCamera(M_PI / 4, fwidth / fheight, 0.0001, 100); + camera.translate(0, 0, 2.5); + + scene.add(ambient); + scene.add(light); + scene.add(mesh); + scene.add(camera); + + auto renderer = Rasterizer(); + auto renderTarget = + RenderTarget(width, height, TextureFormat::RgbDouble); + + const auto characters = std::array{'.', ',', '-', '~', ':', ';', + '=', '!', '*', '#', '$', '@'}; + + auto startTime = std::chrono::high_resolution_clock::now(); + long lastRenderTimestamp = 0; + + while (true) { + auto currentTime = std::chrono::high_resolution_clock::now(); + auto elapsedMs = std::chrono::duration_cast( + currentTime - startTime) + .count(); + std::cout << "\x1b[H"; + + if (elapsedMs > 0) { + std::cout << "FPS: " << std::setfill(' ') << std::setw(3) + << 1000 / (elapsedMs - lastRenderTimestamp); + } + + // mesh.localRotation.y = 0.001 * elapsedMs; + light.localPosition.x = cos(0.001 * elapsedMs) * 2; + light.localPosition.z = sin(0.001 * elapsedMs) * 2; + + lastRenderTimestamp = elapsedMs; + + renderer.render(scene, camera, renderTarget); + + for (int j = 0; j < renderTarget.height; j++) { + for (int i = renderTarget.width - 1; i > -1; i--) { + if (j == 0 && i > renderTarget.width - 8) { + continue; + } + + int index = (i + j * renderTarget.width) * 3; + + auto r = renderTarget.texture.image[index]; + auto g = renderTarget.texture.image[index + 1]; + auto b = renderTarget.texture.image[index + 2]; + + const auto luminance = t::Color(r, g, b).luminance(); + + r = std::clamp(r, 0.0, 1.0); + g = std::clamp(g, 0.0, 1.0); + b = std::clamp(b, 0.0, 1.0); + + char c = ' '; + + for (int cs = 11; cs > -1; cs--) { + if ((1.0 / 12) * cs < luminance) { + c = characters[cs]; + break; + } + } + + std::cout << "\033[38;2;" << static_cast(r * 255) << ";" + << static_cast(g * 255) << ";" + << static_cast(b * 255) << "m" << c << "\033[0m"; + } + + if (j < renderTarget.height - 1) { + std::cout << "\n"; + } + } + } + + return 0; +} \ No newline at end of file