OpenGL 2D线条渲染技术

OpenGL中绘制线条时,经常会遇到锯齿和质量不高的问题。本文将介绍一种技术,可以在OpenGL中实现高质量的2D线条渲染,包括抗锯齿、像素精度控制、线条颜色控制以及透明度混合。

技术概述

本文介绍的技术能够提供:

  • 高质量的抗锯齿线条
  • 比任何CPU光栅化算法更低的CPU开销
  • 更精细的线条粗细控制
  • 线条颜色控制
  • 透明度混合(可以选择使用或不使用)

请确保以原始大小查看所有图片,以便更好地理解技术细节。

使用代码

以下是使用该技术的基本代码示例:

glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glOrtho(0, context_width, context_height, 0, 0.0f, 100.0f); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_COLOR_ARRAY); line(10, 100, 100, 300, 1.2f, 0.5f, 0.0f, 1.0f, 1.0f, // line color RGBA 0.0f, 0.0f, // not used true); // enable alphablend // more line() or glDrawArrays() calls glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_COLOR_ARRAY); // other drawing code... glPopMatrix(); glDisable(GL_BLEND); // restore blending options

如果不使用透明度混合,可以这样设置:

glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glOrtho(0, context_width, context_height, 0, 0.0f, 100.0f); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_COLOR_ARRAY); line(20, 100, 110, 300, 1.2f, 0.5f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, // background color false); // not using alphablend // more line() or glDrawArrays() calls glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_COLOR_ARRAY); // other drawing code... glPopMatrix();

工作原理

要理解这种技术,需要了解一些OpenGL的基础知识。下面是一个简单的OpenGL程序,它仅仅绘制了一个每个顶点颜色不同的三角形。观察这个程序,可以发现:

glLoadIdentity(); //window size is 300x300 glOrtho(0, 300, 300, 0, 0.0f, 100.0f); glClearColor(1, 1, 1, 0.5f); glClearDepth(1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_COLOR_ARRAY); float triangle_vertex[] = { 150, 10, // vertex 1 280, 250, // vertex 2 20, 250 // vertex 3 }; float triangle_color[] = { 1, 0, 0, // red 0, 1, 0, // green 0, 0, 1 // blue }; glVertexPointer(2, GL_FLOAT, 0, triangle_vertex); glColorPointer(3, GL_FLOAT, 0, triangle_color); glDrawArrays(GL_TRIANGLES, 0, 3); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_COLOR_ARRAY);

可以看到,边缘是锯齿状的,但是颜色之间的插值看起来是完美的。这就是想要实现的效果。

“渐变多边形”技术

基于上述观察,可以绘制一个从白色渐变到红色的平行四边形。

float para_vertex[] = { 50, 270, 100, 30, 54, 270, 104, 30 }; float para_color[] = { 1, 1, 1, // white 1, 0, 0 // red }; glVertexPointer(2, GL_FLOAT, 0, para_vertex); glColorPointer(3, GL_FLOAT, 0, para_color); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

右侧仍然是锯齿状的,而左侧是平滑的。现在可以想到什么?接下来,绘制两个平行四边形,从白色渐变到红色,然后再渐变回白色。

float para_vertex[] = { 50, 270, 100, 30, 54, 270, 104, 30, 58, 270, 108, 30 }; float para_color[] = { 1, 1, 1, // white 1, 0, 0, // red 1, 1, 1 // white }; glVertexPointer(2, GL_FLOAT, 0, para_vertex); glColorPointer(3, GL_FLOAT, 0, para_color); glDrawArrays(GL_TRIANGLE_STRIP, 0, 6);

称之为“渐变多边形技术”:绘制一个薄的四边形来渲染线条的核心部分,然后在原始四边形旁边绘制两个更多的四边形,颜色逐渐消失。这给提供了抗锯齿的效果。

质量

本文专注于2D线条绘制,所以“完美质量”是相对于2D图形而言的。特别是Maxim Shemanarev(负责Anti-Grain Geometry)在精细的2D渲染方面是专家。让看看他的文章中的一张图片。

上面的图片显示了线条的厚度从0.3像素开始,每增加0.3像素。使用三角形来近似线条段的正确尺寸并不容易。通过实验和手工校准绘图代码来实现:

这不是完美的,端点不够锐利,所以说是“近乎完美”的。发现fltk-cairo构建方便,所以实际上以Cairo为基准,Cairo是Linux上流行的2D渲染API。

它们之间的差异很微妙,所以请确保在幻灯片程序中翻转它们以观察。为做了一个:

可以看出,Cairo绘制的线条比应有的厚度稍厚。右侧的圆形扇区是用cairo_set_line_width(cr, 1.0)绘制的1px黑色线条。但是,看到的是2px灰色线条。在代码中,努力在精确的像素坐标上给1px #000000线条,特别是水平/垂直条件下。但在子像素坐标、其他颜色和方向上没有保证。

理想的1px黑色线条应该看起来非常接近抗锯齿原始1px线条,但更平滑。现在仔细看看右侧的扇区并翻转比较。

最后的比较:

性能

今天的显卡可以每秒渲染数百万三角形。这种技术利用光栅化,已经相当快了。如果想进一步提高性能,可以通过几何着色器生成顶点,但这取决于。通过简单的基准测试,它比OpenGL原生线条绘制(开启平滑)快30倍。在光栅化繁重的情况下(例如,绘制10000条粗线条),比Cairo快40倍。

可移植性

没有在很多机器上测试过代码,所以不能保证。这种技术依赖于光栅化。GL驱动程序正确实现光栅化的可能性总是比平滑线条绘制更高。据所知,大多数硬件都支持子像素精度光栅化。观察到,OpenGL ES在iPhone上的光栅化看起来不错。它可能会起作用。在测试中,经常有舍入误差导致微小的伪影。这不是完美的,但仍然很好。再次,不能保证,最好的方法是自己测试。

结束语

使用三角形来近似线段并不是什么新鲜事,相信很多程序员从OpenGL 1.0开始就做到了。重要的是校准代码以提供如此高质量的输出并发布它。绘制好看的线条应该是图形API的基本功能。奇怪的是,这么多年过去了,没有一个优雅的解决方案,许多程序只是容忍抗锯齿。

代码设计用于易于集成,并可以轻松替换“传统”线条绘制。所以下载zip文件并包含头文件进行测试。如果发现这很有用,只希望能引用这个页面。

渐变多边形技术扩展到实现比线段更复杂的形状的抗锯齿:折线。不要错过本文的第二部分,“通过镶嵌绘制折线”。

2011年6月06日 - 更新下载文件。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485