[转]:复杂多边形光栅化算法

news/2024/2/29 2:49:23

虽然已经一年多没有维护gbox这个图形库项目了,最近确实时间不够用。。。

今年的重点是把xmake彻底正好,至少在架构和大功能(包依赖管理)上,要完全落实下来,后期就是零散的维护和插件功能扩展了。。

tbox我会陆陆续续一直进行一些小规模更新,明年上半年稍微重构一些模块后,就开始重点重新搞gbox了,这才是我一直最想做,也是最喜欢做的项目了

所以我宁愿开发的慢点,也要把它做精,做到最好。。

好了,回归正题,虽然现在gbox还处于早期开发中,并不能用到实际的项目中去,但是里面的一些算法,还是很有参考学习价值的。。

我这两天没事就拿出来分享下,如果有感兴趣的同学,可以直接阅读源码:monotone.c

毕竟这个算法我陆陆续续花了整整一年的时间,才把它彻底搞透,并且实现出来。。

为什么会花这么久呢,也许是我太笨了哈。。嘿嘿。。当然也有工作原因哈。。

我先简单讲讲研究和实现这个复杂多边形光栅化算法的背景:

我的gbox目前有两套渲染设备,一套是直接纯算法渲染,其核心算法就是扫描多边形填充算法,这个算法已经算是很普遍了,也很成熟,效率也很高
但是在我的另外一套基于opengl es渲染设备中(为了能够利用gpu进行加速渲染),在渲染复杂多边形时,就遇到了问题:opengl不支持复杂多边形的填充

后来我想了很多办法,也去google了下,发现可以通过opengl的模板来实现,然后我就开写了。。

写到一半,整体效果也出来了,自以为搞定了,却又遇到一个很难跨过的瓶颈,效率太低了,用这种方式渲染一个老虎头,帧率只有:15 fps

比我用纯算法的实现还慢,后来就思考为什么这么慢呢,一个原因就是模板确实很慢。。。

第二个原因就是:我要实现通用的渲染接口,要支持各种填充规则,裁剪规则,这些复杂性,也使得基于模板的方式整体不太好优化。。

就这样折腾了半年,最后决定,还是整体重构gbox吧,彻底不用模板实现了,采用另外一种方式:

先在上层对复杂多边形根据各种填充规则和裁剪,进行预处理,核心算法呢就是:对复杂多边形进行三角化分割,并且合并成凸多边形
再送到opengl中进行快速渲染。。。

那问题来了,如果才能高效分割多边形呢,而且还要支持各种填充规则?

继续google,最后发现libtess2的光栅化代码里面的算法是可以完全做到的,但是我不可能直接用它的代码,一个原因是维护不方便
另外一个原因是,它里面的实现,很多地方效率不是很高,而我要实现的比他更高效,更稳定。。。

那就必须要先看透它的实现逻辑,然后再去改进和优化里面的算法实现。。。

虽然里面代码不多,但是我光看透,就又花了半年时间,最后陆陆续续写了半年,终于才完全搞定。。

最后效果吗,还是不错的,至少在我的mac pro上用opengl渲染老虎头,帧率可以达到:60 fps

当然,里面肯定还是有很多问题在里面的,不做最近确实没时间整了,只能先搁置下来了,等以后在优化优化。。。

先晒晒,三角化后的效果:

test_triangulation1
test_triangulation2
test_triangulation3

然后再晒张老虎头效果:

draw_tiger

接着我再对分割算法做些简要描述:

gbox中实现算法跟libtess2算法中的一些不同和改进的地方:

  • 整体扫描线方向从纵向扫描,改成了横向扫描,这样更符合图像扫描的席位逻辑,代码处理上也会更方便

  • 我们移除了3d顶点坐标投影的过程,因为我们只处理2d多边形,所以会比libtess2更快

  • 处理了更多交点情况,优化了更多存在交点误差计算的地方,因此我们的算法会更稳定,精度也更高

  • 整体支持浮点和定点切换,在效率和精度上可以自己权衡调整

  • 采用自己独有的算法实现了活动边缘比较,精度更高,稳定性更好

  • 优化了从三角化的mesh合并成凸多边形的算法,效率更高

  • 对每个区域遍历,移除了没必要的定点计数过程,因此效率会快很多

整个算法总共有四个阶段:

  1. 从原始复杂多边形构建DCEL mesh网(DCEL双连通边缘链表, 跟quad-edge类似,相当于是个简化版).

  2. 如果多边形是凹多边形或者复杂多边形,那么先把它分割成单调多边形区域(mesh结构维护)

  3. 对基于mesh的单调多边形进行快速三角化处理

  4. 合并三角化后的区域到凸多边形

