2017-01-23 121 views
1

我使用OpenGL渲染2D地圖,並在此過程中需要渲染具有大量頂點的填充多邊形(100,000+)。爲此,我使用glu tessellator將多邊形鑲嵌到三角形,並使用VBO渲染三角形。使用OpenGL渲染具有大量頂點的填充複雜多邊形

多邊形已成功呈現。 問題是鑲嵌過程變得非常緩慢。對於一些帶有500,000頂點的圖表,我的筆記本電腦(i5-3230M 2.6GHz,8G RAM)將花費近2分鐘。這是我的應用程序無法接受的。

是否有其他鑲嵌算法比glu tessellator更快?

或者我做錯了嗎?

下面的兩個圖像是渲染結果與

glPolygonMode(GL_FRONT, GL_LINE) 

polygons with wireframe turned on closer look

編輯:的地圖數據是靜態的和原來的多邊形數據是在緯度 - 經度的格式。我已經將曲面細分的多邊形數據(這些三角形)保存在單獨的文件中。

爲了更清楚(與問題沒有直接關係),爲了在屏幕上渲染,需要投影來將LL格式轉換爲屏幕座標。

問題是用戶可能有數千個圖表要安裝(將在其中進行鑲嵌細分)。儘管曲面細分只能運行一次,但仍需要很長時間。

回答

0

有幾個想法,我有......你可以將tesselation拆分成有組織的塊,...有點像網格?然後,如果事情發生了變化,您可以智能地僅重新計算改變的部分。

+0

又一想我是多久你打的鑲嵌。你能在其他一半的框架上運行tesselation嗎? –

+0

我只爲每個圖表稱呼它一次 –

1

是靜態還是動態地圖?

對於使用不同的渲染靜態地圖

爲什麼不tesselated多邊形存儲一些文件,而不是再次tesselate它...

對於動態地圖

將可能會更快不需要像這樣凸出多邊形的方法:

  1. 島嶼色彩清晰的屏幕
  2. 渲染島嶼勾勒

    GL_LINE_LOOP未填寫原語並不需要在所有tesselate。

  3. 填充在瓦爾特

    簡單地從點開始以外的多邊形和洪水填充瓦爾特地圖。如果洪水填充編碼正確(沒有遞歸和填充線而不是像素),那麼它應該只需要幾個[毫秒]。這種方法的問題是你需要訪問渲染的東西,所以你至少需要2遍渲染。在GPU上實現洪水填充並不容易。

    也有像CPU存儲邊緣點側和預計算上CPU側的瓦爾特填充的替代品。在這種情況下,您需要爲每個y圖像掃描線列出x座標,這些掃描線將保存每個地塊的起點和終點。然後只需填寫單渲染通道的空白......

    這應該RT呈現容易

[編輯]成長填充測試演示

做了一些測試用迭代增長填充你的數據。數據集中存在一些問題,例如您的多邊形重疊,這可能只是漏洞,但由於我沒有填充顏色信息,而只是反對ID,所以很難說。無論如何,這也可以修復。這裏小贏32 VCL/OpenGL的/ SW演示使用方法,我上面提到的(動態地圖):

  • Win32 Demo使用緩慢的下載是免費的,不需要任何登記剛剛輸入的代碼。

它是Win32的獨立的沒有安裝使用OpenGL + VCL

  • 鼠標滾輪放大
  • 移+鼠標滾輪選擇不同多邊形
  • 鼠標+左按鈕平底鍋

有很少的問題可以修復,但作爲概念證明它運作良好。我把你的ASCII地圖編譯成二進制形式(所以加載速度更快,但形式相同,只是計算多邊形,然後計算每個多邊形的點數,然後將點x,y計數爲64位雙精度值,計數爲32位整數)。我使用我自己的OpenGL引擎(我無法共享),因此您需要編碼這些東西(如OpenGL,FBO和Texture init/set/usage)。反正這裏這個VCL應用的C++代碼:

