Фрагментный шейдер: получить расстояние от фрагмента до кривой Безье

Начнем с того, где я нахожусь; текущее состояние

У меня есть фрагментный шейдер, который генерирует эту текстуру через FBO. У меня есть почти все примитивы, которые я хочу, кроме кривых Безье.

Мой довольно большой фрагментный шейдер:

testfrag = """//fragment
uniform float thickness; //sets beam thickness
uniform float light; //sets beam intensity

uniform vec4 lines[200]; //pairs of lines; x1, y1, x2, y2
uniform vec3 circles[200]; //circles x,y,radius
uniform vec4 ellipses[200]; //ellipses x,y,width, heightmultiplication
uniform int ellipselength = 1; //amount of ellipses
uniform int linelength = 2; //amount of linepairs
uniform int circlelength = 1; //amount of circles


uniform vec2 polygons[200]; //polygon data, points in x,y. (0,0) points used as end marker
uniform int polylength; //amount of polygons


int i; //for counter variable 1
int i2; //for counter variable 2
float mindistance = 1.0; //distance from fragment to next segment

float P2L(in vec2 p, in vec2 lp1, in vec2 lp2) //distance point to line segment
{
  float squared = pow(distance(lp1, lp2), 2); //squared line length
  float t = dot(p - lp1, lp2 - lp1) / squared; //relative position in parellel to line
  if (t < 0.0) return distance(p, lp1); //before the line, get radius to point
  if (t > 1.0) return distance(p, lp2); //after the line, get radius to point
  return distance(p, lp1 + t * (lp2 - lp1)); //otherwise, get distance line to point
}
float P2C(in vec2 p, in vec3 circle) //point to circle
{
    return abs(distance(p, circle.xy)-circle.z); //euclidian distance - radius
}
float P2E(in vec2 p, in vec4 ellipse) //point to ellipse
{
    return abs(sqrt(pow(p.x-ellipse.x,2) + pow((p.y-ellipse.y)/ellipse.w, 2)) -ellipse.z); // similar to circle, with factor on height
}

bool PinPoly(in vec2 p, in int start, in int len) //test if point in polygon
{
  int i, j; 
  bool c = false;
  for (i = start, j = len-1+start; i < len+start; j = i++) {
    if ( ((polygons[i].y>p.y) != (polygons[j].y>p.y)) &&
     (p.x < (polygons[j].x-polygons[i].x) * (p.y-polygons[i].y) / (polygons[j].y-polygons[i].y) + polygons[i].x) )
       c = !c;
  }
  return c;
} //balls if I know how this works



void main()
{   

    vec2 pos = gl_TexCoord[0].xy; // get position on fbo
    for (i = 0; i < linelength; i++) //test lines
    {
        mindistance = min(mindistance, P2L(pos, lines[i].xy, lines[i].zw));
    }
    for (i = 0; i < circlelength; i++) //test circles
    {
        mindistance = min(mindistance, P2C(pos, circles[i]));
    }
    for (i = 0; i < ellipselength; i++) //test ellipses
    {
        mindistance = min(mindistance, P2E(pos, ellipses[i]));
    }
    i = 1;
    int first;
    while (i < polylength) //test polygons
    {
        //first for line segments
        first = i-1;
        while (polygons[i] != (0.0, 0.0))
        {
            mindistance = min(mindistance, P2L(pos, polygons[i-1], polygons[i]));
            i++;

        }

        mindistance = min(mindistance, P2L(pos, polygons[i-1], polygons[first]));
        if (PinPoly(pos, first, i-first)) //then test if it is inside a polygon
        {
            mindistance = 0.0;
        }
        i += 2; //jump over the (0,0) vec2

    }

    gl_FragColor = light*(1.0/thickness)*(thickness-mindistance).xxxx; //set color of fragment
}
"""

Что называется примерно так в среде pyglet:

