对点和直线的着色设置alpha值设置,可以进行抗锯齿,在光栅化阶段有影响,在混合时候也有影响。
超级采样抗锯齿(Super-Sampling Anti-aliasing,简称SSAA)此是早期抗锯齿方法,比较消耗资源,但简单直接,先把图像映射到缓存并把它放大,再用超级采样把放大后的图像像素进行采样,一般选取2个或4个邻近像素,把这些采样混合起来后,生成的最终像素,令每个像素拥有邻近像素的特征,像素与像素之间的过渡色彩,就变得近似,令图形的边缘色彩过渡趋于平滑。再把最终像素还原回原来大小的图像,并保存到帧缓存也就是显存中,替代原图像存储起来,最后输出到显示器,显示出一帧画面。这样就等于把一幅模糊的大图,通过细腻化后再缩小成清晰的小图。如果每帧都进行抗锯齿处理,游戏或视频中的所有画面都带有抗锯齿效果。而将图像映射到缓存并把它放大时,放大的倍数被用于分别抗锯齿的效果,如:图1,AA后面的x2、x4、x8就是原图放大的倍数。 超级采样抗锯齿中使用的采样法一般有两种:
1.顺序栅格超级采样(Ordered Grid Super-Sampling,简称OGSS),采样时选取2个邻近像素。
2.旋转栅格超级采样(Rotated Grid Super-Sampling,简称RGSS),采样时选取4个邻近像素。
多重采样抗锯齿,应该在一个Drawcall中的Fragment Shader之后(Blend混合之前进行处理,目的是支持部分渲染物体的抗锯齿)对多边形边缘进行了多重采样覆盖颜色计算(颜色计算时候会考虑alpha值)。
1.对点和直线的绘制进行抗锯齿设置和启用alpha混合
对点和直线绘制的抗锯齿绘制方式,应该是在光栅化填充阶段进行,且前后将一个drawcall分离为前后绘制,用于像素在很合阶段的alpha混合操作。
1)启用用GL_LINE_SMOOTH线条的着色模式。
点线多边形大小着色计算,在启用抗锯齿相关设置下光栅化片段着色数量,斜率的 计算都不一样。
2)用glHint函数指定点线面的抗锯齿覆盖面积的计算, 消除边缘的采样质量:
WINGDIAPI void APIENTRY glHint (GLenum target, GLenum mode);
target指定采样对象为点线面,或者纹理:
#define GL_PERSPECTIVE_CORRECTION_HINT 0x0C50
#define GL_POINT_SMOOTH_HINT 0x0C51
#define GL_LINE_SMOOTH_HINT 0x0C52
#define GL_POLYGON_SMOOTH_HINT 0x0C53
#define GL_FOG_HINT 0x0C54
值为:
/* HintMode */
#define GL_DONT_CARE 0x1100
#define GL_FASTEST 0x1101
#define GL_NICEST 0x1102
能够在图像渲染质量和效率之间取到一个平衡。
3)启用混合
使得边缘多次采样后,前后采样重复的像素之间用混合处理(要和着色模式和glHint函数结合使用),边缘的alpha值更小,用融合避免锯齿。
所有Blend混合在索引颜色模式下是非法的, 至于颜色索引模式的抗锯齿的也是一样的,但是不能有混合而是使用包含alpha的,所以抗锯齿也不能很好的支持。
glEnable (GL_LINE_SMOOTH);
glHint (GL_LINE_SMOOTH_HINT, GL_DONT_CARE);
glEnable (GL_BLEND);
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
实例代码:
#include <GL/glut.h>
#include <stdlib.h>
#include <stdio.h>
static float rotAngle = 0.;
/* Initialize antialiasing for RGBA mode, including alpha
* blending, hint, and line width. Print out implementation
* specific info on line width granularity and width.
*/
void init(void)
{
GLfloat values[2];
glGetFloatv (GL_LINE_WIDTH_GRANULARITY, values);
printf ("GL_LINE_WIDTH_GRANULARITY value is %3.1f\n", values[0]);
glGetFloatv (GL_LINE_WIDTH_RANGE, values);
printf ("GL_LINE_WIDTH_RANGE values are %3.1f %3.1f\n",
values[0], values[1]);
glEnable(GL_LINE_SMOOTH);
glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
glEnable (GL_BLEND);
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glLineWidth (1.5);
glClearColor(0.0, 0.0, 0.0, 0.0);
}
/* Draw 2 diagonal lines to form an X
*/
void display(void)
{
glClear(GL_COLOR_BUFFER_BIT);
glColor3f (0.0, 1.0, 0.0);
glPushMatrix();
glRotatef(-rotAngle, 0.0, 0.0, 0.1);
glBegin (GL_LINES);
glVertex2f (-0.5, 0.5);
glVertex2f (0.5, -0.5);
glEnd ();
glPopMatrix();
glColor3f (0.0, 0.0, 1.0);
glPushMatrix();
glRotatef(rotAngle, 0.0, 0.0, 0.1);
glBegin (GL_LINES);
glVertex2f (0.5, 0.5);
glVertex2f (-0.5, -0.5);
glEnd ();
glPopMatrix();
glFlush();
}
void reshape(int w, int h)
{
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if (w <= h)
gluOrtho2D (-1.0, 1.0,
-1.0*(GLfloat)h/(GLfloat)w, 1.0*(GLfloat)h/(GLfloat)w);
else
gluOrtho2D (-1.0*(GLfloat)w/(GLfloat)h,
1.0*(GLfloat)w/(GLfloat)h, -1.0, 1.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void keyboard(unsigned char key, int x, int y)
{
switch (key) {
case 'r':
case 'R':
rotAngle += 20.;
if (rotAngle >= 360.) rotAngle = 0.;
glutPostRedisplay();
break;
case 27: /* Escape Key */
exit(0);
break;
default:
break;
}
}
/* Main Loop
* Open window with initial window size, title bar,
* RGBA display mode, and handle input events.
*/
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize (200, 200);
glutCreateWindow (argv[0]);
init();
glutReshapeFunc (reshape);
glutKeyboardFunc (keyboard);
glutDisplayFunc (display);
glutMainLoop();
return 0;
}
2.点和线使用多重采样抗锯齿
OGL推荐的方式,一个片段有多组信息并不是单一采样中的一个(多个颜色,多个深度值,多个纹理坐标),这些片段的信息来自多个附近的采样位置,再对这些信息进行覆盖着色计算(按照权重),而不是根据周边填充颜色进行alpha混合,用更多的空间信息组合的着色算法来解决问题。
前提是创建一个支持多重采样的窗口,得到一个多重采样的缓存区。
glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB | GLUT_MULTISAMPLE);
2)检查GPU支持多重采样
draw call之前启用多重采样即可。OGL会在片段光栅化时候自动进行多重采样和采样后的颜色元素组合得到抗锯齿的效果。
OGL多重采样下,无法对采样的样本数量进行更改,也无法查询子像素的采样位置,点线面的着色模式也会被忽略,多重采样下默认支持GL_POINT_SMOOTH, GL_LINE_SMOOTH设置否都会平滑像素大小, 点的大小和直线的宽度设置也是受到多重采样支持的,设置是起作用的。
glEnable (GL_MULTISAMPLE_ARB); // 多重采样其实边缘还是有些锯齿的,不管使用片段alpha否。
默认情况下多重采样得到的覆盖颜色值是独立于片段的alpha值的,但下面这些状态被启用时候,多重采样最终颜色覆盖值会考虑alpha值因素。
#define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E // 使用片段的alpha值来计算最终的覆盖颜色值
#define GL_SAMPLE_ALPHA_TO_ONE 0x809F // 把片段的alpha值设置为1,然后多重采样计算覆盖颜色值时使用这个值
// 使用glSampleCoverage函数所设置的值,这个值与经过计算产生的覆盖值进行组合(And操作)
// glSampleCoverage:
typedef void (GLAPIENTRY * PFNGLSAMPLECOVERAGEPROC) (GLclampf value, GLboolean invert);
// value是自己指定的和多重采样组合时候使用的alpha值,invert表示是否在和覆盖颜色值AND之前先翻转value值。
#define GL_SAMPLE_COVERAGE 0x80A0
#define GLEW_STATIC
#include <Gl/glew.h>
#include <GL/glut.h>
#include <stdlib.h>
#include <stdio.h>
#pragma comment(lib, "glew32s.lib")
static int bgtoggle = 1;
/*
* Print out state values related to multisampling.
* Create display list with "pinwheel" of lines and
* triangles.
*/
void init(void)
{
static GLint buf[1], sbuf[1];
int i, j;
glClearColor(0.0, 0.0, 0.0, 0.0);
glGetIntegerv (GL_SAMPLE_BUFFERS_ARB, buf);
printf ("number of sample buffers is %d\n", buf[0]);
glGetIntegerv (GL_SAMPLES_ARB, sbuf);
printf ("number of samples is %d\n", sbuf[0]);
glNewList (1, GL_COMPILE);
for (i = 0; i < 19; i++) {
glPushMatrix();
glRotatef(360.0*(float)i/19.0, 0.0, 0.0, 1.0);
glColor3f (1.0, 1.0, 1.0);
glLineWidth((i%3)+1.0);
// 绘制一根白色的线,线的大小交错
glBegin (GL_LINES);
glVertex2f (0.25, 0.05);
glVertex2f (0.9, 0.2);
glEnd ();
// 在旋转后的local坐标系下绘制一个三角形,填充为浅蓝色
glColor3f (0.0, 1.0, 1.0);
glBegin (GL_TRIANGLES);
glVertex2f (0.25, 0.0);
glVertex2f (0.9, 0.0);
glVertex2f (0.875, 0.10);
glEnd ();
glPopMatrix();
}
glEndList ();
// 填充黄色方块,和底色黑色交错显示
glNewList (2, GL_COMPILE);
glColor3f (1.0, 0.5, 0.0);
glBegin (GL_QUADS);
for (i = 0; i < 16; i++) {
for (j = 0; j < 16; j++) {
if (((i + j) % 2) == 0) {
glVertex2f (-2.0 + (i * 0.25), -2.0 + (j * 0.25));
glVertex2f (-2.0 + (i * 0.25), -1.75 + (j * 0.25));
glVertex2f (-1.75 + (i * 0.25), -1.75 + (j * 0.25));
glVertex2f (-1.75 + (i * 0.25), -2.0 + (j * 0.25));
}
}
}
glEnd ();
glEndList ();
}
/* Draw two sets of primitives, so that you can
* compare the user of multisampling against its absence.
*
* This code enables antialiasing and draws one display list
* and disables and draws the other display list
*/
void display(void)
{
glClear(GL_COLOR_BUFFER_BIT);
if (bgtoggle)
glCallList (2);
// 只是多了一句启用多重采样,那么就可以实现抗锯齿效果
glEnable (GL_MULTISAMPLE_ARB);
glPushMatrix();
glTranslatef (-1.0, 0.0, 0.0);
glCallList (1);
glPopMatrix();
glDisable (GL_MULTISAMPLE_ARB);
glPushMatrix();
glTranslatef (1.0, 0.0, 0.0);
glCallList (1);
glPopMatrix();
glutSwapBuffers();
}
void reshape(int w, int h)
{
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if (w <= (2 * h))
gluOrtho2D (-2.0, 2.0,
-2.0*(GLfloat)h/(GLfloat)w, 2.0*(GLfloat)h/(GLfloat)w);
else
gluOrtho2D (-2.0*(GLfloat)w/(GLfloat)h,
2.0*(GLfloat)w/(GLfloat)h, -2.0, 2.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void keyboard(unsigned char key, int x, int y)
{
switch (key) {
case 'b':
case 'B':
bgtoggle = !bgtoggle;
glutPostRedisplay();
break;
case 27: /* Escape Key */
exit(0);
default:
break;
}
}
/* Main Loop
* Open window with initial window size, title bar,
* RGBA display mode, and handle input events.
*/
int main(int argc, char** argv)
{
glutInit(&argc, argv);
// 显示器窗口使用多重采样
glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB | GLUT_MULTISAMPLE);
glutInitWindowSize (600, 300);
glutCreateWindow (argv[0]);
init();
glutReshapeFunc (reshape);
glutKeyboardFunc (keyboard);
glutDisplayFunc (display);
glutMainLoop();
return 0;
}
3.对多边形进行抗锯齿GL_FILL下用alpha混合,从前往后渲染
使用累积缓冲区实现抗锯齿,使用简单,但是执行缓慢。
使用alpha颜色混合进行抗锯齿,进行多重采样抗锯齿。
alpha颜色混合时在颜色索引模式下对多边形是不能实现抗锯齿的。在RGBA模式下对多边形抗锯齿,可以使用alpha值来表示多边形边缘的覆盖值。
glPolygonMode(GL_FRONT, GL_FILL);//GL_POINT,GL_LINE使用的是点和直线的抗锯齿(alpha混合,或多重采样)
下面讨论的多边形抗锯齿,都是基于GL_FILL模式下的:
对多边形进行抗锯齿的做法,GL_FILL下使用多重采样应该不能对多边形抗锯齿起效 :
glEnable(GL_POLYGON_SMOOTH); //导致多边形边缘的像素根据覆盖值被分配分数形式的alpha值
glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST); // 根据等级计算填充区域
关闭深度缓存区,使得多边形重叠后进来的像素能够得到混合
// 开启混合glEnable (GL_BLEND),glBlendFunc (GL_SRC_ALPHA_SATURATE, GL_ONE);
// GL_SRC_ALPHA_SATURATE = min(GL_SRC_ALPHA, 1 - GL_DST_ALPHA)
绘制场景中所有多边形前,需要从前往后排序多边形,然后从前往后渲染,使得后面进来的多边形混合得到的颜色比例越少.