其中光栅化算法实现上分有七个阶段:

  1. 简化mesh网,并且预先处理一些退化的情况(例如:子区域退化成点、线等)

  2. 构建顶点事件列表并且排序它 (基于最小堆的优先级排序).

  3. 构建活动边缘区域列表并且排序它(使用局部区域的插入排序,大部分情况下都是O(n),而且量不多).

  4. 使用Bentley-Ottman扫描算法,从事件队列中扫描所有顶点事件,并且计算交点和winding值(用于填充规则计算)

  5. 如果产生交点改变了mesh网的拓扑结构或者活动边缘列表发生改变,需要对mesh的一致性进行修复

  6. 当我们处理过程中,发生了一些mesh face的退化情况,那么也需要进行处理

  7. 将单调区域的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){// checktb_assert(priv && points && count);// apply itgb_gl_render_apply_vertices((gb_gl_device_ref_t)priv, points);#ifndef GB_GL_TESSELLATOR_TEST_ENABLE// draw itgb_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 crc32tb_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 colorgb_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 blendgb_glEnable(GB_GL_BLEND);gb_glBlendFunc(GB_GL_SRC_ALPHA, GB_GL_ONE_MINUS_SRC_ALPHA);// apply colorif (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 contourgb_glDrawArrays(GB_GL_TRIANGLE_FAN, 0, (gb_GLint_t)count);// disable blendgb_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){// checktb_assert(device && device->tessellator);#ifdef GB_GL_TESSELLATOR_TEST_ENABLE// set modegb_tessellator_mode_set(device->tessellator, GB_TESSELLATOR_MODE_TRIANGULATION);
//      gb_tessellator_mode_set(device->tessellator, GB_TESSELLATOR_MODE_MONOTONE);
#endif// set rulegb_tessellator_rule_set(device->tessellator, rule);// set funcgb_tessellator_func_set(device->tessellator, gb_gl_render_fill_convex, device);// done tessellatorgb_tessellator_done(device->tessellator, polygon, bounds);}

  • TBOOX项目主页

  • 原文出处:http://tboox.org/cn/2016/07/21/tessellate-polygon-algorithm/


https://www.jiucaihua.cn/news/show-2325500.html

相关文章

vue3.0+echarts立体柱图

前言&#xff1a; vue3.0实现echarts立体柱图 实现效果&#xff1a; 实现步骤&#xff1a; 1、安装echarts cnpm i --save echarts 2、页面定义容器 <template><div ref"echart" class"echartDiv"></div> </template> 3、js…

python中素数的求法_关于求N以内素数的python实现以及优化方法

标签&#xff1a; 大纲&#xff1a; 摘要 一、素数的定义 二、N以内素数常用实现方法 三、优化方法 原理层面 代码层面 range和xrange while 1和while True真的重要吗 摘要 本文主要是参考《编程珠玑-续订版》第一章关于求素数的解释&#xff0c;描述素数的定义&#xff0c;以及…

5kb 的 Vue:尤雨溪发布新作 petite-vue

本文来自&#xff1a;公众号&#xff1a;前端大全 官网git入口&#xff1a;点我 前端程序员想必对尤雨溪及其开发的 Vue 框架不陌生。Vue 是一套用于构建用户界面的渐进式 JavaScript 框架&#xff0c;在 2014 年发布后获得了大量开发者的青睐&#xff0c;目前已更新至 3.0 版…

你需要知道的10位Java开发牛人

1、James Gosling 1983 年&#xff0c;Gosling 获得了加州大学的计算机科学学士学位。1990 年&#xff0c;他获得了卡内基梅隆大学的计算机科学博士学位&#xff0c;师从 BobSproull。在攻读博士期间&#xff0c;他自己开发了一款 emacs&#xff0c;叫 Gosling Emacs(Gosmacs)&…

eclipse打war包_详解window和linux通过tomcat9手工部署war包的方法(超实用)

亲测在windows环境和linux环境不一样Windows第1步&#xff1a; 用maven打war包 (假如得到的war包名为: hello-back.war)第2步: 把war包拷贝到tomcat安装的webapps目录下第3步: 启动tomcat (可以在conf/server.xml修改端口号)第4步: 浏览器访问就好了127.0.0.1::8080linux同样的…

java垃圾回收机制研究

2019独角兽企业重金招聘Python工程师标准>>> 1.什么是java垃圾回收 垃圾回收GC&#xff08;Garbage Collection&#xff09;简单的一句话介绍&#xff1a;回收无任何引用的对象占据的内存。 这个java虚拟机实现的&#xff0c;所有我们java程序员&#xff0c;new了一…

js实现颜色选择器

前言&#xff1a; 纯js就可以实现颜色选择器&#xff0c;不用安装很多的插件&#xff0c;和配置。也不必要有jquery才能使用&#xff0c;只是js文件就可以了。 目录&#xff1a; 实现原理&#xff1a; 如果要获取当前的颜色&#xff0c;可以获取结论的背景&#xff0c;或者再…

Node.js实现单页面爬虫

在imooc网上跟着老师写了两个爬虫&#xff0c;一个最简单的直接爬整个页面&#xff0c;一个完善版把章节标题和对应编号打出来了。看完之后&#xff0c;自己也想写一个爬虫&#xff0c;用自己的博客做测试&#xff0c;虽然结果并没有很成功- -&#xff0c;还是把代码放上来。目…

java xml 默认名称空间 xpath_测试工具XPath

XML有两种MIME类型&#xff0c;即application/xml和text/xml&#xff0c;在HTTP中&#xff0c;MIME Type类型被定义在Content-Type header中。我们经常也会看到接口返回数据类型为XML格式。功能测试/自动化脚本里&#xff0c;经常会需要提取xml数据&#xff0c;用作上下文使用或…

idea中设置支持es6语法

前言&#xff1a; 在接手一些前后端不分离的项目的时候&#xff0c;需要使用到了idea&#xff0c;在这里进行开发的时候发现他老是es6的语法进行爆红&#xff0c;相当的让人不舒服&#xff0c;这里整理下方法。 原因&#xff1a; idea默认的js是es5的&#xff0c;所以es6的语法…