//$$---- Form CPP ---- 
//--------------------------------------------------------------------------- 
#include <vcl.h> 
#include <math.h> 
#pragma hdrstop 
#include "win_main.h" 
#include "gl/OpenGL3D_double.cpp" 
#include "performance.h" 
//--------------------------------------------------------------------------- 
#pragma package(smart_init) 
#pragma resource "*.dfm" 
// VCL 
TMain *Main; 
// OpenGL 
OpenGLtime tim; 
OpenGLscreen scr; 
OpenGL_FBO fbo; 
GLuint txr_map=-1; 
// miscel 
int pn=0;      // vertex count 
double px0,px1,py0,py1;   // bbox 
double mx,my;     // mouse 
double view[16],iview[16];  // direct and inverse Modelview matrix 
double zoom=1.0,dzoom=1.1,viewx=0.0,viewy=0.0; // view 
int index=0;     // selected polygon 
bool _redraw=true; 
DWORD cl_water=0xFFEE9040; 
DWORD cl_land =0xFF70A0B0; 
DWORD cl_edge =0xFF000000; 
DWORD cl_sel =0xFF00FFFF; 
AnsiString tcpu,tgpu; 
// map 
List< List<double> > polygon; // loaded polygons 
     List<double> water;  // points with water from last frame 
//--------------------------------------------------------------------------- 
void view_compute() 
    { 
    double x,y; 
    glMatrixMode(GL_MODELVIEW); 
    glPushMatrix(); 
    glLoadIdentity(); 
    x=divide(1.0,px1-px0)*scr.aspect; 
    y=divide(1.0,py1-py0)*scr._aspect; 
    if (x>y) x=y; 
    x*=zoom; 
    glTranslated(viewx,viewy,0.0); 
    glScaled(x,x,1.0); 
    glTranslated(-0.5*(px0+px1),-0.5*(py0+py1),0.0); 
    glGetDoublev(GL_MODELVIEW_MATRIX,view); 
    glPopMatrix(); 
    matrix_inv(iview,view); 
    } 
//--------------------------------------------------------------------------- 
void map_load_csv(AnsiString filename) 
    { 
    BYTE *dat; 
    AnsiString lin,s,s0; 
    int ix,i,l,hnd,siz,adr; 
    double x,y; 
    List<AnsiString> id; 

     id.allocate(128);  id.num=0; 
    polygon.allocate(128); polygon.num=0; 

    hnd=FileOpen(filename,fmOpenRead); if (hnd<0) return; 
    siz=FileSeek(hnd,0,2); 
     FileSeek(hnd,0,0); 
    dat=new BYTE[siz]; if (dat==NULL) { FileClose(hnd); return; } 
    siz=FileRead(hnd,dat,siz); 
    FileClose(hnd); 

    adr=0; txt_load_lin(dat,siz,adr,true); 
    for (ix=-1,s0="";adr<siz;) 
     { 
     lin=txt_load_lin(dat,siz,adr,true); 
     if (lin=="") continue; 
     i=1; l=lin.Length(); 
     s=str_load_str(lin,i,true); s=s.SubString(2,s.Length()-2); 
     if (s0!=s) 
      { 
      for (ix=0;ix<id.num;ix++) if (id[ix]==s) break; 
      if (ix>=id.num) 
       { 
       ix=id.num; 
       id.add(s); 
       polygon.add(); 
       polygon[ix].allocate(256); 
       polygon[ix].num=0; 
       } 
      s0=s; 
      } 
     s=str_load_str(lin,i,true); s=s.SubString(2,s.Length()-2); x=str2flt(s); 
     s=str_load_str(lin,i,true); s=s.SubString(2,s.Length()-2); y=str2flt(s); 
     polygon[ix].add(x); 
     polygon[ix].add(y); 
     } 
    } 
//--------------------------------------------------------------------------- 
void map_save_bin(AnsiString filename) 
    { 
    int hnd,i; 
    hnd=FileCreate(filename); if (hnd<0) return; 
    FileWrite(hnd,&polygon.num,4); 
    for (i=0;i<polygon.num;i++) 
     { 
     FileWrite(hnd,&polygon[i].num,4); 
     FileWrite(hnd,polygon[i].dat,polygon[i].num*8); 
     } 
    FileClose(hnd); 
    } 
//--------------------------------------------------------------------------- 
void map_load_bin(AnsiString filename) 
    { 
    int hnd,i,n,m; 
    hnd=FileOpen(filename,fmOpenRead); if (hnd<0) return; 
    FileRead(hnd,&n,4); 
    polygon.allocate(n); polygon.num=n; 
    for (i=0;i<n;i++) 
     { 
     FileRead(hnd,&m,4); 
     polygon[i].allocate(m); polygon[i].num=m; 
     FileRead(hnd,polygon[i].dat,m*8); 
     } 
    FileClose(hnd); 
    } 
