雖然已經一年多沒有維護gbox這個圖形庫項目了,最近確實時間不夠用。。。git
今年的重點是把xmake完全正好,至少在架構和大功能(包依賴管理)上,要徹底落實下來,後期就是零散的維護和插件功能擴展了。。github
tbox我會陸陸續續一直進行一些小規模更新,明年上半年稍微重構一些模塊後,就開始重點從新搞gbox了,這纔是我一直最想作,也是最喜歡作的項目了算法
因此我寧願開發的慢點,也要把它作精,作到最好。。架構
好了,迴歸正題,雖然如今gbox還處於早期開發中,並不能用到實際的項目中去,可是裏面的一些算法,仍是頗有參考學習價值的。。app
我這兩天沒事就拿出來分享下,若是有感興趣的同窗,能夠直接閱讀源碼:monotone.cide
畢竟這個算法我陸陸續續花了整整一年的時間,才把它完全搞透,而且實現出來。。學習
爲何會花這麼久呢,也許是我太笨了哈。。嘿嘿。。固然也有工做緣由哈。。優化
我先簡單講講研究和實現這個複雜多邊形光柵化算法的背景:ui
個人gbox目前有兩套渲染設備,一套是直接純算法渲染,其核心算法就是掃描多邊形填充算法,這個算法已經算是很廣泛了,也很成熟,效率也很高 可是在個人另一套基於opengl es渲染設備中(爲了可以利用gpu進行加速渲染),在渲染複雜多邊形時,就遇到了問題:opengl不支持複雜多邊形的填充
google
後來我想了不少辦法,也去google了下,發現能夠經過opengl的模板來實現,而後我就開寫了。。
寫到一半,總體效果也出來了,自覺得搞定了,卻又遇到一個很難跨過的瓶頸,效率過低了,用這種方式渲染一個老虎頭,幀率只有:15 fps
比我用純算法的實現還慢,後來就思考爲何這麼慢呢,一個緣由就是模板確實很慢。。。
第二個緣由就是:我要實現通用的渲染接口,要支持各類填充規則,裁剪規則,這些複雜性,也使得基於模板的方式總體不太好優化。。
就這樣折騰了半年,最後決定,仍是總體重構gbox吧,完全不用模板實現了,採用另一種方式:
先在上層對複雜多邊形根據各類填充規則和裁剪,進行預處理,核心算法呢就是:對複雜多邊形進行三角化分割,而且合併成凸多邊形
再送到opengl中進行快速渲染。。。
那問題來了,若是才能高效分割多邊形呢,並且還要支持各類填充規則?
繼續google,最後發現libtess2的光柵化代碼裏面的算法是能夠徹底作到的,可是我不可能直接用它的代碼,一個緣由是維護不方便 另一個緣由是,它裏面的實現,不少地方效率不是很高,而我要實現的比他更高效,更穩定。。。
那就必需要先看透它的實現邏輯,而後再去改進和優化裏面的算法實現。。。
雖然裏面代碼很少,可是我光看透,就又花了半年時間,最後陸陸續續寫了半年,終於才徹底搞定。。
最後效果嗎,仍是不錯的,至少在個人mac pro上用opengl渲染老虎頭,幀率能夠達到:60 fps
固然,裏面確定仍是有不少問題在裏面的,不作最近確實沒時間整了,只能先擱置下來了,等之後在優化優化。。。
先曬曬,三角化後的效果:
而後再曬張老虎頭效果:
接着我再對分割算法作些簡要描述:
gbox中實現算法跟libtess2算法中的一些不一樣和改進的地方:
整個算法總共有四個階段:
其中光柵化算法實現上分有七個階段:
Bentley-Ottman
掃描算法,從事件隊列中掃描全部頂點事件,而且計算交點和winding值(用於填充規則計算)mesh face
的退化狀況,那麼也須要進行處理left face
標記爲"inside",也就是最後須要獲取的輸出區域若是你想要了解更多算法細節,能夠參考: libtess2/alg_outline.md
光柵化接口的使用例子,來自源碼:gbox/gl/render.c:
更詳細的算法實現細節,請參考個人實現: monotone.c
static tb_void_t gb_gl_render_fill_convex(gb_point_ref_t points, tb_uint16_t count, tb_cpointer_t priv) { // check tb_assert(priv && points && count); // apply it gb_gl_render_apply_vertices((gb_gl_device_ref_t)priv, points); #ifndef GB_GL_TESSELLATOR_TEST_ENABLE // draw it gb_glDrawArrays(GB_GL_TRIANGLE_FAN, 0, (gb_GLint_t)count); #else // the device gb_gl_device_ref_t device = (gb_gl_device_ref_t)priv; // make crc32 tb_uint32_t crc32 = 0xffffffff ^ tb_crc_encode(TB_CRC_MODE_32_IEEE_LE, 0xffffffff, (tb_byte_t const*)points, count * sizeof(gb_point_t)); // make color gb_color_t color; color.r = (tb_byte_t)crc32; color.g = (tb_byte_t)(crc32 >> 8); color.b = (tb_byte_t)(crc32 >> 16); color.a = 128; // enable blend gb_glEnable(GB_GL_BLEND); gb_glBlendFunc(GB_GL_SRC_ALPHA, GB_GL_ONE_MINUS_SRC_ALPHA); // apply color if (device->version >= 0x20) gb_glVertexAttrib4f(gb_gl_program_location(device->program, GB_GL_PROGRAM_LOCATION_COLORS), (gb_GLfloat_t)color.r / 0xff, (gb_GLfloat_t)color.g / 0xff, (gb_GLfloat_t)color.b / 0xff, (gb_GLfloat_t)color.a / 0xff); else gb_glColor4f((gb_GLfloat_t)color.r / 0xff, (gb_GLfloat_t)color.g / 0xff, (gb_GLfloat_t)color.b / 0xff, (gb_GLfloat_t)color.a / 0xff); // draw the edges of the filled contour gb_glDrawArrays(GB_GL_TRIANGLE_FAN, 0, (gb_GLint_t)count); // disable blend gb_glEnable(GB_GL_BLEND); #endif } static tb_void_t gb_gl_render_fill_polygon(gb_gl_device_ref_t device, gb_polygon_ref_t polygon, gb_rect_ref_t bounds, tb_size_t rule) { // check tb_assert(device && device->tessellator); #ifdef GB_GL_TESSELLATOR_TEST_ENABLE // set mode gb_tessellator_mode_set(device->tessellator, GB_TESSELLATOR_MODE_TRIANGULATION); // gb_tessellator_mode_set(device->tessellator, GB_TESSELLATOR_MODE_MONOTONE); #endif // set rule gb_tessellator_rule_set(device->tessellator, rule); // set func gb_tessellator_func_set(device->tessellator, gb_gl_render_fill_convex, device); // done tessellator gb_tessellator_done(device->tessellator, polygon, bounds); }