Mam na zadanie na studia napisać program, który będzie renderował obraz oraz obsługiwał przesunięcie, obrót oraz skalowanie tego obrazu w czasie rzeczywistym.
Do obliczeń matematycznych używam glm. Do ładowania obrazu stb.
Struktura TransformComponent
wygląda w taki sposób:
struct TransformComponent
{
auto get_transform() const
{
glm::mat4 t = glm::mat4(1.0f);
t *= glm::translate(glm::mat4(1.0f), { translation.x, translation.y, 1.0f });
t *= glm::mat4(glm::shearX(glm::mat3(1.0f), shear.x));
t *= glm::mat4(glm::shearY(glm::mat3(1.0f), shear.y));
t *= glm::rotate(glm::mat4(1.0f), rotation, {0.0f, 0.0f, 1.0f});
t *= glm::scale(glm::mat4(1.0f), {scale.x, scale.y, 1.0f});
return t;
}
glm::vec2 translation = {0.0f, 0.0f};
glm::vec2 shear = {0.0f, 0.0f};
glm::vec2 scale = {1.0f, 1.0f};
float rotation = 0.0f;
};
funkcja renderująca
void draw_image_transform(const glm::ivec2& pos, const glm::ivec2& size, const glm::mat4& transform, std::shared_ptr<STBTexture> texture)
{
if (!texture) {
return;
}
static const auto convert_range = [](int old_value, int old_min, int old_max, int new_min, int new_max) {
const int old_range = (old_max - old_min);
if (old_range == 0) {
return new_min;
} else {
const int new_range = (new_max - new_min);
return (((old_value - old_min) * new_range) / old_range) + new_min;
}
};
const auto min = pos;
const auto max = pos + size;
const auto half = size / 2;
const auto finalTransform =
glm::translate(glm::mat4(1.0f), { half.x, half.y, 1.0f }) *
transform *
glm::translate(glm::mat4(1.0f), { -half.x, -half.y, 1.0f });
for (int y = min.y; y < max.y; ++y) {
if (y < 0 || y >= viewport_h) {
return;
}
for (int x = min.x; x < max.x; ++x) {
if (x < 0 || x >= viewport_w) {
continue;
}
int u = convert_range(x, min.x, max.x, 0, texture->w);
int v = convert_range(y, min.y, max.y, 0, texture->h);
const uint32_t texture_index = v * texture->w * texture->channels + u * texture->channels;
const uint32_t color = texture->get_pixel_u32(u, v);
Vector2 transformed = finalTransform * glm::vec4{ x, y, 0.0f, 1.0f };
put_pixel(pixel_buffer, transformed.x, transformed.y, color);
}
}
}
pixel_buffer
to tablica 2d, która jest wrzucana do finalnej tekstury, która jest renderowana na ekranie. put_pixel
wrzuca color do tablicy pixel_buffer
no i gdy nie ma obrotu czy powiększenia, to wszystko jest gicik, ale gdy przeskaluje np. 2 razy obraz to robią się dziury.
Żeby się tego pozbyć, moim zadaniem jest zrobienie interpolacji dwuliniowej. Nie ma to być super szybki algorytm. Wiem, że interpolacja ta działa tak, że biorę 4 sąsiadujące pixele i obliczam średnią ważoną z nich, ale nie mam pojęcia jak to zaimplementować.
Ogólnie musze przejść przez wszystkie pixele w canvasie (wiem, że to będzie wolne, ale na razie chce ogarnąć prostą implementacje) i sprawdzić kolor pixela i potem wziąć sąsiadów i obliczyć średnią i ustawić ten kolor dla pixela.
for(uint32_t y = 0; y < viewport_h; ++y) {
for(uint32_t x = 0; x < viewport_w; ++x) {
//i co teraz???
}
}
ale jakby nie wiem co zrobić w tej pętli.
W jaki sposób ogarnąć tą interpolacje? Przeszukałem cały internet i wiem na czym polega ta interpolacja ale nie wiem jak ją zaimplementować.