From cdff5d73ceaa269bc1d262d6f7b6fdda794de56b Mon Sep 17 00:00:00 2001 From: Kartik Agaram Date: Sun, 4 Oct 2020 23:12:21 -0700 Subject: 6952 - raytracing: much better The image is now visually indistinguishable from the baseline, though the file isn't quite bit-for-bit correct. I found 3 bugs: a) I forgot to normalize the ray. After creating a helper to "automatically" do it for me, it turns out said helper requires manually using. b) I forgot to multiply by t at one place. c) vec3-length was half-written. For the umpteenth time, the bugs were all in the last place I looked. I was worried about spending a lot of time transcribing `main` without any feedback, but that turned out to be perfect. --- apps/raytracing/3.mu | 86 ++++++++++++++++++++++++++++++++++++++- apps/raytracing/color.h | 15 +++++++ apps/raytracing/main.cc | 104 ++++++++++++++++++++++++++++++++++++++++++++++++ apps/raytracing/ray.h | 25 ++++++++++++ apps/raytracing/vec3.h | 102 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 330 insertions(+), 2 deletions(-) create mode 100644 apps/raytracing/color.h create mode 100644 apps/raytracing/main.cc create mode 100644 apps/raytracing/ray.h create mode 100644 apps/raytracing/vec3.h diff --git a/apps/raytracing/3.mu b/apps/raytracing/3.mu index 24d31040..359b5c9b 100644 --- a/apps/raytracing/3.mu +++ b/apps/raytracing/3.mu @@ -10,7 +10,16 @@ fn ray-color _in: (addr ray), _out: (addr rgb) { var in/esi: (addr ray) <- copy _in var out/edi: (addr rgb) <- copy _out var dir/eax: (addr vec3) <- get in, dir - var y-addr/eax: (addr float) <- get dir, y +#? print-string 0, "r.dir: " +#? print-vec3 0, dir +#? print-string 0, "\n" + var unit-storage: vec3 + var unit/ecx: (addr vec3) <- address unit-storage + vec3-unit dir, unit +#? print-string 0, "r.dir normalized: " +#? print-vec3 0, unit +#? print-string 0, "\n" + var y-addr/eax: (addr float) <- get unit, y # t = (dir.y + 1.0) / 2.0 var t/xmm0: float <- copy *y-addr var one/eax: int <- copy 1 @@ -19,6 +28,9 @@ fn ray-color _in: (addr ray), _out: (addr rgb) { var two/eax: int <- copy 2 var two-f/xmm2: float <- convert two t <- divide two-f +#? print-string 0, "t: " +#? print-float 0, t +#? print-string 0, "\n" # whitening = (1.0 - t) * white var whitening-storage: rgb var whitening/ecx: (addr rgb) <- address whitening-storage @@ -26,6 +38,9 @@ fn ray-color _in: (addr ray), _out: (addr rgb) { var one-minus-t/xmm3: float <- copy one-f one-minus-t <- subtract t rgb-scale-up whitening, one-minus-t +#? print-string 0, "whitening: " +#? print-rgb-raw 0, whitening +#? print-string 0, "\n" # out = t * (0.5, 0.7, 1.0) var dest/eax: (addr float) <- get out, r fill-in-rational dest, 5, 0xa @@ -33,8 +48,15 @@ fn ray-color _in: (addr ray), _out: (addr rgb) { fill-in-rational dest, 7, 0xa dest <- get out, b copy-to *dest, one-f + rgb-scale-up out, t +#? print-string 0, "base: " +#? print-rgb-raw 0, out +#? print-string 0, "\n" # blend with whitening rgb-add-to out, whitening +#? print-string 0, "result: " +#? print-rgb-raw 0, out +#? print-string 0, "\n" } fn main -> exit-status/ebx: int { @@ -45,6 +67,14 @@ fn main -> exit-status/ebx: int { var aspect: float var aspect-addr/eax: (addr float) <- address aspect fill-in-rational aspect-addr, 0x10, 9 # 16/9 +#? print-string 0, "aspect ratio: " +#? print-float 0, aspect +#? print-string 0, " " +#? { +#? var foo2/ebx: int <- reinterpret aspect +#? print-int32-hex 0, foo2 +#? } +#? print-string 0, "\n" # camera @@ -52,9 +82,29 @@ fn main -> exit-status/ebx: int { var tmp/eax: int <- copy 2 var two-f/xmm4: float <- convert tmp var viewport-height/xmm7: float <- copy two-f +#? print-string 0, "viewport height: " +#? print-float 0, viewport-height +#? print-string 0, " " +#? { +#? var foo: float +#? copy-to foo, viewport-height +#? var foo2/ebx: int <- reinterpret foo +#? print-int32-hex 0, foo2 +#? } +#? print-string 0, "\n" # viewport-width = aspect * viewport-height var viewport-width/xmm6: float <- convert tmp viewport-width <- multiply aspect +#? print-string 0, "viewport width: " +#? print-float 0, viewport-width +#? print-string 0, " " +#? { +#? var foo: float +#? copy-to foo, viewport-width +#? var foo2/ebx: int <- reinterpret foo +#? print-int32-hex 0, foo2 +#? } +#? print-string 0, "\n" # focal-length = 1.0 tmp <- copy 1 var focal-length/xmm5: float <- convert tmp @@ -119,6 +169,9 @@ fn main -> exit-status/ebx: int { # u = i / (image-width - 1) var u/xmm0: float <- convert i u <- divide image-width-1 +#? print-string 0, "u: " +#? print-float 0, u +#? print-string 0, "\n" # v = j / (image-height - 1) var v/xmm1: float <- convert j v <- divide image-height-1 @@ -147,6 +200,9 @@ fn main -> exit-status/ebx: int { vec3-add-to dest, tmp # . r.dir -= origin vec3-subtract-from dest, origin +#? print-string 0, "ray direction: " +#? print-vec3 0, dest +#? print-string 0, "\n" } # pixel-color = ray-color(r) var c-storage: rgb @@ -226,6 +282,20 @@ fn print-rgb screen: (addr screen), _c: (addr rgb) { print-string screen, "\n" } +fn print-rgb-raw screen: (addr screen), _v: (addr rgb) { + var v/esi: (addr rgb) <- copy _v + print-string screen, "(" + var tmp/eax: (addr float) <- get v, r + print-float screen, *tmp + print-string screen, ", " + tmp <- get v, g + print-float screen, *tmp + print-string screen, ", " + tmp <- get v, b + print-float screen, *tmp + print-string screen, ")" +} + fn rgb-white _c: (addr rgb) { var c/esi: (addr rgb) <- copy _c var one/eax: int <- copy 1 @@ -377,6 +447,9 @@ fn vec3-scale-down _v1: (addr vec3), f: float { fn vec3-unit in: (addr vec3), out: (addr vec3) { var len/xmm0: float <- vec3-length in +#? print-string 0, "len: " +#? print-float 0, len +#? print-string 0, "\n" copy-object in, out vec3-scale-down out, len } @@ -393,5 +466,14 @@ fn vec3-length-squared _v: (addr vec3) -> result/xmm0: float { var tmp/xmm1: float <- copy *src tmp <- multiply tmp result <- copy tmp - # + # result += v.y * v.y + src <- get v, y + tmp <- copy *src + tmp <- multiply tmp + result <- add tmp + # result += v.z * v.z + src <- get v, z + tmp <- copy *src + tmp <- multiply tmp + result <- add tmp } diff --git a/apps/raytracing/color.h b/apps/raytracing/color.h new file mode 100644 index 00000000..a8a34b26 --- /dev/null +++ b/apps/raytracing/color.h @@ -0,0 +1,15 @@ +#ifndef COLOR_H +#define COLOR_H + +#include "vec3.h" + +#include + +void write_color(std::ostream &out, color pixel_color) { + // Write the translated [0,255] value of each color component. + out << static_cast(255.999 * pixel_color.x()) << ' ' + << static_cast(255.999 * pixel_color.y()) << ' ' + << static_cast(255.999 * pixel_color.z()) << '\n'; +} + +#endif diff --git a/apps/raytracing/main.cc b/apps/raytracing/main.cc new file mode 100644 index 00000000..8ebfc08f --- /dev/null +++ b/apps/raytracing/main.cc @@ -0,0 +1,104 @@ +#include + +// print float in some sort of intuitive hex that also helps visualize the underlying bits +void p(std::ostream &out, float f) { + int bits = *(int*)&f; + // sign + if (bits & 0x80000000) { + out << '-'; + } + // mantissa + int mantissa = bits & 0x007fffff; + int exponent = (bits & 0x7f800000) >> 23; + out << std::hex << mantissa << "P" << std::dec << (exponent-127); +} + +#include "color.h" +#include "ray.h" +#include "vec3.h" + +color ray_color(const ray& r) { +//? std::cerr << "r.dir: " << r.direction() << '\n'; +//? std::cerr << "r.dir length: "; +//? p(std::cerr, r.direction().length()); +//? std::cerr << '\n'; + vec3 unit_direction = unit_vector(r.direction()); +//? std::cerr << "r.dir normalized: " << unit_direction << '\n'; + float t = 0.5*(unit_direction.y() + 1.0); +//? std::cerr << "t: "; +//? p(std::cerr, t); +//? std::cerr << '\n'; + vec3 whitening = (1.0-t)*color(1.0, 1.0, 1.0); +//? std::cerr << "whitening: "; +//? p(std::cerr, whitening.x()); +//? std::cerr << " "; +//? p(std::cerr, whitening.y()); +//? std::cerr << " "; +//? p(std::cerr, whitening.z()); +//? std::cerr << "\n"; + vec3 base = t*color(0.5, 0.7, 1.0); +//? std::cerr << "base: "; +//? p(std::cerr, base.x()); +//? std::cerr << " "; +//? p(std::cerr, base.y()); +//? std::cerr << " "; +//? p(std::cerr, base.z()); +//? std::cerr << "\n"; + vec3 result = base + whitening; +//? std::cerr << "result: "; +//? p(std::cerr, result.x()); +//? std::cerr << " "; +//? p(std::cerr, result.y()); +//? std::cerr << " "; +//? p(std::cerr, result.z()); +//? std::cerr << "\n"; + return result; +} + +int main() { + + // Image + const float aspect_ratio = 16.0 / 9.0; +//? std::cerr << "aspect ratio: " << aspect_ratio << ' ' << std::hex << *(int*)&aspect_ratio << '\n'; + const int image_width = 400; + const int image_height = static_cast(image_width / aspect_ratio); + + // Camera + + float viewport_height = 2.0; +//? std::cerr << "viewport height: " << viewport_height << ' ' << std::hex << *(int*)&viewport_height << '\n'; + float viewport_width = aspect_ratio * viewport_height; +//? std::cerr << "viewport width: " << viewport_width << ' ' << std::hex << *(int*)&viewport_width << '\n'; + float focal_length = 1.0; + + auto origin = point3(0, 0, 0); + auto horizontal = vec3(viewport_width, 0, 0); + auto vertical = vec3(0, viewport_height, 0); + auto lower_left_corner = origin - horizontal/2 - vertical/2 - vec3(0, 0, focal_length); + + // Render + + std::cout << "P3\n" << image_width << " " << image_height << "\n255\n"; + + for (int j = image_height-1; j >= 0; --j) { +//? std::cerr << "\rScanlines remaining: " << j << ' ' << std::flush; + for (int i = 0; i < image_width; ++i) { + auto u = float(i) / (image_width-1); +//? std::cerr << "u: " << u << '\n'; + auto v = float(j) / (image_height-1); + ray r(origin, lower_left_corner + u*horizontal + v*vertical - origin); +//? std::cerr << "ray origin: " << r.orig.x() << " " << r.orig.y() << " " << r.orig.z() << '\n'; +//? std::cerr << "ray direction: " << r.dir.x() << " " << r.dir.y() << " " << r.dir.z() << '\n'; +//? std::cerr << "ray dir.x: " << r.dir.x() << " "; +//? p(std::cerr, r.dir.x()); +//? std::cerr << '\n'; + color pixel_color = ray_color(r); +//? std::cerr << "pixel color: " << pixel_color.x() << " " << pixel_color.y() << " " << pixel_color.z() << '\n'; + write_color(std::cout, pixel_color); +//? break; + } +//? break; + } + +//? std::cerr << "\r"; +} diff --git a/apps/raytracing/ray.h b/apps/raytracing/ray.h new file mode 100644 index 00000000..ed65168a --- /dev/null +++ b/apps/raytracing/ray.h @@ -0,0 +1,25 @@ +#ifndef RAY_H +#define RAY_H + +#include "vec3.h" + +class ray { + public: + ray() {} + ray(const point3& origin, const vec3& direction) + : orig(origin), dir(direction) + {} + + point3 origin() const { return orig; } + vec3 direction() const { return dir; } + + point3 at(float t) const { + return orig + t*dir; + } + + public: + point3 orig; + vec3 dir; +}; + +#endif diff --git a/apps/raytracing/vec3.h b/apps/raytracing/vec3.h new file mode 100644 index 00000000..d8341d36 --- /dev/null +++ b/apps/raytracing/vec3.h @@ -0,0 +1,102 @@ +#ifndef VEC3_H +#define VEC3_H + +#include +#include + +using std::sqrt; + +class vec3 { + public: + vec3() : e{0,0,0} {} + vec3(float e0, float e1, float e2) : e{e0, e1, e2} {} + + float x() const { return e[0]; } + float y() const { return e[1]; } + float z() const { return e[2]; } + + vec3 operator-() const { return vec3(-e[0], -e[1], -e[2]); } + float operator[](int i) const { return e[i]; } + float& operator[](int i) { return e[i]; } + + vec3& operator+=(const vec3 &v) { + e[0] += v.e[0]; + e[1] += v.e[1]; + e[2] += v.e[2]; + return *this; + } + + vec3& operator*=(const float t) { + e[0] *= t; + e[1] *= t; + e[2] *= t; + return *this; + } + + vec3& operator/=(const float t) { + return *this *= 1/t; + } + + float length() const { + return sqrt(length_squared()); + } + + float length_squared() const { + return e[0]*e[0] + e[1]*e[1] + e[2]*e[2]; + } + + public: + float e[3]; +}; + +// Type aliases for vec3 +using point3 = vec3; // 3D point +using color = vec3; // RGB color + +// vec3 Utility Functions + +inline std::ostream& operator<<(std::ostream &out, const vec3 &v) { + return out << v.e[0] << ' ' << v.e[1] << ' ' << v.e[2]; +} + +inline vec3 operator+(const vec3 &u, const vec3 &v) { + return vec3(u.e[0] + v.e[0], u.e[1] + v.e[1], u.e[2] + v.e[2]); +} + +inline vec3 operator-(const vec3 &u, const vec3 &v) { + return vec3(u.e[0] - v.e[0], u.e[1] - v.e[1], u.e[2] - v.e[2]); +} + +inline vec3 operator*(const vec3 &u, const vec3 &v) { + return vec3(u.e[0] * v.e[0], u.e[1] * v.e[1], u.e[2] * v.e[2]); +} + +inline vec3 operator*(float t, const vec3 &v) { + return vec3(t*v.e[0], t*v.e[1], t*v.e[2]); +} + +inline vec3 operator*(const vec3 &v, float t) { + return t * v; +} + +inline vec3 operator/(vec3 v, float t) { + return (1/t) * v; +} + +inline float dot(const vec3 &u, const vec3 &v) { + return u.e[0] * v.e[0] + + u.e[1] * v.e[1] + + u.e[2] * v.e[2]; +} + +inline vec3 cross(const vec3 &u, const vec3 &v) { + return vec3(u.e[1] * v.e[2] - u.e[2] * v.e[1], + u.e[2] * v.e[0] - u.e[0] * v.e[2], + u.e[0] * v.e[1] - u.e[1] * v.e[0]); +} + +inline vec3 unit_vector(vec3 v) { + return v / v.length(); +} + +#endif -- cgit 1.4.1-2-gfad0