Удивительное перспективное деление (C++)

В моем softwre-renderer есть проекция точек при помощи матрицы и последующее перспективное деление. В целом код выглядит так:

void DrawSector(gfx::ImageBuffer<RGBQUAD>* imgBuffer, const math::Mat4<float>& vpMatrix, const World& world, size_t sectorIndex)
{
    auto sector = world.getSector(sectorIndex);

    for(const auto& wall : sector->getWalls())
    {
        size_t pointsPerPrimitive = wall->vertices.size();
        for(size_t i = 0; i < pointsPerPrimitive; i++)
        {
            // Индексы двух точек
            size_t i0 = i;
            size_t i1 = ((i+1) > (pointsPerPrimitive-1) ? 0 : i+1);

            // Две точки в пространстве
            auto v0 = wall->vertices[i0];
            auto v1 = wall->vertices[i1];

            // Проекция (в NDC пространство)
            auto v0T = vpMatrix * math::Vec4<float>({v0.x, v0.y, v0.z, 1.0f});
            auto v1T = vpMatrix * math::Vec4<float>({v1.x,v1.y,v1.z,1.0f});
            v0T = v0T / v0T.w;
            v1T = v1T / v1T.w;

            // Перевод в координаты экрана
            auto v0S = math::NdcToScreen(math::Vec2<float>(v0T.x,v0T.y),imgBuffer->getWidth(),imgBuffer->getHeight());
            auto v1S = math::NdcToScreen(math::Vec2<float>(v1T.x,v1T.y),imgBuffer->getWidth(),imgBuffer->getHeight());

            // Нарисовать линию
            gfx::SetLine(imgBuffer,v0S.x,v0S.y,v1S.x,v1S.y,{0,255,0},gfx::SAFE_CHECK_ALL_POINTS);
        }
    }
}

В итоге я получаю примерно такую картинку:

введите сюда описание изображения

Но, если какие-то точки уходят за границы frustum'а видимости, происходит такое:

введите сюда описание изображения

Я примерно понимаю почему это происходит, и я решил попробовать "малой кровью" устранить этот эффект, чтобы вершины не выворачивало при уходе их за границу frustum'а. Я заметил что 4-ая компонента (которая участвует в перспективном делении после умножения матрицы на вершину) становится отрицательной как раз когда точка выходит за frustum. Я решил попробовать просто ограничить ее каким-то значением:

// Проекция (в NDC пространство)
auto v0T = vpMatrix * math::Vec4<float>({v0.x, v0.y, v0.z, 1.0f});
auto v1T = vpMatrix * math::Vec4<float>({v1.x,v1.y,v1.z,1.0f});
v0T = v0T / std::max(v0T.w,0.01f);
v1T = v1T / std::max(v1T.w,0.01f);

В итоге выворот вершин устранился, но появились небольшие искажения перспективы при определенных углах. Я подумал что эти искажение сможет исправить уменьшение ограничивающего числа (я не мог использовать 0, поскольку это было бы деление на ноль), и я решил просто чуть уменьшить, например до 0.001f. В итоге эффект искажения действительно уменьшился, НО:

введите сюда описание изображения

Всё начало просто дичайшим образом тормозить, будто бы производятся какие-то титанические вычисления! И чем меньше я делал ограничивающее значение, тем сильнее были тормоза. Я еще пытался убирать max вовсе, и просто вбивал туда 0.001f без всяких условий - и тормоза сохранялись!

Что это вообще такое? Откуда такие тормоза? Неужели банальное деление на маленькое float значение - такая тяжелая операция для процессора? Должно ли так вообще быть или это следствие какого-то моего косяка, который я где-то допустил?


Ответы (0 шт):