Написание шейдера, определяющего нахождение точки внутри многоугольника

Есть прямоугольник, который занимает весь экран, этот прямоугольник нужно полностью залить одним цветом, исключая область внутри, которая должна быть прозрачной. Область эта описывается как массив вершин с известной точкой центра, упорядоченных против часовой стрелки. Каждые две соседние вершины имеют одинаковый центральный угол. Многоугольник не является правильным, так как расстояние от центра до вершин разное. Изначально был написан следующий шейдер, который полностью работал

Рабочая версия шейдера:

#version 430 core

layout(std430, binding = 0) buffer MyBuffer {
    vec2 dots[]; // массив вершин многоугольника, упорядоченных против часовой стрелки относительно центра
};
uniform int size;//размер массива вершин
uniform vec2 pleyersViewCenter;//позиция камеры
uniform vec2 position;//центр многоугольника

out vec4 fragColor;

//проверка, лежит ли точка внутри многоугольника
bool isPointInsidePolygon(float x, float y)
{
    int intersections = 0;

    for (int i = 0; i < size; i++)
    {
        vec2 v1 = dots[i];
        vec2 v2 = dots[(i + 1) % size];

        // Проверяем пересечение
        if (((v1.y > y) != (v2.y > y)) &&
            (x < (v2.x - v1.x) * (y - v1.y) / (v2.y - v1.y) + v1.x))
        {
            intersections++;
        }
    }

    return (intersections % 2 != 0); // Нечетное количество пересечений
}

void main()
{
    vec2 fragCoord = gl_FragCoord.xy;//координаты текущего пикселя
    //приводим координаты к мировым
    float x = pleyersViewCenter.x - 1280 + fragCoord.x;
    float y = pleyersViewCenter.y + 720 - fragCoord.y;

    //закрашиваем пиксель в зависимости от его положения относительно многоугольника
    if (isPointInsidePolygon(x, y))
    {
        fragColor = vec4(0.0, 0.0, 0.0, 0.0); // прозрачный, если внутри
    }
    else
    {
        fragColor = vec4(60 / 255.0, 60 / 255.0, 60 / 255.0,  1.0); // темный, если снаружи
    }
}

Так как массив вершин слишком большой (1800 элементов), описанный выше код выполняется крайне долго, хоть и полностью корректно. Поэтому было принято решение об оптимизации. Идея заключалась в том, чтоб разбить многоугольник на треугольные сегменты, искать угол между направлением, вектора, выходящего из центра многоугольника в проверяемую точку, и осью Ох, а относительно этого угла выбирать всего 1 треугольный сегмент, после чего проверяем, если точка лежит внутри этого треугольника, следовательно она лежит внутри заданного многоугольника, а иначе находится вне его. Код был написан, но не работает. Ошибок никаких не выдает, просто не определяет точки, которые находятся внутри многоугольника, и все закрашивает одним цветом.

Не рабочая версия шейдера:

#version 430 core

layout(std430, binding = 0) buffer MyBuffer {
    vec2 dots[]; // массив вершин многоугольника, упорядоченных против часовой стрелки относительно центра
};
uniform int size;//размер массива вершин
uniform vec2 pleyersViewCenter;//позиция камеры
uniform vec2 position;//центр многоугольника

out vec4 fragColor;

//проверка лежит ли точка внутри треугольника
bool isPointInTriangle(vec2 P, vec2 A, vec2 B, vec2 C) {
    // Векторные произведения для каждой стороны треугольника
    float cross1 = (B.x - A.x) * (P.y - A.y) - (B.y - A.y) * (P.x - A.x);
    float cross2 = (C.x - B.x) * (P.y - B.y) - (C.y - B.y) * (P.x - B.x);
    float cross3 = (A.x - C.x) * (P.y - C.y) - (A.y - C.y) * (P.x - C.x);

    // Проверяем знаки произведений
    bool has_neg = (cross1 < 0.0) || (cross2 < 0.0) || (cross3 < 0.0);
    bool has_pos = (cross1 > 0.0) || (cross2 > 0.0) || (cross3 > 0.0);

    // Точка внутри треугольника, если все знаки одинаковы
    return !(has_neg && has_pos);
}

//проверка, лежит ли точка внутри многоугольника
bool isPointInsidePolygon(float x, float y)
{
    vec2 currDot = vec2(x, y);//Текущая точка
    vec2 currDirection = position - currDot;//Вектор от центра до точки
    float angle = atan(currDirection.y, currDirection.x); // Угол между вектором и осью X

    // Приводим угол в диапазон [0, 2π]
    if (angle < 0.0) {
        angle += 2.0 * 3.14159265358979323846; // Нормализуем угол
    }

    // Найти индекс ближайших точек по углу
    int index = int(angle / (2.0 * 3.14159265358979323846 / float(size)));

    return isPointInTriangle(currDot, position, dots[index], dots[(index + 1) % size]);
}

void main()
{
    vec2 fragCoord = gl_FragCoord.xy;//координаты текущего пикселя
    //приводим координаты к мировым
    float x = pleyersViewCenter.x - 1280 + fragCoord.x;
    float y = pleyersViewCenter.y + 720 - fragCoord.y;

    //закрашиваем пиксель в зависимости от его положения относительно многоугольника
    if (isPointInsidePolygon(x, y))
    {
        fragColor = vec4(0.0, 0.0, 0.0, 0.0); // прозрачный, если внутри
    }
    else
    {
        fragColor = vec4(60 / 255.0, 60 / 255.0, 60 / 255.0,  1.0); // темный, если снаружи
    }
}

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