OpenGL反走样/抗锯齿实例

摘自:http://dxwang.blog.51cto.com/384651/693834

摘录的原因:详细分析了什么是反走样现象,并且提供了混合blend的解释,非常清晰易懂。很多接触图像学的童靴在概念上往往被戏弄得摸不着北!!

1. 反走样

       在光栅图形显示器上绘制非水平且非垂直的直线或多边形边界时,或多或少会呈现锯齿状或台阶状外观。这是因为直线、多边形、色彩边界等是连续的,而光栅则是由离散的点组成,在光栅显示设备上表现直线、多边形等,必须在离散位置采样。由于采样不充分重建后造成的信息失真,就叫走样(aliasing)。而用于减少或消除这种效果的技术,就称为反走样(antialiasing)。

2. OpenGL反走样的实现

OpengL中的反走样采用的是融合的技术,来实现点、线和图形的边沿以及雾和颜色和纹理的插值运算。 OpenGL实现反走样需要满足两个条件,一是启用混合,二是启用针对几何图元的反走样处理。

3. OpenGL混合Blend

混合是什么呢?混合就是把两种颜色混在一起。具体一点,就是把某一像素位置原来的颜色和将要画上去的颜色,通过某种方式混在一起,从而实现特殊的效果。
假设我们需要绘制这样一个场景:透过红色的玻璃去看绿色的物体,那么可以先绘制绿色的物体,再绘制红色玻璃。在绘制红色玻璃的时候,利用“混合”功能,把将要绘制上去的红色和原来的绿色进行混合,于是得到一种新的颜色,看上去就好像玻璃是半透明的。


要使用OpenGL的混合功能,只需要调用:glEnable(GL_BLEND);即可。
要关闭OpenGL的混合功能,只需要调用:glDisable(GL_BLEND);即可。

glBlendFunc(源因子, 目标因子)完成混合方法的定义。

举例来说:
如果设置了glBlendFunc(GL_ONE, GL_ZERO);,则表示完全使用源颜色,完全不使用目标颜色,因此画面效果和不使用混合的时候一致(当然效率可能会低一点点)。如果没有设置源因子和目标因子,则默认情况就是这样的设置。
如果设置了glBlendFunc(GL_ZERO, GL_ONE);,则表示完全不使用源颜色,因此无论你想画什么,最后都不会被画上去了。(但这并不是说这样设置就没有用,有些时候可能有特殊用途)
如果设置了glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);,则表示源颜色乘以自身的alpha 值,目标颜色乘以1.0减去源颜色的alpha值,这样一来,源颜色的alpha值越大,则产生的新颜色中源颜色所占比例就越大,而目标颜色所占比例则减小。这种情况下,我们可以简单的将源颜色的alpha值理解为“不透明度”。这也是混合时最常用的方式。
如果设置了glBlendFunc(GL_ONE, GL_ONE);,则表示完全使用源颜色和目标颜色,最终的颜色实际上就是两种颜色的简单相加。例如红色(1, 0, 0)和绿色(0, 1, 0)相加得到(1, 1, 0),结果为***。
注意:
所谓源颜色和目标颜色,是跟绘制的顺序有关的。假如先绘制了一个红色的物体,再在其上绘制绿色的物体。则绿色是源颜色,红色是目标颜色。如果顺序反过来,则红色就是源颜色,绿色才是目标颜色。在绘制时,应该注意顺序,使得绘制的源颜色与设置的源因子对应,目标颜色与设置的目标因子对应。不要被混乱的顺序搞晕了。


三维混合的注意事项
问题:也许你迫不及待的想要绘制一个三维的带有半透明物体的场景了。但是现在恐怕还不行,还有一点是在进行三维场景的混合时必须注意的,那就是深度缓冲。深度缓冲是这样一段数据,它记录了每一个像素距离观察者有多近。在启用深度缓冲测试的情况下,如果将要绘制的像素比原来的像素更近,则像素将被绘制。否则,像素就会被忽略掉,不进行绘制。这在绘制不透明的物体时非常有用――不管是先绘制近的物体再绘制远的物体,还是先绘制远的物体再绘制近的物体,或者干脆以混乱的顺序进行绘制,最后的显示结果总是近的物体遮住远的物体。然而在你需要实现半透明效果时,发现一切都不是那么美好了。如果你绘制了一个近距离的半透明物体,则它在深度缓冲区内保留了一些信息,使得远处的物体将无法再被绘制出来。虽然半透明的物体仍然半透明,但透过它看到的却不是正确的内容了。

方案:要解决以上问题,需要在绘制半透明物体时将深度缓冲区设置为只读,这样一来,虽然半透明物体被绘制上去了,深度缓冲区还保持在原来的状态。如果再有一个物体出现在半透明物体之后,在不透明物体之前,则它也可以被绘制(因为此时深度缓冲区中记录的是那个不透明物体的深度)。以后再要绘制不透明物体时,只需要再将深度缓冲区设置为可读可写的形式即可。嗯?你问我怎么绘制一个一部分半透明一部分不透明的物体?这个好办,只需要把物体分为两个部分,一部分全是半透明 的,一部分全是不透明的,分别绘制就可以了。
注意: 即使使用了以上技巧,我们仍然不能随心所欲的按照混乱顺序来进行绘制。必须是先绘制不透明的物体,然后绘制透明的物体。否则,假设背景为蓝色,近处一块红色玻璃,中间一个绿色物体。如果先绘制红色半透明玻璃的话,它先和蓝色背景进行混合,则以后绘制中间 的绿色物体时,想单独与红色玻璃混合已经不能实现了。

