{{:cgfs:shaded_triangles-000.png?400|}} #include #include #include #include #include #include #include "vmath.h" int const Cw = 600; int const Ch = 600; using vec2 = vmath::vec2; void PutPixel(int x, int y, Color const &color) { DrawPixel(x + Cw / 2, -y + Ch / 2, color); } template requires std::output_iterator void Interpolate(int i0, float d0, int i1, float d1, Iter out) { if (i0 == i1) { *out = d0; return; } auto a = (d1 - d0) / (i1 - i0); auto d = d0; for (int i = i0; i <= i1; ++i) { *out++ = d; d += a; } } struct VertexPositionIntensity : vec2 { float h; VertexPositionIntensity() = default; VertexPositionIntensity(float x, float y, float intensity) : vec2{x, y}, h{intensity} {} }; Color color_scaled(Color const &color, float scale) { auto r = static_cast( std::clamp(static_cast(scale * color.r), 0, 255)); auto g = static_cast( std::clamp(static_cast(scale * color.g), 0, 255)); auto b = static_cast( std::clamp(static_cast(scale * color.b), 0, 255)); return {r, g, b, color.a}; } void DrawShadedTriangle(VertexPositionIntensity const &p0, VertexPositionIntensity const &p1, VertexPositionIntensity const &p2, Color const &color) { VertexPositionIntensity P0{p0}, P1{p1}, P2{p2}; if (P1.y < P0.y) { std::swap(P1, P0); } if (P2.y < P0.y) { std::swap(P2, P0); } if (P2.y < P1.y) { std::swap(P2, P1); } auto yi0 = static_cast(std::floor(P0.y)); auto yi1 = static_cast(std::floor(P1.y)); auto yi2 = static_cast(std::floor(P2.y)); auto x01 = std::vector(yi1 - yi0 + 1); auto h01 = std::vector(yi1 - yi0 + 1); Interpolate(yi0, P0.x, yi1, P1.x, x01.begin()); Interpolate(yi0, P0.h, yi1, P1.h, h01.begin()); auto x12 = std::vector(yi2 - yi1 + 1); auto h12 = std::vector(yi2 - yi1 + 1); Interpolate(yi1, P1.x, yi2, P2.x, x12.begin()); Interpolate(yi1, P1.h, yi2, P2.h, h12.begin()); auto x02 = std::vector(yi2 - yi0 + 1); auto h02 = std::vector(yi2 - yi0 + 1); Interpolate(yi0, P0.x, yi2, P2.x, x02.begin()); Interpolate(yi0, P0.h, yi2, P2.h, h02.begin()); x01.pop_back(); assert(x01.size() + x12.size() == x02.size()); h01.pop_back(); assert(h01.size() + h12.size() == h02.size()); auto x012 = std::vector(x01.size() + x12.size() + 1); auto corner_x012 = std::copy(x01.begin(), x01.end(), x012.begin()); std::copy(x12.begin(), x12.end(), corner_x012); auto h012 = std::vector(h01.size() + h12.size() + 1); auto corner_h012 = std::copy(h01.begin(), h01.end(), h012.begin()); std::copy(h12.begin(), h12.end(), corner_h012); auto m = x012.size() / 2; auto &x_left = x012; auto &h_left = h012; auto &x_right = x02; auto &h_right = h02; if (x02[m] < x012[m]) { std::swap(x_left, x_right); std::swap(h_left, h_right); } auto h_segment = std::vector(); for (int y = yi0; y <= yi2; ++y) { auto x_l = static_cast(std::floor(x_left[y - yi0])); auto x_r = static_cast(std::floor(x_right[y - yi0])); h_segment.clear(); Interpolate(x_l, h_left[y - yi0], x_r, h_right[y - yi0], std::back_inserter(h_segment)); for (int x = x_l; x <= x_r; ++x) { float h = h_segment[x - x_l]; PutPixel(x, y, color_scaled(color, h)); } } } int main() { InitWindow(Cw, Ch, "Shaded Triangles"); VertexPositionIntensity p0{-100, 200, 1.0}; VertexPositionIntensity p1{100, 100, 0.2}; VertexPositionIntensity p2{-200, -200, 0.0}; while (!WindowShouldClose()) { BeginDrawing(); ClearBackground(RAYWHITE); DrawShadedTriangle(p0, p1, p2, GREEN); EndDrawing(); } }