//--------------------------------------------------------------------------- 
void map_bbox() 
    { 
    int ix,i,n; 
    double *p,a; 
    pn=0; 
    px0=px1=polygon[0][0]; 
    py0=py1=polygon[0][1]; 
    for (ix=0;ix<polygon.num;ix++) 
     { 
     p=polygon[ix].dat; 
     n=polygon[ix].num; pn+=n>>1; 
     for (i=0;i<n;i+=2) 
      { 
      a=*p; p++; if (px0>a) px0=a; if (px1<a) px1=a; 
      a=*p; p++; if (py0>a) py0=a; if (py1<a) py1=a; 
      } 
     } 
    } 
//--------------------------------------------------------------------------- 
void map_draw() 
    { 
    int ix,i,n; 
    double *p,a; 
// glLineWidth(2.0); 
    for (ix=0;ix<polygon.num;ix++) 
     { 
     p=polygon[ix].dat; 
     n=polygon[ix].num; 
     if (ix==index) glColor4ubv((BYTE*)&cl_sel); 
     else   glColor4ubv((BYTE*)&cl_edge); 
     glBegin(GL_LINE_LOOP); 
     for (i=0;i<n;i+=2,p+=2) glVertex2dv(p); 
     glEnd(); 
     } 
// glLineWidth(1.0); 
    } 
//--------------------------------------------------------------------------- 
void TMain::draw() 
    { 
    tbeg(); 
    tim.tbeg(); 

    // [ render outline to texture ] 
    fbo.bind(scr); 
    glClearColor(divide((cl_land)&255,255),divide((cl_land>>8)&255,255),divide((cl_land>>16)&255,255),1.0); 
    scr.cls(); 
    glMatrixMode(GL_MODELVIEW); 
    glLoadMatrixd(view); 
    glMatrixMode(GL_PROJECTION); 
    glLoadIdentity(); 

    if (water.num) // water start points for grow fill 
     { 
     // add water around txr border 
     glBegin(GL_POINTS); 
     glColor4ubv((BYTE*)&cl_water); 
     for (int i=0;i<water.num;i+=2) 
     glVertex2dv(water.dat+i); 
     glEnd(); 
     } 

    map_draw(); 
    scr.exe(); 

    fbo.unbind(scr); 

    // [ copy GL texture to CPU image ] 
    scr.txrs.txr_ld(txr_map); 
    // [ create ScanLines for direct pixel access pyx[y][x] ] 
    int e,x,y,xs,ys; DWORD **pyx,*p,c0,c1; double a[3]; 
    xs=scr.txrs.txr.xs;      // texture resolution (rounded up to power of 2) 
    ys=scr.txrs.txr.ys; 
    pyx=new DWORD*[ys]; 
    p=(DWORD*)scr.txrs.txr.txr;    // CPU image pixel data 
    for (y=0;y<ys;y++,p+=xs) pyx[y]=p;  // scan line pointers 

    // [ Grow Fill water ] 
    c0=rgb2bgr(cl_land); 
    c1=rgb2bgr(cl_water); 
    if (water.num==0) // first frame view must be set so water is on all borders 
     { 
     // add water around txr border 
     for (x= 1,y=0;y<ys;y++) pyx[y][x]=c1; 
     for (x=xs-2,y=0;y<ys;y++) pyx[y][x]=c1; 
     for (y= 1,x=0;x<xs;x++) pyx[y][x]=c1; 
     for (y=ys-2,x=0;x<xs;x++) pyx[y][x]=c1; 
     } 

    for (e=1;e;)       // grow it 
    for (e=0,y=1;y<ys-1;y++) 
    for ( x=1;x<xs-1;x++) 
     if (pyx[y][x]==c0) 
     if ((pyx[y-1][x]==c1) 
     ||(pyx[y+1][x]==c1) 
     ||(pyx[y][x-1]==c1) 
     ||(pyx[y][x+1]==c1)) { e=1; pyx[y][x]=c1; } 

    // create water start points for next frame 
    water.num=0; 
    e=4; // step 
    for (y=1;y<ys-2;y+=e) 
    for (x=1;x<xs-2;x+=e) 
     if ((pyx[y-1][x-1]==c1) // enough water around (x,y)? 
     &&(pyx[y-1][x ]==c1) 
     &&(pyx[y-1][x+1]==c1) 
     &&(pyx[y ][x-1]==c1) 
     &&(pyx[y ][x ]==c1) 
     &&(pyx[y ][x+1]==c1) 
     &&(pyx[y+1][x-1]==c1) 
     &&(pyx[y+1][x ]==c1) 
     &&(pyx[y+1][x+1]==c1)) 
      { 
      // convert pixel(x,y) -> World(x,y) 
      a[0]=divide(2.0*x,xs)-1.0; 
      a[1]=divide(2.0*y,ys)-1.0; 
      a[2]=0.0; 
      matrix_mul_vector(a,iview,a); 
      water.add(a[0]); 
      water.add(a[1]); 
      } 

    // [ copy CPU image back to GL texture ] 
    delete[] pyx;       // release ScanLines no need for them anymore 
    scr.txrs.txr.rgb2bgr();     // I got RGB/BGR mismatch somewhere 
    scr.txrs.txr_st(txr_map);    // scr.txrs.txr.txr holds pointer to 32bit pixel data 
    scr.exe(); 

    // [ render texture to screen ] 
    scr.cls(); 
    glMatrixMode(GL_MODELVIEW); 
    glLoadIdentity(); 
    glMatrixMode(GL_PROJECTION); 
    glLoadIdentity(); 
    scr.txrs.bind(txr_map); 
    glColor3f(1.0,1.0,1.0); 
    glBegin(GL_QUADS); 
    glTexCoord2f(0.0,0.0); glVertex2f(-1.0,-1.0); 
    glTexCoord2f(1.0,0.0); glVertex2f(+1.0,-1.0); 
    glTexCoord2f(1.0,1.0); glVertex2f(+1.0,+1.0); 
    glTexCoord2f(0.0,1.0); glVertex2f(-1.0,+1.0); 
    glEnd(); 
    scr.txrs.unbind(); 
    // [info] 
    glColor3f(1.0,1.0,1.0); 
    scr.text_init_pix(1.0); 
    scr.text(tcpu); 
    scr.text(tgpu); 
    scr.text_exit(); 

    scr.exe(); 
    scr.rfs(); 

    tend(); tcpu=" CPU time: "+tstr(1); 
    tim.tend(); 
    } 
