From 8ec2ea1b580009190706cbe0de5a4b03b269c25d Mon Sep 17 00:00:00 2001 From: Andrew Golovashevich Date: Thu, 20 Mar 2025 18:19:44 +0300 Subject: [PATCH] Applying perspective interpolation to textures --- programs/lab5/src/triangle.hpp | 11 + .../utilities/shapes/triangle.hpp | 191 ++++++++++++++++++ 2 files changed, 202 insertions(+) diff --git a/programs/lab5/src/triangle.hpp b/programs/lab5/src/triangle.hpp index b9140db..845e1ec 100644 --- a/programs/lab5/src/triangle.hpp +++ b/programs/lab5/src/triangle.hpp @@ -43,6 +43,16 @@ namespace BGTU::ComputerGraphicsLabWork::Impl { RendererApi::PointF<3> tb{tb4[0] / tb4[3], tb4[1] / tb4[3], tb4[2] / tb4[3]}; RendererApi::PointF<3> tc{tc4[0] / tc4[3], tc4[1] / tc4[3], tc4[2] / tc4[3]}; +#if 1 + Utilities::Shapes::triangle_perspective_interpolator> interpolator{ + {ta.x, ta.y, ta4[3]}, + {this->shader_p0.x, this->shader_p0.y, ta.z}, + {tb.x, tb.y, tb4[3]}, + {this->shader_p1.x, this->shader_p1.y, tb.z}, + {tc.x, tc.y, tc4[3]}, + {this->shader_p2.x, this->shader_p2.y, tc.z}, + }; +#else Utilities::Shapes::triangle_barycentric_interpolator> interpolator{ {ta.x, ta.y}, {this->shader_p0.x, this->shader_p0.y, ta.z}, @@ -51,6 +61,7 @@ namespace BGTU::ComputerGraphicsLabWork::Impl { {tc.x, tc.y}, {this->shader_p2.x, this->shader_p2.y, tc.z}, }; +#endif Utilities::Shapes::iterate_triangle_fill( {static_cast::component_t>(ta.x), static_cast::component_t>(ta.y)}, diff --git a/utilities/include/bgtu/computer_graphics_lab_work/utilities/shapes/triangle.hpp b/utilities/include/bgtu/computer_graphics_lab_work/utilities/shapes/triangle.hpp index 06e6e9c..3445901 100644 --- a/utilities/include/bgtu/computer_graphics_lab_work/utilities/shapes/triangle.hpp +++ b/utilities/include/bgtu/computer_graphics_lab_work/utilities/shapes/triangle.hpp @@ -433,6 +433,197 @@ namespace BGTU::ComputerGraphicsLabWork::Utilities::Shapes { ) noexcept: interpolator{p0, c0, p1, c1, p2, c2} {} + [[nodiscard]] inline RendererApi::Color::Transparent get_color(RendererApi::PointF<2>::component_t x, RendererApi::PointF<2>::component_t y) const final { + return this->interpolator.interpolate_point(x, y); + } + }; + + template + struct _triangle_perspective_interpolator { + private: + long double full_square; + RendererApi::PointF<3> p0; + RendererApi::PointF<3> p1; + RendererApi::PointF<3> p2; + variable_t v0; + variable_t v1; + variable_t v2; + + static long double calculate_square_2( + RendererApi::PointF<2> p0, + RendererApi::PointF<3> p1, + RendererApi::PointF<3> p2 + ) noexcept { + return (p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y); + } + + public: + _triangle_perspective_interpolator( + RendererApi::PointF<3> p0, variable_t v0, + RendererApi::PointF<3> p1, variable_t v1, + RendererApi::PointF<3> p2, variable_t v2 + ) : p0{p0}, p1{p1}, p2{p2}, v0{v0}, v1{v1}, v2{v2}, full_square{calculate_square_2({p0.x, p0.y}, p1, p2)} { + } + + [[nodiscard]] variable_t interpolate_point(RendererApi::PointF<2> p) const { + const double k0 = calculate_square_2(p, this->p1, this->p2) / (this->full_square * this->p0.z); + const double k1 = calculate_square_2(p, this->p2, this->p0) / (this->full_square * this->p1.z); + const double k2 = calculate_square_2(p, this->p0, this->p1) / (this->full_square * this->p2.z); + const double divider = k0 + k1 + k2; + + return transform([=, this](property_getter_t pg) -> auto { return (pg(this->v0) * k0 + pg(this->v1) * k1 + pg(this->v2) * k2) / divider; }); + } + + [[nodiscard]] variable_t interpolate_point(RendererApi::PointF<2>::component_t x, RendererApi::PointF<2>::component_t y) const { + return this->interpolate_point(RendererApi::PointF<2>{x, y}); + } + }; + + + template + struct triangle_perspective_interpolator { + private: + _triangle_perspective_interpolator(interpolator_t interpolator) { + return static_cast(interpolator([](variable_t v) { return v; })); + }> impl; + public: + triangle_perspective_interpolator( + RendererApi::PointF<3> p0, variable_t v0, + RendererApi::PointF<3> p1, variable_t v1, + RendererApi::PointF<3> p2, variable_t v2 + ) : impl{p0, v0, p1, v1, p2, v2} {}; + + [[nodiscard]] variable_t interpolate_point(RendererApi::PointF<2> p) const { + return this->impl.interpolate_point(p); + } + + [[nodiscard]] variable_t interpolate_point(RendererApi::PointF<2>::component_t x, RendererApi::PointF<2>::component_t y) const { + return this->impl.interpolate_point(x, y); + } + }; + + template<> + struct triangle_perspective_interpolator { + private: + _triangle_perspective_interpolator(interpolator_t interpolator) { + return RendererApi::Color{ + static_cast(interpolator([](RendererApi::Color const &c) { return c.red; })), + static_cast(interpolator([](RendererApi::Color const &c) { return c.green; })), + static_cast(interpolator([](RendererApi::Color const &c) { return c.blue; })) + }; + }> impl; + public: + inline triangle_perspective_interpolator( + RendererApi::PointF<3> p0, RendererApi::Color v0, + RendererApi::PointF<3> p1, RendererApi::Color v1, + RendererApi::PointF<3> p2, RendererApi::Color v2 + ) : impl{p0, v0, p1, v1, p2, v2} { + } + + + [[nodiscard]] inline RendererApi::Color interpolate_point(RendererApi::PointF<2> p) const { + return this->impl.interpolate_point(p); + } + + [[nodiscard]] inline RendererApi::Color interpolate_point(RendererApi::PointF<2>::component_t x, RendererApi::PointF<2>::component_t y) const { + return this->impl.interpolate_point(x, y); + } + }; + + template<> + struct triangle_perspective_interpolator { + private: + union _interpolators_t { + triangle_perspective_interpolator ignore_alpha; + _triangle_perspective_interpolator(interpolator_t interpolator) { + return RendererApi::Color::Transparent{ + static_cast(interpolator([](RendererApi::Color::Transparent const &c) { return c.red; })), + static_cast(interpolator([](RendererApi::Color::Transparent const &c) { return c.green; })), + static_cast(interpolator([](RendererApi::Color::Transparent const &c) { return c.blue; })), + static_cast(interpolator([](RendererApi::Color::Transparent const &c) { return c.alpha; })) + }; + }> transparent; + } interpolators; + bool is_alpha_const; + RendererApi::Color::component_fast_t const_alpha_value; + + inline triangle_perspective_interpolator( + bool is_alpha_const, + RendererApi::PointF<3> p0, RendererApi::Color::Transparent v0, + RendererApi::PointF<3> p1, RendererApi::Color::Transparent v1, + RendererApi::PointF<3> p2, RendererApi::Color::Transparent v2 + ) : interpolators{is_alpha_const ? _interpolators_t{.ignore_alpha{p0, v0.without_alpha(), p1, v1.without_alpha(), p2, v2.without_alpha()}} : _interpolators_t{.transparent{p0, v0, p1, v1, p2, v2}}}, + is_alpha_const{is_alpha_const}, const_alpha_value{v0.alpha} {} + + public: + inline triangle_perspective_interpolator( + RendererApi::PointF<3> p0, RendererApi::Color::Transparent v0, + RendererApi::PointF<3> p1, RendererApi::Color::Transparent v1, + RendererApi::PointF<3> p2, RendererApi::Color::Transparent v2 + ) : triangle_perspective_interpolator{ + v0.alpha == v1.alpha && v0.alpha == v2.alpha, + p0, v0, p1, v1, p2, v2 + } {} + + [[nodiscard]] inline RendererApi::Color::Transparent interpolate_point(RendererApi::PointF<2> p) const { + if (this->is_alpha_const) + return this->interpolators.ignore_alpha.interpolate_point(p).with_alpha(this->const_alpha_value); + else + return this->interpolators.transparent.interpolate_point(p); + } + + [[nodiscard]] inline RendererApi::Color::Transparent interpolate_point(RendererApi::PointF<2>::component_t x, RendererApi::PointF<2>::component_t y) const { + return this->interpolate_point(RendererApi::PointF<2>{x, y}); + } + }; + + template + struct triangle_perspective_interpolator> { + private: + using point_t = RendererApi::Point; + + _triangle_perspective_interpolator(interpolator_t interpolator) { + if constexpr (DIMENSIONS == 2) + return point_t{ + static_cast(interpolator([](point_t const &p) { return p.x; })), + static_cast(interpolator([](point_t const &p) { return p.y; })) + }; + else + return point_t{ + static_cast(interpolator([](point_t const &p) { return p.x; })), + static_cast(interpolator([](point_t const &p) { return p.y; })), + static_cast(interpolator([](point_t const &p) { return p.z; })) + }; + }> impl; + public: + triangle_perspective_interpolator( + RendererApi::PointF<3> p0, point_t v0, + RendererApi::PointF<3> p1, point_t v1, + RendererApi::PointF<3> p2, point_t v2 + ) : impl{p0, v0, p1, v1, p2, v2} { + } + + + [[nodiscard]] point_t interpolate_point(RendererApi::PointF<2> p) const { + return this->impl.interpolate_point(p); + } + + [[nodiscard]] point_t interpolate_point(RendererApi::PointF<2>::component_t x, RendererApi::PointF<2>::component_t y) const { + return this->impl.interpolate_point(x, y); + } + }; + + class triangle_perspective_shader : public Shader { + private: + triangle_perspective_interpolator interpolator; + public: + inline triangle_perspective_shader( + RendererApi::PointF<3> p0, RendererApi::Color::Transparent c0, + RendererApi::PointF<3> p1, RendererApi::Color::Transparent c1, + RendererApi::PointF<3> p2, RendererApi::Color::Transparent c2 + ) noexcept: interpolator{p0, c0, p1, c1, p2, c2} {} + + [[nodiscard]] inline RendererApi::Color::Transparent get_color(RendererApi::PointF<2>::component_t x, RendererApi::PointF<2>::component_t y) const final { return this->interpolator.interpolate_point(x, y); }