Extensible barycentric interpolator

This commit is contained in:
Andrew Golovashevich 2025-03-07 15:12:51 +03:00
parent 0b816497e9
commit 9e58b48034
3 changed files with 167 additions and 55 deletions

View File

@ -1,6 +1,7 @@
#include <utility> #include <utility>
#include <numbers> #include <numbers>
#include <cmath> #include <cmath>
#include <bgtu/computer_graphics_lab_work/renderer_api/point.hpp>
#include <bgtu/computer_graphics_lab_work/utilities/shader.hpp> #include <bgtu/computer_graphics_lab_work/utilities/shader.hpp>
#include <bgtu/computer_graphics_lab_work/utilities/shapes/triangle.hpp> #include <bgtu/computer_graphics_lab_work/utilities/shapes/triangle.hpp>
@ -24,10 +25,9 @@ namespace BGTU::ComputerGraphicsLabWork::Impl {
RendererApi::PointF<3> tc = data->transform * this->c; RendererApi::PointF<3> tc = data->transform * this->c;
Utilities::Shapes::triangle_barycentric_interpolator<double> z_interpolator{ Utilities::Shapes::triangle_barycentric_interpolator<double> z_interpolator{
{ta.x, ta.y}, {ta.x, ta.y}, ta.z,
{tb.x, tb.y}, {tb.x, tb.y}, tb.z,
{tc.x, tc.y}, {tc.x, tc.y}, tc.z
ta.z, tb.z, tc.z
}; };
Utilities::Shapes::iterate_triangle_fill( Utilities::Shapes::iterate_triangle_fill(
@ -48,25 +48,25 @@ namespace BGTU::ComputerGraphicsLabWork::Impl {
Triangle triangles[] = { Triangle triangles[] = {
{{0.0, 0.0, 1.0}, angle5(0, 2), angle5(1, 1)}, {{0.0, 0.0, 1.0}, angle5(0, 2), angle5(1, 1)},
{{0.0, 0.0, -1.0}, angle5(0, 2), angle5(1, 1)}, {{0.0, 0.0, -1.0}, angle5(0, 2), angle5(1, 1)},
{{0.0, 0.0, 1.0}, angle5(2, 2), angle5(1, 1)}, {{0.0, 0.0, 1.0}, angle5(2, 2), angle5(1, 1)},
{{0.0, 0.0, -1.0}, angle5(2, 2), angle5(1, 1)}, {{0.0, 0.0, -1.0}, angle5(2, 2), angle5(1, 1)},
{{0.0, 0.0, 1.0}, angle5(2, 2), angle5(3, 1)}, {{0.0, 0.0, 1.0}, angle5(2, 2), angle5(3, 1)},
{{0.0, 0.0, -1.0}, angle5(2, 2), angle5(3, 1)}, {{0.0, 0.0, -1.0}, angle5(2, 2), angle5(3, 1)},
{{0.0, 0.0, 1.0}, angle5(4, 2), angle5(3, 1)}, {{0.0, 0.0, 1.0}, angle5(4, 2), angle5(3, 1)},
{{0.0, 0.0, -1.0}, angle5(4, 2), angle5(3, 1)}, {{0.0, 0.0, -1.0}, angle5(4, 2), angle5(3, 1)},
{{0.0, 0.0, 1.0}, angle5(4, 2), angle5(5, 1)}, {{0.0, 0.0, 1.0}, angle5(4, 2), angle5(5, 1)},
{{0.0, 0.0, -1.0}, angle5(4, 2), angle5(5, 1)}, {{0.0, 0.0, -1.0}, angle5(4, 2), angle5(5, 1)},
{{0.0, 0.0, 1.0}, angle5(6, 2), angle5(5, 1)}, {{0.0, 0.0, 1.0}, angle5(6, 2), angle5(5, 1)},
{{0.0, 0.0, -1.0}, angle5(6, 2), angle5(5, 1)}, {{0.0, 0.0, -1.0}, angle5(6, 2), angle5(5, 1)},
{{0.0, 0.0, 1.0}, angle5(6, 2), angle5(7, 1)}, {{0.0, 0.0, 1.0}, angle5(6, 2), angle5(7, 1)},
{{0.0, 0.0, -1.0}, angle5(6, 2), angle5(7, 1)}, {{0.0, 0.0, -1.0}, angle5(6, 2), angle5(7, 1)},
{{0.0, 0.0, 1.0}, angle5(8, 2), angle5(7, 1)}, {{0.0, 0.0, 1.0}, angle5(8, 2), angle5(7, 1)},
{{0.0, 0.0, -1.0}, angle5(8, 2), angle5(7, 1)}, {{0.0, 0.0, -1.0}, angle5(8, 2), angle5(7, 1)},
{{0.0, 0.0, 1.0}, angle5(8, 2), angle5(9, 1)}, {{0.0, 0.0, 1.0}, angle5(8, 2), angle5(9, 1)},
{{0.0, 0.0, -1.0}, angle5(8, 2), angle5(9, 1)}, {{0.0, 0.0, -1.0}, angle5(8, 2), angle5(9, 1)},
{{0.0, 0.0, 1.0}, angle5(0, 2), angle5(9, 1)}, {{0.0, 0.0, 1.0}, angle5(0, 2), angle5(9, 1)},
{{0.0, 0.0, -1.0}, angle5(0, 2), angle5(9, 1)}, {{0.0, 0.0, -1.0}, angle5(0, 2), angle5(9, 1)},
}; };

View File

@ -27,6 +27,9 @@ namespace BGTU::ComputerGraphicsLabWork::RendererApi {
return !(*this == other); return !(*this == other);
} }
class Transparent;
[[nodiscard]] constexpr Transparent with_alpha(component_fast_t alpha) const noexcept;
class Transparent { class Transparent {
public: public:
@ -43,6 +46,8 @@ namespace BGTU::ComputerGraphicsLabWork::RendererApi {
constexpr Transparent(Color c) noexcept: red{c.red}, green{c.green}, blue{c.blue}, alpha{component_max_value} {} constexpr Transparent(Color c) noexcept: red{c.red}, green{c.green}, blue{c.blue}, alpha{component_max_value} {}
constexpr Transparent(Color c, component_fast_t alpha) noexcept: red{c.red}, green{c.green}, blue{c.blue}, alpha{alpha} {}
constexpr bool operator==(Color::Transparent const &other) const noexcept { constexpr bool operator==(Color::Transparent const &other) const noexcept {
return this->red == other.red && this->green == other.green && this->blue == other.blue && this->alpha == other.alpha; return this->red == other.red && this->green == other.green && this->blue == other.blue && this->alpha == other.alpha;
} }
@ -56,4 +61,8 @@ namespace BGTU::ComputerGraphicsLabWork::RendererApi {
}; };
}; };
}; };
constexpr Color::Transparent Color::with_alpha(Color::component_fast_t alpha) const noexcept {
return Color::Transparent{*this, alpha};
};
} }

