Complex polygon rasterization algorithm

Although the graphic library project of gbox has not been maintained for more than a year, there is really not enough time recently. . .

The focus of this year is to make xmake completely right, at least in terms of architecture and major functions (package dependency management), to be fully implemented, and the later period is scattered maintenance and plug-in function expansion. .

I will continue to carry out some small-scale updates to tbox. After refactoring some modules in the first half of next year, I will focus on rebuilding gbox. This is the project I have always wanted to do, and it is also my favorite project.

So I would rather slow down the development, but also make it more precise and do my best. .

Okay, back to the topic. Although gbox is still in early development and cannot be used in actual projects, some of the algorithms in it are still very valuable for reference and learning. .

I have nothing to do in the past two days and share it. If there are interested students, you can directly read the source code: monotone.c

After all, it took me a whole year to get this algorithm thoroughly and implement it. .

Why did it take so long, maybe I am too stupid. . Hehe. . Of course, there are also reasons for work. .

Let me briefly talk about the background of the research and implementation of this complex polygon rasterization algorithm:

My gbox There are currently two rendering device, a direct pure rendering algorithm, the core of the algorithm is to scan the polygon fill algorithm, which is considered a very common, very mature, very high efficiency
but in my other set Based on the opengl es rendering device (in order to be able to use GPU for accelerated rendering), when rendering complex polygons, problems are encountered:opengl不支持复杂多边形的填充

Later, I thought of a lot of ways, and I went to google, and found that it can be achieved through OpenGL templates, and then I started writing. .

Halfway through the writing, the overall effect came out. I thought it was done, but I encountered a bottleneck that was difficult to overcome. The efficiency was too low. Rendering a tiger head in this way, the frame rate is only: 15 fps

It was slower than my implementation using pure algorithms. Later I thought about why it was so slow. One reason was that the template was really slow. . .

The second reason is: I want to implement a universal rendering interface and support various filling rules and cropping rules. These complexities also make the overall template-based approach not easy to optimize. .

After tossing like this for half a year, I finally decided to refactor gbox as a whole, without using templates at all, and adopt another method:

First, the complex polygons are preprocessed in the upper layer according to various filling rules and clipping. The core algorithm is: 对复杂多边形进行三角化分割,并且合并成凸多边形
then send it to OpenGL for fast rendering. . .

So the question is, what if the polygon can be divided efficiently, and various filling rules should be supported?

Continue google, finally found inside the raster code libtess2 algorithm can be completely done, but I can not use it directly in the code, one of the reasons is inconvenient to maintain
another reason is that it is inside of implementation, efficiency is not a lot of places Very high, and what I want to achieve is more efficient and more stable than him. . .

Then we must first see its implementation logic, and then improve and optimize the algorithm implementation inside. . .

Although there is not much code in it, it took me half a year to read it through, and it took half a year to write it one after another, and finally it was completely done. .

The final effect is still good, at least on my mac pro with opengl rendering tiger head, the frame rate can reach: 60 fps

Of course, there are still many problems in it. If you don't do it, there is really no time to fix it recently. You can only put it aside and optimize it later. . .

Expose first, and the effect after triangulation:

test_triangulation1
test_triangulation2
test_triangulation3

And then put the tiger head effect:

draw_tiger

Then I will briefly describe the segmentation algorithm:

Some differences and improvements between the algorithm implemented in gbox and the algorithm in libtess2:

  • The overall scanning line direction has been changed from vertical scanning to horizontal scanning, which is more in line with the image scanning seat logic, and the code processing will be more convenient
  • We have removed the 3d vertex coordinate projection process, because we only deal with 2d polygons, it will be faster than libtess2
  • Deal with more intersections, optimize more places where there is intersection error calculation, so our algorithm will be more stable and more accurate
  • Overall support for floating-point and fixed-point switching, you can weigh and adjust yourself in terms of efficiency and accuracy
  • Using its own unique algorithm to achieve active edge comparison, with higher accuracy and better stability
  • Optimized the algorithm of merging from triangular meshes into convex polygons, which is more efficient
  • For each area traversal, the unnecessary fixed-point counting process is removed, so the efficiency will be much faster

The whole algorithm has four stages in total:

  1. Construct a DCEL mesh network from the original complex polygons (DCEL double-connected edge linked list, similar to quad-edge, equivalent to a simplified version).
  2. If the polygon is concave or complex, then first divide it into monotonic polygonal areas (maintenance of mesh structure)
  3. Fast triangulation of mesh-based monotonic polygons
  4. Merge the triangulated area into a convex polygon

There are seven stages in the implementation of the rasterization algorithm:

  1. Simplify the mesh network and deal with some degraded situations in advance (for example: sub-regions degenerated into points, lines, etc.)
  2. Build a list of vertex events and sort it (based on the priority of the smallest heap).
  3. Construct a list of active edge regions and sort it (using insertion sort of local regions, in most cases it is O(n), and the amount is not much).
  4. Use a Bentley-Ottmanscanning algorithm to scan all vertex events from the event queue, and calculate the intersection and winding values ​​(used to fill rule calculations)
  5. If the intersection point changes the topology of the mesh network or the active edge list changes, the consistency of the mesh needs to be repaired
  6. When we are dealing with some mesh facedegradation, we also need to deal with it
  7. left faceMark the monotonous area as "inside", which is the output area that needs to be obtained at the end

If you want to know more algorithm details, you can refer to: libtess2/alg_outline.md

An example of the use of the rasterization interface, from the source code: gbox/gl/render.c :

For more detailed algorithm implementation details, please refer to my implementation: 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);
    }

Personal Homepage: TBOOX Open Source Project
Original Source: http://tboox.org/cn/2016/07/21/tessellate-polygon-algorithm/

Guess you like

Origin blog.csdn.net/waruqi/article/details/53201513