def on_draw(self):
    if not self.inited:
        vert = shader.Shader(shader.texvertex)
        frag = shader.Shader(shader.testfrag)
        self.TexProgram = shader.Program([vert, frag])
        with self.TexProgram:
            self.TexProgram["lines[0]"] = (0.25, 0.5, 0.75, 0.5)
            self.TexProgram["lines[1]"] = (0.0, 0.0, 1.0, 1.0)
            self.TexProgram["circles[0]"] = (0.5, 0.5, 0.1)
            self.TexProgram["ellipses[0]"] = (0.75, 0.25, 0.1, 2.0) #x,y, width, multiplicator on height
            self.TexProgram["polygons[0]"] = (0.1, 0.9)
            self.TexProgram["polygons[1]"] = (0.2, 0.9)
            self.TexProgram["polygons[2]"] = (0.2, 1)
            self.TexProgram["polygons[3]"] = (0.1, 1)
            self.TexProgram["polygons[4]"] = (0,0)
            self.TexProgram["polygons[5]"] = (0.1, 0.1)
            self.TexProgram["polygons[6]"] = (0.2, 0.1)
            self.TexProgram["polygons[7]"] = (0.2, 0.2)
            self.TexProgram["polygons[8]"] = (0.1, 0.2)
            self.TexProgram["polygons[9]"] = (0,0)
            self.TexProgram["polylength"] = 8
            self.TexProgram["light"] = 1.05
            self.TexProgram["thickness"] = 0.02
        self.inited = True

    self.clear()
    else:
        with self.TexProgram:
            self.fbo.bind_texture()
            self.fbo.clear()
            t = (0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0)
            x1 = 0
            x2 = w
            y1 = 0
            y2 = h

            glColor4d(1,1,1,1)
            glEnable (GL_TEXTURE_2D)
            glBegin(GL_QUADS)
            glTexCoord2d(t[0], t[1]); glVertex2d(x1, y1)
            glTexCoord2d(t[2], t[3]); glVertex2d(x2, y1)
            glTexCoord2d(t[4], t[5]); glVertex2d(x2, y2)
            glTexCoord2d(t[6], t[7]); glVertex2d(x1, y2)
            glEnd()

Кривые Безье часто имеют более 3 точек, все находится в 2D-пространстве. Как я это вижу, есть 2 способа решить эту проблему.

1:

Generate points on curve.
get closest curve point.
generate points around curve point.
get closest point of those.
rince and repeat last steps until satisfactory precision is reached.

2: Придумайте формулу/алгоритм для аналитического определения расстояния. Я попытался это сделать и не нашел решения для любого количества точек кривой Безье, только для постоянного количества.

Я бы предпочел решение второго метода. Я могу придумать решение для первого метода самостоятельно, но если кто-то знает какие-либо ресурсы, чтобы найти его, чтобы я не тратил время впустую, я все равно мог бы быть полезен.


person Berserker    schedule 27.08.2012    source источник


Ответы (1)


Я бы не рекомендовал вычислять расстояние аналитически для произвольных кривых Безье, потому что каждая точка кривой Безье зависит от каждого узла, то же верно и для расстояния до кривой. (Даже без этого ваша текущая реализация кажется довольно трудоемкой и не будет масштабироваться.)

Я могу порекомендовать два способа вычисления карт расстояний (или диаграмм Вороного) для произвольных форм:

  1. Быстрое вычисление обобщенных диаграмм Вороного...: для каждой линии (сегмента) вы рисуете " палатка" (два четырехугольника, расположенных как крыша) и визуализировать сцену сверху - буфер глубины - это ваша карта расстояний. Толщина и интенсивность луча могут быть адаптированы в шейдере или путем адаптации высоты палатки. Для каждой точки вы визуализируете конус. Редактировать: дополнительная геометрия также может быть сгенерирована в шейдере геометрии.

  2. Jump Flood: для этого вам нужно настроить пинг-понг. схемы и рендеринга в несколько буферов. Я пропускаю алгоритм, так как он немного пространный для объяснения, посмотрите на статью.

Оба алгоритма хорошо работают для большого количества фигур.

person ta55e    schedule 21.09.2012