View File

@ -248,8 +248,8 @@ namespace BGTU::ComputerGraphicsLabWork::Utilities::Shapes {
); );
} }
template<class variable_t> template<class variable_t, auto transform>
struct triangle_barycentric_interpolator { struct _triangle_barycentric_interpolator {
private: private:
long double full_square; long double full_square;
RendererApi::PointF<2> p0; RendererApi::PointF<2> p0;
@ -259,11 +259,6 @@ namespace BGTU::ComputerGraphicsLabWork::Utilities::Shapes {
variable_t v1; variable_t v1;
variable_t v2; variable_t v2;
struct _empty {
};
std::conditional_t<std::is_same_v<variable_t, RendererApi::Color::Transparent>, bool, _empty> skip_alpha;
static long double calculate_square_2( static long double calculate_square_2(
RendererApi::PointF<2> p0, RendererApi::PointF<2> p0,
RendererApi::PointF<2> p1, RendererApi::PointF<2> p1,
@ -272,53 +267,161 @@ namespace BGTU::ComputerGraphicsLabWork::Utilities::Shapes {
return (p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y); return (p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y);
} }
public: public:
triangle_barycentric_interpolator( _triangle_barycentric_interpolator(
RendererApi::PointF<2> p0, RendererApi::PointF<2> p1, RendererApi::PointF<2> p2, RendererApi::PointF<2> p0, variable_t v0,
variable_t v0, variable_t v1, variable_t v2 RendererApi::PointF<2> p1, variable_t v1,
RendererApi::PointF<2> p2, variable_t v2
) : p0{p0}, p1{p1}, p2{p2}, v0{v0}, v1{v1}, v2{v2}, full_square{calculate_square_2(p0, p1, p2)} { ) : p0{p0}, p1{p1}, p2{p2}, v0{v0}, v1{v1}, v2{v2}, full_square{calculate_square_2(p0, p1, p2)} {
if constexpr (std::is_same_v<variable_t, RendererApi::Color::Transparent>) {
this->skip_alpha = v0.alpha == v1.alpha && v0.alpha == v2.alpha;
}
} }
variable_t interpolate_point(RendererApi::PointF<2> p) const { [[nodiscard]] variable_t interpolate_point(RendererApi::PointF<2> p) const {
const double k0 = calculate_square_2(p, this->p1, this->p2) / this->full_square; const double k0 = calculate_square_2(p, this->p1, this->p2) / this->full_square;
const double k1 = calculate_square_2(p, this->p2, this->p0) / this->full_square; const double k1 = calculate_square_2(p, this->p2, this->p0) / this->full_square;
const double k2 = calculate_square_2(p, this->p0, this->p1) / this->full_square; const double k2 = calculate_square_2(p, this->p0, this->p1) / this->full_square;
if constexpr (std::is_same_v<variable_t, RendererApi::Color::Transparent> || std::is_same_v<variable_t, RendererApi::Color>) { return transform([=, this]<class property_getter_t>(property_getter_t pg) -> auto { return pg(this->v0) * k0 + pg(this->v1) * k1 + pg(this->v2) * k2; });
auto f = [&]<class intermediate_t>(intermediate_t (variable_t::*offset)) -> intermediate_t {
return static_cast<intermediate_t>((this->v0.*offset) * k0 + (this->v1.*offset) * k1 + (this->v2.*offset) * k2);
};
if constexpr (std::is_same_v<variable_t, RendererApi::Color::Transparent>) {
RendererApi::Color base{
f(&RendererApi::Color::Transparent::red),
f(&RendererApi::Color::Transparent::green),
f(&RendererApi::Color::Transparent::blue),
};
return RendererApi::Color::Transparent{
base.red, base.green, base.blue,
(this->skip_alpha ? this->v0.alpha : f(&RendererApi::Color::Transparent::alpha))
};
} else {
return RendererApi::Color{
f(&RendererApi::Color::red),
f(&RendererApi::Color::green),
f(&RendererApi::Color::blue)
};
}
} else {
return static_cast<variable_t>((this->v0) * k0 + (this->v1) * k1 + (this->v2) * k2);
}
} }
variable_t interpolate_point(RendererApi::PointF<2>::component_t x, RendererApi::PointF<2>::component_t y) const { [[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}); return this->interpolate_point(RendererApi::PointF<2>{x, y});
} }
}; };
template<class variable_t>
struct triangle_barycentric_interpolator {
private:
_triangle_barycentric_interpolator<variable_t, []<class interpolator_t>(interpolator_t interpolator) {
return static_cast<variable_t>(interpolator([](variable_t v) { return v; }));
}> impl;
public:
triangle_barycentric_interpolator(
RendererApi::PointF<2> p0, variable_t v0,
RendererApi::PointF<2> p1, variable_t v1,
RendererApi::PointF<2> 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_barycentric_interpolator<RendererApi::Color> {
private:
_triangle_barycentric_interpolator<RendererApi::Color, []<class interpolator_t>(interpolator_t interpolator) {
return RendererApi::Color{
static_cast<RendererApi::Color::component_fast_t>(interpolator([](RendererApi::Color const &c) { return c.red; })),
static_cast<RendererApi::Color::component_fast_t>(interpolator([](RendererApi::Color const &c) { return c.green; })),
static_cast<RendererApi::Color::component_fast_t>(interpolator([](RendererApi::Color const &c) { return c.blue; }))
};
}> impl;
public:
inline triangle_barycentric_interpolator(
RendererApi::PointF<2> p0, RendererApi::Color v0,
RendererApi::PointF<2> p1, RendererApi::Color v1,
RendererApi::PointF<2> 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_barycentric_interpolator<RendererApi::Color::Transparent> {
private:
union _interpolators_t {
triangle_barycentric_interpolator<RendererApi::Color> ignore_alpha;
_triangle_barycentric_interpolator<RendererApi::Color::Transparent, []<class interpolator_t>(interpolator_t interpolator) {
return RendererApi::Color::Transparent{
static_cast<RendererApi::Color::component_fast_t>(interpolator([](RendererApi::Color::Transparent const &c) { return c.red; })),
static_cast<RendererApi::Color::component_fast_t>(interpolator([](RendererApi::Color::Transparent const &c) { return c.green; })),
static_cast<RendererApi::Color::component_fast_t>(interpolator([](RendererApi::Color::Transparent const &c) { return c.blue; })),
static_cast<RendererApi::Color::component_fast_t>(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_barycentric_interpolator(
bool is_alpha_const,
RendererApi::PointF<2> p0, RendererApi::Color::Transparent v0,
RendererApi::PointF<2> p1, RendererApi::Color::Transparent v1,
RendererApi::PointF<2> 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_barycentric_interpolator(
RendererApi::PointF<2> p0, RendererApi::Color::Transparent v0,
RendererApi::PointF<2> p1, RendererApi::Color::Transparent v1,
RendererApi::PointF<2> p2, RendererApi::Color::Transparent v2
) : triangle_barycentric_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<unsigned DIMENSIONS, class component_t>
struct triangle_barycentric_interpolator<RendererApi::Point<DIMENSIONS, component_t>> {
private:
using point_t = RendererApi::Point<DIMENSIONS, component_t>;
_triangle_barycentric_interpolator<RendererApi::Color, []<class interpolator_t>(interpolator_t interpolator) {
if constexpr (DIMENSIONS == 2)
return point_t{
static_cast<point_t::component_fast_t>(interpolator([](point_t const &p) { return p.x; })),
static_cast<point_t::component_fast_t>(interpolator([](point_t const &p) { return p.y; }))
};
else
return RendererApi::Point<3, component_t>{
static_cast<point_t::component_fast_t>(interpolator([](point_t const &p) { return p.x; })),
static_cast<point_t::component_fast_t>(interpolator([](point_t const &p) { return p.y; })),
static_cast<point_t::component_fast_t>(interpolator([](point_t const &p) { return p.z; }))
};
}> impl;
public:
triangle_barycentric_interpolator(
RendererApi::PointF<2> p0, point_t v0,
RendererApi::PointF<2> p1, point_t v1,
RendererApi::PointF<2> p2, point_t v2
) : impl{p0, v0, p1, v1, p2, v2} {
}
[[nodiscard]] RendererApi::Color interpolate_point(RendererApi::PointF<2> p) const {
return this->impl.interpolate_point(p);
}
[[nodiscard]] RendererApi::Color interpolate_point(RendererApi::PointF<2>::component_t x, RendererApi::PointF<2>::component_t y) const {
return this->impl.interpolate_point(x, y);
}
};
class triangle_barycentric_shader : public Shader { class triangle_barycentric_shader : public Shader {
private: private:
triangle_barycentric_interpolator<RendererApi::Color::Transparent> interpolator; triangle_barycentric_interpolator<RendererApi::Color::Transparent> interpolator;
@ -326,7 +429,7 @@ namespace BGTU::ComputerGraphicsLabWork::Utilities::Shapes {
inline triangle_barycentric_shader( inline triangle_barycentric_shader(
RendererApi::PointF<2> p0, RendererApi::PointF<2> p1, RendererApi::PointF<2> p2, RendererApi::PointF<2> p0, RendererApi::PointF<2> p1, RendererApi::PointF<2> p2,
RendererApi::Color::Transparent c0, RendererApi::Color::Transparent c1, RendererApi::Color::Transparent c2 RendererApi::Color::Transparent c0, RendererApi::Color::Transparent c1, RendererApi::Color::Transparent c2
) noexcept: interpolator{p0, p1, p2, c0, c1, 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 { [[nodiscard]] inline RendererApi::Color::Transparent get_color(RendererApi::PointF<2>::component_t x, RendererApi::PointF<2>::component_t y) const final {