//--------------------------------------------------------------------------- 
void TMain::mouse(double x,double y,TShiftState sh) 
    { 
    x=divide(2.0*x,scr.xs)-1.0; 
    y=1.0-divide(2.0*y,scr.ys); 
    if (sh.Contains(ssLeft)) 
     { 
     viewx+=x-mx; 
     viewy+=y-my; 
     view_compute(); 
     _redraw=true; 
     } 
    mx=x; 
    my=y; 
    } 
//--------------------------------------------------------------------------- 
__fastcall TMain::TMain(TComponent* Owner) : TForm(Owner) 
    { 
    scr.init(this); 
    txr_map=fbo.add(scr); 
// map_load_csv("map.csv"); 
// map_save_bin("map.bin"); 
    map_load_bin("map.bin"); 
    map_bbox(); 
    view_compute(); 
    draw(); 
    _redraw=true; 
    } 
//--------------------------------------------------------------------------- 
void __fastcall TMain::FormDestroy(TObject *Sender) 
    { 
    scr.exit(); 
    } 
//--------------------------------------------------------------------------- 
void __fastcall TMain::FormPaint(TObject *Sender) 
    { 
    _redraw=true; 
    } 
//--------------------------------------------------------------------------- 
void __fastcall TMain::FormResize(TObject *Sender) 
    { 
    scr.resize(); 
    fbo.resize(scr); 
    _redraw=true; 
    } 
//--------------------------------------------------------------------------- 
void __fastcall TMain::FormMouseWheel(TObject *Sender, TShiftState Shift, int WheelDelta, TPoint &MousePos, bool &Handled) 
    { 
    if (Shift.Contains(ssShift)) 
     { 
     if (WheelDelta>0) index++; else index--; 
     if (index>=polygon.num) index=polygon.num-1; 
     if (index<0) index=0; 
     _redraw=true; 
     } 
    else{ 
     double p[3]={ mx,my,0.0 }; 
     view_compute(); 
     matrix_mul_vector(p,iview,p); 
     if (WheelDelta>0) zoom*=dzoom; else zoom/=dzoom; 
     view_compute(); 
     matrix_mul_vector(p,view,p); 
     viewx-=p[0]-mx; 
     viewy-=p[1]-my; 
     view_compute(); 
     _redraw=true; 
     } 
    } 