总结:绘制顺序就是首先绘制所有不透明的物体。如果两个物体都是不透明的,则谁先谁后都没有关系。然后,将深度缓冲区设置为只读。接下来,绘制所有半透明的物体。如果两个物体都是半透明的,则谁先谁后只需要根据自己的意愿(注意了,先绘制的将成为“目标颜色”,后绘制的将成为“源颜色”,所以绘制的顺序将会对结果造成一些影响)。最后,将深度缓冲区设置为可读可写形式。

调用glDepthMask(GL_FALSE);可将深度缓冲区设置为只读形式。调用glDepthMask(GL_TRUE);可将深度缓冲区设置为可读可写形式。

4.OpenGL的反走样函数

void glHint(GLenum target,GLenum hint);

        hint定义了反走样的方法
         GL_FASTEST 给出最有效的选择
         GL_NICEST 给出最高质量的选择
         GL_DONT_CARE 没有选择

         target定义反走样的对象


        GL_POINT_SMOOTH_HINT 指定点


           GL_LINE_SMOOTH_HINT 线
           GL_POLYGON_SMOOTH_HINT 多边形的采样质量
           GL_FOG_HINT 指出雾化计算是按每个象素进行(GL_NICEST),还是按每个顶点进行(GL_FASTEST)
           GL_PERSPECTIVE_CORRECTION_HINT 指定颜色纹理插值的质量
          其中GL_PERSPECTIVE_CORRECTION_HINT用以纠正单纯线性插值带来的观察错误。

5.OpenGL的反走样实例1---点的反走样

#include<windows.h>

#include<gl/glut.h>

#include<math.h>

#define x_Screen 800

#define y_Screen    600

#define little 50

#define middle 20

#define large   8

void myBackground()

{

glClearColor(0.0,0.0,0.0,1.0);

glColor3f(1.0,1.0,1.0);

}


void myDisplay()

{

glEnable ( GL_DEPTH_TEST );

//如果没有抗锯齿,则点为方形的。如果我们启动抗锯齿设置,则点是一个圆点。

glEnable(GL_POINT_SMOOTH);

glEnable(GL_LINE_SMOOTH);

glHint(GL_POINT_SMOOTH_HINT, GL_NICEST); // Make round points, not square points

glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);  // Antialias the lines

glEnable(GL_BLEND);

glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);


glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

int i;

glBegin(GL_POINTS);

for(i=0;i<little;i++)

glVertex2f(50.0+rand()%x_Screen,50.0+rand()%y_Screen);

glEnd();

glPointSize(2);

glBegin(GL_POINTS);

for(i=0;i<middle;i++)

glVertex2f(50.0+rand()%x_Screen,50.0+rand()%y_Screen);

glEnd();

glPointSize(8);

glBegin(GL_POINTS);

for(i=0;i<large;i++)

glVertex2f(50.0+rand()%x_Screen,50.0+rand()%y_Screen);

glEnd();


glBegin(GL_POLYGON);

for(i=0;i<64;i++)

glVertex2f(600+50.0*cos((float)i/10) ,500+50.0*sin((float)i/10) );

glEnd();

glLineWidth(3);

glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);

glEnable(GL_BLEND);

glEnable(GL_LINE_SMOOTH);

glBegin(GL_LINE_STRIP);

for(i=0;i<19;i++){

glVertex2f(rand()%10+i*70,rand()%50+50.0+(i%2)*80);

}

glEnd();

glutSwapBuffers();

}

void myChange(int w,int h)

{

glViewport(0,0,w,h);

glMatrixMode(GL_PROJECTION);

glLoadIdentity();

gluOrtho2D(0.0,x_Screen,0.0,y_Screen);

glMatrixMode(GL_MODELVIEW);

glLoadIdentity();

}



void main()

{

glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGB|GLUT_DEPTH);

glutInitWindowSize(x_Screen,y_Screen);

glutCreateWindow("Star");

glutDisplayFunc(myDisplay);

glutReshapeFunc(myChange);

myBackground();

glutMainLoop();

}

6.OpenGL的反走样实例2---线的反走样

#include <GL/glut.h>

#include <stdio.h>


static float rotAngle = 0.;


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);

glEnable(GL_BLEND);

glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE);


glLineWidth(1.5);


glClearColor(0.0, 0.0, 0.0, 0.0);

}


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.0) rotAngle = 0.0;

glutPostRedisplay();

break;

case 27:

exit(0);

break;

default:

break;

}

}


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;

}


你可能感兴趣的:(OpenGL,反走样)