From 43714bd116945573e7e4c854445462afa7f9b1b4 Mon Sep 17 00:00:00 2001 From: Keuin Date: Mon, 11 Apr 2022 22:13:57 +0800 Subject: Implement ray3, timer and a simple viewport scanner. Fix bitmap wrong pixel sequence. Remove default constructor of bitmap. Add pixel mixture method. --- CMakeLists.txt | 5 +++-- bitmap.h | 41 +++++++++++++++++++++++++++++++-- main_simple_scanner.cpp | 60 +++++++++++++++++++++++++++++++++++++++++++++++++ ray.h | 36 +++++++++++++++++++++++++++++ timer.h | 27 ++++++++++++++++++++++ 5 files changed, 165 insertions(+), 4 deletions(-) create mode 100644 main_simple_scanner.cpp create mode 100644 ray.h create mode 100644 timer.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b822baa..b5a00d4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,12 +12,13 @@ set(CMAKE_VERBOSE_MAKEFILE on) # main executable -add_executable(rt main.cpp vec.h bitmap.h) +add_executable(rt main.cpp vec.h bitmap.h ray.h) add_executable(image_output main_image_output.cpp vec.h bitmap.h) +add_executable(simple_scanner main_simple_scanner.cpp vec.h bitmap.h ray.h timer.h) # googletest -add_executable(all_tests test.cpp vec.h bitmap.h) +add_executable(all_tests test.cpp vec.h bitmap.h ray.h) target_link_libraries(all_tests gtest_main) include(FetchContent) diff --git a/bitmap.h b/bitmap.h index b94bb25..31b9053 100644 --- a/bitmap.h +++ b/bitmap.h @@ -10,13 +10,48 @@ #include #include +#define COLORMIX_OVERFLOW_CHECK + +//// T is some unsigned integer +//template +//T saturate_add(T a, T b) { +// T c = a + b; +// if (a > c || b > c) { +// c = (1ULL << (sizeof(T) * 8U)) - 1ULL; +// } +// return c; +//} // T is some unsigned integer, do not use float point types! template struct pixel { T r, g, b; + + /** + * Create a pixel with given depth, from normalized color values. + * For example: for 8bit pixel, with (1, 0.5, 0.25), we get: (255, 127, 63). + */ + static pixel from_normalized(double r, double g, double b) { + const auto mod = (1ULL << (sizeof(T) * 8U)) - 1ULL; + return pixel{.r = (T) (mod * r), .g = (T) (mod * g), .b = (T) (mod * b)}; + } }; +// Mix two colors a and b. Returns a*u + b*v +template +inline pixel mix(const pixel &a, const pixel &b, double u, double v) { + assert(u >= 0); + assert(v >= 0); + assert(u <= 1); + assert(v <= 1); + assert(u + v <= 1); + pixel c{0, 0, 0}; + c.r = (T) (u * a.r + v * b.r); + c.g = (T) (u * a.g + v * b.g); + c.b = (T) (u * a.b + v * b.b); + return c; +} + // 8 bit pixel using pixel8b = pixel; @@ -28,16 +63,18 @@ class bitmap { pixel &image(unsigned x, unsigned y) { assert(x < width); assert(y < height); - return content[x * width + y]; + return content[x + y * width]; } pixel &image(unsigned x, unsigned y) const { assert(x < width); assert(y < height); - return content[x * width + y]; + return content[x + y * width]; } public: + bitmap() = delete; + bitmap(unsigned int width, unsigned int height) : width(width), height(height) { content.resize(width * height, pixel{}); } diff --git a/main_simple_scanner.cpp b/main_simple_scanner.cpp new file mode 100644 index 0000000..a1d75f7 --- /dev/null +++ b/main_simple_scanner.cpp @@ -0,0 +1,60 @@ +// +// Created by Keuin on 2022/4/11. +// + +#include +#include + +#include "vec.h" +#include "ray.h" +#include "bitmap.h" +#include "timer.h" + + +class viewport { + uint16_t width, height; + vec3d center; + bitmap8b bitmap_{width, height}; + + static pixel8b color(const ray3d &r) { + const auto u = (r.direction().y + 1.0) * 0.5; + return mix( + pixel8b::from_normalized(1.0, 1.0, 1.0), + pixel8b::from_normalized(0.5, 0.7, 1.0), + 1.0 - u, + u + ); + } + +public: + viewport() = delete; + + viewport(uint16_t width, uint16_t height, vec3d viewport_center) : + width(width), height(height), center(viewport_center) {} + + void render(vec3d viewpoint) { + const auto r = center - viewpoint; + const int half_width = width / 2, half_height = height / 2; + for (int j = -half_height; j < half_height; ++j) { + for (int i = -half_width; i < half_width; ++i) { + const auto dir = r + vec3d{static_cast(i), static_cast(j), 0}; + const ray3d ray{viewpoint, dir}; // from camera to pixel (on the viewport) + const auto pixel = color(ray); + bitmap_.set(i + half_width, j + half_height, pixel); + } + } + } + + const bitmap8b &bitmap() const { + return bitmap_; + } +}; + +int main() { + viewport vp{1920, 1080, vec3d{0, 0, -100}}; + timer tm; + tm.start_measure(); + vp.render(vec3d{0, 0, 0}); // camera position as the coordinate origin + tm.stop_measure(); + vp.bitmap().write_plain_ppm(std::cout); +} \ No newline at end of file diff --git a/ray.h b/ray.h new file mode 100644 index 0000000..3281c6e --- /dev/null +++ b/ray.h @@ -0,0 +1,36 @@ +// +// Created by Keuin on 2022/4/11. +// + +#ifndef RT_RAY_H +#define RT_RAY_H + +#include "vec.h" + +// A ray is a half-line, starts from a 3d point, along the direction of a unit vector, to the infinity +template +class ray3 { + const vec3 source_; + const vec3 direction_; // unit vector + +public: + ray3() = delete; + ray3(const vec3 &source, const vec3 &direction) : source_(source), direction_(direction.unit_vec()) {} + + vec3 source() const { + return source_; + } + + vec3 direction() const { + return direction_; + } + + template + vec3 at(U t) const { + return source_ + direction_ * t; + } +}; + +using ray3d = ray3; + +#endif //RT_RAY_H diff --git a/timer.h b/timer.h new file mode 100644 index 0000000..1f100cd --- /dev/null +++ b/timer.h @@ -0,0 +1,27 @@ +// +// Created by Keuin on 2022/4/11. +// + +#ifndef RT_TIMER_H +#define RT_TIMER_H + +#include +#include + +class timer { +private: + typeof(std::chrono::system_clock::now()) start_time; +public: + void start_measure() { + fprintf(stderr, "Start timing...\n"); + start_time = std::chrono::system_clock::now(); + } + void stop_measure() { + const auto end = std::chrono::system_clock::now(); + const auto duration = std::chrono::duration_cast(end - start_time); + const auto secs = 1e-3 * duration.count() * std::chrono::microseconds::period::num / std::chrono::microseconds::period::den; + fprintf(stderr, "Stop timing. Duration: %fs\n", secs); + } +}; + +#endif //RT_TIMER_H -- cgit v1.2.3