//--------------------------------------------------------------------------- 
void __fastcall TMain::FormMouseMove(TObject *Sender, TShiftState Shift, int X,int Y) { mouse(X,Y,Shift); } 
void __fastcall TMain::FormMouseUp(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y) { mouse(X,Y,Shift); } 
void __fastcall TMain::FormMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y) { mouse(X,Y,Shift); } 
//--------------------------------------------------------------------------- 
void __fastcall TMain::Timer1Timer(TObject *Sender) 
    { 
    tgpu=AnsiString().sprintf(" GPU time: [%8.3lf ms]",tim.time()); 
    if (_redraw) { draw(); _redraw=false; } 
    } 
//--------------------------------------------------------------------------- 
void __fastcall TMain::FormDblClick(TObject *Sender) 
    { 
    Width+=10; // ignore this had some bug in resize FBO texture and this was for debugging it 
    } 
//--------------------------------------------------------------------------- 

我也用我的動態列表模板,以便:


List<double> xxx;相同double xxx[];
xxx.add(5);增加5結束的列表
xxx[7]訪問數組元素(保險箱)
xxx.dat[7]訪問ar光元件(不安全的,但快速直達)
xxx.num是數組
xxx.reset()的實際使用的大小將清除陣列,並設置xxx.num=0
xxx.allocate(100)100項目

如果您需要的矩陣和向量幫忙預分配的空間數學例程看到這一點:

在底部,你可以找到連C++實現我使用鏈接的答案...

可以忽略VCL東西的應用程序只需要在它的單定時器間隔40 ms如果需要重畫,並獲取如果準備好測量GL時間...

對你來說重要的東西只是draw()例程。

它的工作原理是這樣的:

  1. 綁定FBO,使渲染texture
  2. 明確其與land color
  3. 呈現多邊形與edge color

    勾勒如果你有孔使其與watter color和填充後渲染他們再與edge color

  4. 在第一幀,你應該有鑑於未放大,因此所有的土地被包圍瓦爾特渲染瓦爾特起點與watter color

    。所以第一個watter點是紋理的邊界矩形。

  5. 解除綁定FBO和紋理PixelData取出複製到CPU端內存

  6. 增長填滿所有瓦爾特到land color像素(停止edge color或任何其他)

    您可以使用任何填補像洪水填充,分段線填充等,但謹慎堆棧溢出遞歸方法。我決定用:

    因爲它是重複的。它並不是那麼快(因此CPU時間很大,但CPU時間的大部分是由GPU/CPU之間的紋理傳輸同步引起的),但通過將圖像細分爲「方形」區域並在需要時傳播填充可顯着加速。

  7. 從該映像創建瓦爾特起點

    所以掃描整個圖像(有一些步驟不需要掃描所有點),如果發現瓦爾特其添加爲瓦爾特起點watter爲下一幀(在世界座標)。這很好的工作,直到你的看法不會改變太多幀,所以限制縮放變化和平移步驟...

    當新的幀包含watter沒有任何起點和其他watter不可訪問時,這裏出現gfx問題。這需要一些思考/測試,但我認爲它應該可以通過從第一幀獲得的一些靜態預定義的watter起始點(每個多邊形很少)解決。

  8. 直接呈現CPU側圖像或傳回給GL質地和渲染。

這裏預覽:

preview

+0

鼓舞人心的,但沒有完全理解。正如你所建議的那樣,我會重新考慮我的設計。還有其他的多邊形和符號,除了水和土地,洪水填補的方式還會工作嗎?而RT的意思是? –

+0

@ShaoboZi您可以添加底色渲染後的額外的東西...而且是好主意,渲染方面唯一重要的東西,查看位置和變焦(無需渲染1米細節時,像素爲100.0米大或標記外景等等...)如果你分享一些數據,我想嘗試在C++中編碼,作爲RT時間可行的概念驗證... – Spektre

+0

@ShaoboZi我正在考慮將你的島嶼轉換成一組水平切片/行而不是三角測量。這可以大大緩解這一點並且我認爲計算速度也會更快。 – Spektre