在OpenGL中绘制线条时,经常会遇到锯齿和质量不高的问题。本文将介绍一种技术,可以在OpenGL中实现高质量的2D线条渲染,包括抗锯齿、像素精度控制、线条颜色控制以及透明度混合。
本文介绍的技术能够提供:
请确保以原始大小查看所有图片,以便更好地理解技术细节。
以下是使用该技术的基本代码示例:
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日 - 更新下载文件。