Как я могу сместить / уменьшить треугольный многоугольник в GLSL?

8

Мне нужно сместить все (синие) треугольники, каждый независимо от других, используя вершинный шейдер. Чтобы манипулировать треугольником в целом, я создал пользовательские (vec3) атрибуты для каждой вершины (красный), представляющие соседние вершины влево (фиолетовый) и вправо (зеленый). Из этого мне нужно вывести оранжевую точку, равноудаленную (в пространстве экрана ) от обоих смежных краев. С тремя такими оранжевыми точками, полученными из каждого треугольника, обработанный (оранжевый) треугольник передается фрагментному шейдеру.

за операцию вершины смещенные треугольники

В идеале, треугольник будет отбракован (как при возврате / без визуализации), если смещения отменяют любое доступное пространство внутри треугольника, например, во втором треугольнике на втором изображении.

Я использую THREE.BufferGeometry () в качестве моей структуры данных.

Вот снимок экрана с эффектом, к которому я стремлюсь:

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

Jackalope
источник
Не могли бы вы добавить немного больше о более широком контексте? Должны ли смещенные треугольники оставаться прикрепленными, как в исходной сетке? Означает ли «отбракованный» исходный треугольник или просто отклонение смещено, и треугольник остается в своем первоначальном размере?
трихоплакс
1
Итак ... как это работает с сетками? Потому что в сетке у вершины более 2 соседей. Или это только для отдельных треугольников?
Болас
Моя реализация такова, что все треугольники расположены в непрерывном буфере: [P1.x, P1.y, P1.z, P2.x, P2.y, P2.z ... Pn.x, Pn.y, Pn.z] с соседними точками, также явно размеченными в атрибутах. Таким образом, каждая вершина каждой грани может быть рассчитана и обработана без воздействия на соседние грани. Никол Болас, да, имеет дело с каждым треугольником отдельно.
Jackalope
trichoplax - «Culled» означает выброшенный, а не визуализированный, как в одностороннем примитиве, обращенном назад.
Jackalope
1
@Jackalope: « Кажется, вы оба предполагаете, что GPU видит лица« привязанными »к другим лицам». Это потому, что, вообще говоря, это правда. В большинстве сеток не только соседние треугольники используют «идентичные атрибуты»; они используют одни и те же вершины . Это может быть через списки треугольников, которые используют один и тот же индекс несколько раз, или через полосы треугольников, или что-то еще. Но, вообще говоря, сетки повторно используют соседние вершины. Ваши сетки не делают, но ваш конкретный случай не меняет общий случай. Вот почему я попросил разъяснений.
Николь Болас

Ответы:

9

Учитывая треугольник ▲ ABC, мы делим пополам угол ACBAC с линией AD, полученной с помощью теоремы Бисектора об углах :

BA / BD = CA / CD Диаграмма вставки треугольника Точка E представляет нашу объективную уточненную позицию на желаемом результирующем врезанном треугольнике. Поскольку он лежит на биссектрисе угла AD, он равноудален от сторон BA и CA, образуя одинаковые прямоугольные треугольники ▲ AFE и ▲ AGE. Теперь мы можем использовать синус для прямоугольных треугольников, чтобы найти длину AE:

AE = EG / Sin (AGEAG)

Это все, что нам нужно, так что давайте приготовим немного GLSL!

Мы начнем со всех типичных атрибутов: матрицы положения, нормали и преобразования, но поскольку вершинный шейдер работает только с одной вершиной, нам нужно добавить соседние вершины в качестве дополнительных атрибутов. Таким образом, каждая вершина найдет свою собственную «точку E», создав результирующий треугольник вставки. (Примечание: я не называю их "B" и "C" здесь, потому что они еще не находятся на экране .)

    attribute vec3 left; //vertex to the left of this vertex
    attribute vec3 right; //vertex to the right of this vertex

Говоря о пространстве экрана, я также включаю соотношение сторон дисплея (и делаю его равномерным в случае изменения размера окна).

После подготовки различных нормалей к фрагментному шейдеру и преобразования лица в пространство отсечения, мы можем приступить к применению приведенной выше математики:

        attribute vec3 left; //vertex to the left of this vertex
        attribute vec3 right; //vertex to the right of this vertex
        uniform float aspect;
        varying vec3 vNormal;
        varying vec2 vUv;

        void main() {
            vNormal = normal;
            vUv = uv;

            mat4 xform= projectionMatrix * modelViewMatrix;
            vec4 A = xform * vec4( position, 1.0 );
            vec4 B = xform * vec4( left, 1.0 );
            vec4 C = xform * vec4( right, 1.0 );

            vec3 CB = C.xyz - B.xyz;
            vec2 BA = B.xy - A.xy;
            vec2 CA = C.xy - A.xy;
            float lengthBA = length(BA);
            float lengthCA = length(CA);
            float ratio = lengthBA / ( lengthBA + lengthCA );
            vec3 D = B.xyz + ratio * CB.xyz;
            vec3 AD = D - A.xyz;
            vec3 bisect = normalize(AD);

            float theta = acos( dot(BA, CA) / (lengthBA * lengthCA) ) / 2.0;
            float AE = 1.0/(sin(theta)*aspect);
            newPos.z += AE/length(AD) * (D.z - A.z);
            newPos.x += bisect.x*AE;
            newPos.y += bisect.y*AE;

            gl_Position = newPos;
        }

Этот код дает нам результаты ниже.

Скриншот

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

Jackalope
источник
1
Хорошая работа, выясняя это! Очень нравится математическое описание в начале.
user1118321
0

Этого можно достичь без тригонометрических функций, уменьшив окружность треугольника.

incircle()вычисляет вкрапление треугольника, образованного вершинами A,B,C, возвращает центр и радиус как vec4. Вершины X=A,B,Cзатем перемещаются внутрь на долю их расстояния до центра вогнутости ( Q-X), которое равно отношению требуемого запаса к радиусу вогнутости ( m/Q.w).

vec4 incircle(vec3 A, vec3 B, vec3 C) {
    float a = length(B - C), b = length(C - A), c = length(A - B);
    float abc = a + b + c;
    // http://mathworld.wolfram.com/Incenter.html
    vec3 I = (a * A + b * B + c * C) / abc;
    // http://mathworld.wolfram.com/Inradius.html
    float r = 0.5
            * sqrt((-a + b + c) * (a - b + c) * (a + b - c) / abc);
    return vec4(I, r);
}

vec3 A,B,C; // vertices
float m; // margin
vec4 Q = incircle(A,B,C);
A += clamp(m / Q.w, 0.0, 1.0) * (Q.xyz - A);
B += clamp(m / Q.w, 0.0, 1.0) * (Q.xyz - B);
C += clamp(m / Q.w, 0.0, 1.0) * (Q.xyz - C);
Адам
источник
Очень интересно, Адам! Я не слышал об этой функции.
Jackalope