DrawPixelだけで三角形を描く (3) 明暗をつける

vmath.h

#include <raylib.h>

#include <algorithm>
#include <cassert>
#include <cmath>
#include <iterator>
#include <vector>

#include "vmath.h"

int const Cw = 600;
int const Ch = 600;

using vec2 = vmath::vec2<float>;

void PutPixel(int x, int y, Color const &color) {
    DrawPixel(x + Cw / 2, -y + Ch / 2, color);
}

template <typename Iter>
    requires std::output_iterator<Iter, float>
void Interpolate(int i0, float d0, int i1, float d1, Iter out) {
    if (i0 == i1) {
        *out = d0;
        return;
    }

    float a = (d1 - d0) / (i1 - i0);
    float d = d0;

    for (int i = i0; i <= i1; ++i) {
        *out = d;
        ++out;
        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<unsigned char>(
        std::clamp(static_cast<int>(scale * color.r), 0, 255));
    auto g = static_cast<unsigned char>(
        std::clamp(static_cast<int>(scale * color.g), 0, 255));
    auto b = static_cast<unsigned char>(
        std::clamp(static_cast<int>(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) {
	using std::swap;
							
    VertexPositionIntensity P0{p0}, P1{p1}, P2{p2};
    if (P1.y < P0.y) {
        swap(P1, P0);
    }
    if (P2.y < P0.y) {
        swap(P2, P0);
    }
    if (P2.y < P1.y) {
        swap(P2, P1);
    }

    int yi0 = static_cast<int>(std::floor(P0.y));
    int yi1 = static_cast<int>(std::floor(P1.y));
    int yi2 = static_cast<int>(std::floor(P2.y));

    std::vector<float> x01(yi1 - yi0 + 1);
    std::vector<float> h01(yi1 - yi0 + 1);
    Interpolate(yi0, P0.x, yi1, P1.x, x01.begin());
    Interpolate(yi0, P0.h, yi1, P1.h, h01.begin());

    std::vector<float> x12(yi2 - yi1 + 1);
    std::vector<float> h12(yi2 - yi1 + 1);
    Interpolate(yi1, P1.x, yi2, P2.x, x12.begin());
    Interpolate(yi1, P1.h, yi2, P2.h, h12.begin());

    std::vector<float> x02(yi2 - yi0 + 1);
    std::vector<float> h02(yi2 - yi0 + 1);
    Interpolate(yi0, P0.x, yi2, P2.x, x02.begin());
    Interpolate(yi0, P0.h, yi2, P2.h, h02.begin());

	// remove an overlapping point at x01.back and x12.front
    x01.pop_back();
    assert(x01.size() + x12.size() == x02.size());

	// remove an overlapping point at h01.back and h12.front
    h01.pop_back();
    assert(h01.size() + h12.size() == h02.size());

    std::vector<float> x012(x01.size() + x12.size());
    std::merge(x01.begin(), x01.end(), x12.begin(), x12.end(), x012.begin());

    std::vector<float> h012(h01.size() + h12.size());
    std::merge(h01.begin(), h01.end(), h12.begin(), h12.end(), h012.begin());

    assert(x012.size() == x02.size());
    assert(h012.size() == h02.size());
    assert(x012.size() == h012.size());

    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]) {
        swap(x_left, x_right);
        swap(h_left, h_right);
    }

    std::vector<float> h_segment;

    for (int y = yi0; y <= yi2; ++y) {
        int x_l = static_cast<int>(std::floor(x_left[y - yi0]));
        int x_r = static_cast<int>(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();
    }
}

文書の編集
文書の先頭へ