From 11c5760110e1b05d31ca88b564b79638bacc6e4c Mon Sep 17 00:00:00 2001 From: Ludovic 'Archivist' Lagouardette Date: Sun, 10 May 2020 12:34:05 +0200 Subject: [PATCH] renderer works --- .gitignore | 1 + include/gp/math.hpp | 104 +++++++++++++++++++ include/gp/rendering/bmp_viewport.hpp | 138 ++++++++++++++++++++++++++ include/gp/rendering/renderer.hpp | 45 +++++++-- tests/math.cpp | 81 +++++++++++++++ 5 files changed, 358 insertions(+), 11 deletions(-) create mode 100644 include/gp/rendering/bmp_viewport.hpp diff --git a/.gitignore b/.gitignore index efa382c..1111191 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,4 @@ default.profraw bin/tests.profdata bin/tests.profraw bin/test_n +render.bmp diff --git a/include/gp/math.hpp b/include/gp/math.hpp index 6aa2bef..497cd93 100644 --- a/include/gp/math.hpp +++ b/include/gp/math.hpp @@ -5,13 +5,68 @@ #if !defined(NO_FP_MATH) # include "gp/math/fp_math.hpp" #endif +#include "gp/algorithm/repeat.hpp" namespace gp { + template + T fixed_sqrt(T value) { + if(value == 0) return 0; + T ret = value / 2; + T tmp; + + gp::repeat(fixed_passes, [&](){ + tmp = ret; + ret = (value / tmp + tmp) / 2; + }); + + return ret; + } + + template + T epsilon_sqrt(T value) { + if(value == 0) return 0; + T ret = value / 2; + T tmp; + constexpr T epsilon = gp_config::rendering::epsilon; + size_t cnt = 0; + while( + !( + (ret+epsilon)*ret > value + && (ret-epsilon)*ret < value + ) + && cnt < cap_passes + ){ + tmp = ret; + ret = (value / tmp + tmp) / 2; + ++cnt; + }; + + return ret; + } + + template + T stable_sqrt(T value) { + if(value == 0) return 0; + T ret = value / 2; + T tmp; + while(ret != tmp){ + tmp = ret; + ret = (value / tmp + tmp) / 2; + }; + + return ret; + } + template struct vec2_g { T x; T y; + vec2_g() + : x{} + , y{} + {} + vec2_g( T _x, T _y @@ -27,10 +82,21 @@ namespace gp { }; } + vec2_g operator*(vec2_g rhs) { + return { + x * rhs.x, + y * rhs.y + }; + } + vec2_g operator+(vec2_g oth) { return {x+oth.x, y+oth.y}; } + vec2_g operator-(vec2_g oth) { + return {x-oth.x, y-oth.y}; + } + vec2_g normalize() { T ilen = fast_isqrt(x*x+y*y); return {x*ilen, y*ilen}; @@ -43,6 +109,12 @@ namespace gp { T y; T z; + vec3_g() + : x{} + , y{} + , z{} + {} + vec3_g( T _x, T _y, @@ -73,10 +145,22 @@ namespace gp { }; } + vec3_g operator*(vec3_g rhs) { + return { + x * rhs.x, + y * rhs.y, + z * rhs.z + }; + } + vec3_g operator+(vec3_g oth) { return {x+oth.x, y+oth.y, z+oth.z}; } + vec3_g operator-(vec3_g oth) { + return {x-oth.x, y-oth.y, z-oth.z}; + } + vec3_g normalize() { T ilen = fast_isqrt(x*x+y*y+z*z); return {x*ilen, y*ilen, z*ilen}; @@ -90,6 +174,13 @@ namespace gp { T z; T w; + vec4_g() + : x{} + , y{} + , z{} + , w{} + {} + vec4_g( T _x, T _y, @@ -125,10 +216,23 @@ namespace gp { }; } + vec4_g operator*(vec4_g rhs) { + return { + x * rhs.x, + y * rhs.y, + z * rhs.z, + w * rhs.w + }; + } + vec4_g operator+(vec4_g oth) { return {x+oth.x, y+oth.y, z+oth.z, w+oth.w}; } + vec4_g operator-(vec4_g oth) { + return {x-oth.x, y-oth.y, z-oth.w, z-oth.w}; + } + vec4_g normalize() { T ilen = fast_isqrt(x*x+y*y+z*z+w*w); return {x*ilen, y*ilen, z*ilen, w*ilen}; diff --git a/include/gp/rendering/bmp_viewport.hpp b/include/gp/rendering/bmp_viewport.hpp new file mode 100644 index 0000000..571b1bb --- /dev/null +++ b/include/gp/rendering/bmp_viewport.hpp @@ -0,0 +1,138 @@ +#pragma once +#include "gp/function.hpp" +#include "gp/buffer.hpp" +#include "gp/math.hpp" +#include "gp/bitops.hpp" +#include "gp/algorithm/tmp_manip.hpp" + + +#include + +namespace gp{ + template + class bmp_viewport { + using src_t = typename gp::either)>, + gp::buffer> + >::type; + src_t source; + gp::vec2_g resolution; + color_type get(int32_t x, int32_t y) { + gp_config::assertion(x>=0, "getting an x below zero"); + gp_config::assertion(y>=0, "getting an y below zero"); + + if constexpr (lazy) { + return source({x, y}); + } else { + return source[x][y]; + } + } + public: + bmp_viewport(gp::vec2_g res, src_t src) + : source{src} + , resolution{res} + {} + + gp::buffer::associated_iterator write(gp::buffer destination) { + using sle16 = /*gp::endian_wrapper<*/int16_t/*, gp::endian::little>*/; + using sbe16 = /*gp::endian_wrapper<*/int16_t/*, gp::endian::big>*/; + using sle32 = /*gp::endian_wrapper<*/int32_t/*, gp::endian::little>*/; + using sbe32 = /*gp::endian_wrapper<*/int32_t/*, gp::endian::big>*/; + + auto it = destination.begin(); + *(it++) = 'B'; + *(it++) = 'M'; + auto& filesize = gp::buffer{it.data, (it+4).data}.cast()[0]; + it = it+4; + + *(it++) = 0; + *(it++) = 0; + *(it++) = 0; + *(it++) = 0; + auto& pixel_array_offset = gp::buffer{it.data, (it+4).data}.cast()[0]; + it = it+4; + + auto dib_start = it; + + auto& dibsize = gp::buffer{it.data, (it+4).data}.cast()[0]; + it = it+4; + + auto& width = gp::buffer{it.data, (it+4).data}.cast()[0]; + width = resolution.x; + it = it+4; + auto& height = gp::buffer{it.data, (it+4).data}.cast()[0]; + it = it+4; + height = resolution.y; + + auto& plane_cnt = gp::buffer{it.data, (it+2).data}.cast()[0]; + it = it+2; + plane_cnt = 1; + auto& bit_per_pixel = gp::buffer{it.data, (it+2).data}.cast()[0]; + it = it+2; + bit_per_pixel = sizeof(color_type)*8; // TODO: correct the size + + auto& compression_method = gp::buffer{it.data, (it+4).data}.cast()[0]; + it = it+4; + compression_method = 0; + + auto& image_size = gp::buffer{it.data, (it+4).data}.cast()[0]; + it = it+4; + + auto& h_pixel_per_meter = gp::buffer{it.data, (it+4).data}.cast()[0]; + it = it+4; + h_pixel_per_meter = 2835; + auto& v_pixel_per_meter = gp::buffer{it.data, (it+4).data}.cast()[0]; + it = it+4; + v_pixel_per_meter = 2835; + + auto& colors_in_palette = gp::buffer{it.data, (it+4).data}.cast()[0]; + it = it+4; + colors_in_palette = 0; + auto& important_colors = gp::buffer{it.data, (it+4).data}.cast()[0]; + it = it+4; + important_colors = 0; + + auto dib_end = it; + dibsize = dib_end - dib_start; + auto pixel_array_start = it; + + for(int32_t line = resolution.y - 1; line >= 0; line--) + { + int32_t len = 0; + for(int32_t row = 0; row < resolution.x; row++) + { + // TODO: add more default color modes + if constexpr (std::is_same>::value) + { + auto color = get(row, line); + *(it++) = color.x; + *(it++) = color.y; + *(it++) = color.z; + *(it++) = color.w; + } else { + it = it + sizeof(color_type); + } + len+=sizeof(color_type); + } + for(;len % 4; ++len) + { + *(it++) = 0; + } + } + + auto pixel_array_end = it; + pixel_array_offset = pixel_array_start - destination.begin(); + filesize = pixel_array_end - destination.begin(); + image_size = pixel_array_end - pixel_array_start; + + + std::cout << width << std::endl; + std::cout << height << std::endl; + std::cout << filesize << std::endl; + std::cout << image_size << std::endl; + + + return it; + } + }; +} \ No newline at end of file diff --git a/include/gp/rendering/renderer.hpp b/include/gp/rendering/renderer.hpp index 5c22cca..fc5b362 100644 --- a/include/gp/rendering/renderer.hpp +++ b/include/gp/rendering/renderer.hpp @@ -46,20 +46,41 @@ public: renderer() = default; + render_point sdf(vec3 render_target) { + return gp::min_of( + scene_elements.begin(), + scene_elements.end(), + [&](auto& p){ + return p(render_target); + } + ); + } + color_t render(vec2 pixel) { g_t depth = projection_start; - vec3 target{0,0,0}; - target = target.normalize(); + vec3 target = _camera.normal; + auto half_res = _resolution/vec2{2.0, 2.0}; + pixel = pixel - half_res; + pixel = pixel / half_res; + pixel = pixel * _fov; + pixel = pixel / vec2{180, 180} * vec2{gp::pi,gp::pi}; + + // Y-rot (adjusts x) + target = vec3{ + target.x*gp::cos(pixel.x) + target.z*gp::sin(pixel.x), + target.y, + -target.x*gp::sin(pixel.x)+target.z*gp::cos(pixel.x) + }; + // X-rot (adjusts y) + target = vec3{ + target.x, + target.y*gp::cos(pixel.y) - target.z*gp::sin(pixel.y), + target.y*gp::sin(pixel.y) + target.z*gp::cos(pixel.y) + }; + vec3 render_target{_camera.position}; - for(int i = 0; i < passes; ++i) { - render_target = _camera.position+depth*target; - render_point distance = gp::min_of( - scene_elements.begin(), - scene_elements.end(), - [&](auto& p){ - return p(render_target); - } - ); + for(int i = 1; i < passes; ++i) { + render_point distance = sdf(render_target); if(distance.distance < epsilon) { return materials[distance.material](render_target); @@ -70,6 +91,8 @@ public: if(depth >= projection_end) { break; } + + render_target = _camera.position+depth*target; } return sky_box(render_target); } diff --git a/tests/math.cpp b/tests/math.cpp index 56098ed..537d077 100644 --- a/tests/math.cpp +++ b/tests/math.cpp @@ -1,7 +1,13 @@ #include "test_scaffold.h" +#include "gp/array.hpp" #include "gp/math.hpp" #include "gp/rendering/renderer.hpp" +#include "gp/rendering/bmp_viewport.hpp" #include +#include +#include +#include +#include struct sin_test : public test_scaffold { @@ -39,6 +45,81 @@ struct render_test : public test_scaffold { virtual int run() { int res = 0; renderer a; + a._resolution = vec2{1000,500}; + + a.sky_box = [](vec3) -> color_t {return {0,0,0,0};}; + auto v = a.materials.push( + [&](vec3 p) -> color_t { + //return color_t{0,0,1,1}; + const float EPSILON = 0.001; + /*vec3( + a.sdf(vec3(p.x + EPSILON, p.y, p.z)).distance - a.sdf(vec3(p.x - EPSILON, p.y, p.z)).distance, + a.sdf(vec3(p.x, p.y + EPSILON, p.z)).distance - a.sdf(vec3(p.x, p.y - EPSILON, p.z)).distance, + a.sdf(vec3(p.x, p.y, p.z + EPSILON)).distance - a.sdf(vec3(p.x, p.y, p.z - EPSILON)).distance + )*/ + auto normals = p.normalize(); + + auto light = vec3(1,1,1).normalize(); + + auto tmp = light*p; + auto color = tmp.x+tmp.y+tmp.z; + + + + return vec4(vec3(color, color, color), 1.0); + //return {v.normalize(), 1.0}; + } + ); + + auto sphere = a.scene_elements.push( + [=](vec3 pos) -> render_point { + render_point ret; + ret.distance = gp::fixed_sqrt(pos.x*pos.x + pos.y*pos.y + pos.z*pos.z) - 1.0; + ret.material = v; + return ret; + } + ); + + a._camera.position = vec3{0, 0, -2}; + a._camera.normal = vec3{0, 0, 1}; + + using pic_color = gp::vec4_g; + + gp::bmp_viewport vp{ + {1000,500}, + [&](gp::vec2_g p) -> pic_color { + auto orig = a.render({(float)p.x,(float)p.y}); + pic_color ret{}; + ret.x = (uint8_t)(orig.x*255); + ret.y = (uint8_t)(orig.y*255); + ret.z = (uint8_t)(orig.z*255); + ret.w = (uint8_t)(orig.w*255); + return ret; + } + }; + + gp::array* buff = new gp::array(); + + auto begin = std::chrono::steady_clock::now(); + + auto r_end = vp.write(buff->as_buffer()); + + auto end = std::chrono::steady_clock::now(); + + std::cout << "render time: " << std::chrono::duration_cast(end - begin).count() << std::endl; + + + + auto myfile = std::fstream("render.bmp", std::ios::out | std::ios::binary); + + myfile.write(buff->begin().data, r_end - buff->begin()); + + myfile.close(); + + delete buff; + + //gp_config::assertion(a.render(vec2{64,32}).x == color_t{1.0,0,0,1.0}.x, "red sphere not perceived"); + //gp_config::assertion(a.render(vec2{0,0}).x == color_t{0.0,0,1.0,1.0}.x, "blue sky not perceived"); return res; }