2012-08-27 66 views
2

讓我們從我的位置開始; current state片段着色器:獲得從片段到貝塞爾曲線的距離

我有一個片段着色器,通過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分,一切都在二維空間。 我看到它的方式有兩種解決方法。

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: 拿出一個公式/算法得到的距離分析。我嘗試過這樣,並沒有發現任何數量的貝塞爾曲線點的解決方案,只有恆定的數量。

我更喜歡第二種方法的解決方案。 我可以自己想出第一種方法的解決方案,但如果有人知道任何資源來查找它,所以我不浪費時間可能會有所幫助。

回答

1

我不會推薦計算任意貝塞爾曲線的解析距離,因爲貝塞爾曲線的每一點都依賴於每一個結,對曲線的距離也是如此。 (即使沒有這種當前的實現似乎是相當耗時,不會結垢。)

,我可以推薦兩種方法來計算距離映射(或Voronoi圖)任意形狀:

  1. Fast computation of generalized Voronoi diagrams...:對於每條線(線段),您繪製一個「帳篷」(兩個四邊形排列成屋頂)並從上方渲染場景 - 深度緩衝區是您的距離地圖。可以在着色器中調整光束的厚度和強度,或者調整帳篷的高度。對於每個點,渲染一個圓錐體。 編輯:多邊形也可以在幾何着色器中生成。

  2. Jump Flooding:爲此,您需要設置乒乓渲染方案並渲染到多個緩衝區。我跳過這個算法,因爲它有點冗長,請看這篇論文。

這兩種算法都適用於大量的形狀。