#pragma once #include "gp_config.hpp" #include "gp/math.hpp" #include "gp/function.hpp" #include "gp/algorithm/min_of.hpp" #include "gp/indexed_array.hpp" using vec2 = gp::vec2_g<>; using vec3 = gp::vec3_g<>; using vec4 = gp::vec4_g<>; struct camera{ vec3 position; vec3 normal; }; using index_t = size_t; using distance_t = gp_config::rendering::default_type; using color_t = GP_CONFIG__RENDERING__COLOR_T; struct render_point{ distance_t distance; index_t material; bool operator<(const render_point& rhs) { return distance < rhs.distance; } }; using sdf_t = gp::function; using material_t = gp::function; class renderer { using g_t = gp_config::rendering::default_type; constexpr static auto epsilon = gp_config::rendering::epsilon; public: gp::indexed_array scene_elements; gp::indexed_array materials; material_t sky_box = [](vec3) -> color_t { return vec4{0,0,0,0};}; vec2 _resolution{128,64}; camera _camera{{0, 0, -1}, {0, 0, 0}}; vec2 _fov{90, 45}; distance_t projection_start = 1; distance_t projection_end = 50; size_t passes = 12; 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 = _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 = 1; i < passes; ++i) { render_point distance = sdf(render_target); if(distance.distance < epsilon) { return materials[distance.material](render_target); } depth += distance.distance; if(depth >= projection_end) { break; } render_target = _camera.position+depth*target; } return sky_box(render_target); } };