OpenGL超级宝典笔记——反走样

来源:互联网 发布:linux怎么解压缩zip 编辑:程序博客网 时间:2024/05/14 13:42

转自:http://my.oschina.net/sweetdark/blog/170634

OpenGL超级宝典笔记——反走样

反走样

OpenGL的混合还可以用于反走样。在绝大多数情况下,一个渲染片段映射到屏幕上的一个像素。在屏幕上的像素是一个小方格。被着色的像素和未被着色的像素区分非常地明显。在这种情况下,可能会产生锯齿。锯齿是计算机生成图像的严重缺陷,使得图像看起来不自然。

image

(没有开启反走样)

image

(开启了反走样)

为了消除图元的锯齿,OpenGL使用混合把像素的目标颜色与周边像素的颜色进行混合。在图元的边缘上,像素的颜色会稍微延伸到相邻的像素上。

开启反走样,首先要开启alpha混合。

glEnable(GL_BLEND);

glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

当然还可以通过glBlendEquation来改变混合方程。默认情况下是混合方程被设置为GL_ADD. 然后选择开启点反走样,线反走样,多边形反走样。

glEnable(GL_POINT_SMOOTH);glEnable(GL_LINE_SMOOTH);glEnable(GL_POLYGON_SMOOTH);

 

在使用GL_POLYGON_SMOOTH的时候要注意,未必能够使实心几何图元的边缘变得平滑,要实现这个目的还需要一些其他的工作。对实心物体进行抗锯齿处理并不常用,而且在很大程度上被多重采样的方法替代。

示例程序(可以通过右键菜单来切换反走样模式):

#include "gltools.h" #include <math.h>  #include "math3d.h"  //屏幕的宽,高  #define SCREEN_X 800  #define SCREEN_Y 600  //大中小星星的数量  #define LARGE_NUM 20  #define MEDIUM_NUM 30  #define SMALL_NUM 40  //星星的坐标  M3DVector2f smallStars[SMALL_NUM];M3DVector2f mediumStars[MEDIUM_NUM];M3DVector2f largeStars[LARGE_NUM]; void ChangeSize(GLsizei w, GLsizei h){ if (h == 0)    h = 1;  glViewport(0, 0, w, h);  glMatrixMode(GL_PROJECTION);  glLoadIdentity(); //设置为2D的正投影,使得坐标从屏幕的左下角开始 gluOrtho2D(0.0, SCREEN_X, 0.0, SCREEN_Y);  glMatrixMode(GL_MODELVIEW);  glLoadIdentity();  glutPostRedisplay();} void SetupRC(){  glClearColor(0.0f, 0.0f, 0.0f, 1.0f);   //随机获取星星的位置    for (int i = 0; i < SMALL_NUM; ++i)  {    smallStars[i][0] = (GLfloat)(rand() % SCREEN_X);    smallStars[i][1] = (GLfloat)(rand() % SCREEN_Y);  }   for (int i = 0; i < MEDIUM_NUM; ++i)  {    mediumStars[i][0] = (GLfloat)(rand() % SCREEN_X);    mediumStars[i][1] = (GLfloat)((rand() % SCREEN_Y) + 50);  }   for (int i = 0; i < LARGE_NUM; ++i)  {    largeStars[i][0] = (GLfloat)(rand() % SCREEN_X);    largeStars[i][1] = (GLfloat)(rand() % SCREEN_Y);  }} void RenderScene(){  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);  glColor3f(1.0f, 1.0f, 1.0f);   //画小星星   glPointSize(1.5);  glBegin(GL_POINTS);   for (int i = 0; i < SMALL_NUM; ++i)      glVertex2fv(smallStars[i]);  glEnd();   //画中等大小的星星   glPointSize(3.5);  glBegin(GL_POINTS);   for (int i = 0; i < MEDIUM_NUM; ++i)    {      glVertex2fv(mediumStars[i]);    }  glEnd();   //大星星   glPointSize(5.5);  glBegin(GL_POINTS);   for (int i = 0; i < LARGE_NUM; ++i)  {    glVertex2fv(largeStars[i]);  }  glEnd();   //画月亮   GLfloat angle = 0.0;  GLfloat xCircle = 650.0f;  GLfloat yCircle = 400.0f;  GLfloat r = 80.0f;  glBegin(GL_TRIANGLE_FAN);    glVertex2f(xCircle, yCircle);     for (angle = 0.0f; angle < 2.0f * 3.14159f; angle += 0.1f)    {      glVertex2f(xCircle + (float)cos(angle) * r, yCircle + (float)sin(angle) * r);    }    glVertex2f(xCircle + r, yCircle);  glEnd();   //星座连线   glLineWidth(3.0);  glBegin(GL_LINE_STRIP);    glVertex2f(0.0f, 50.0f);    glVertex2f(50.0f, 150.0f);    glVertex2f(100.0f, 20.0f);    glVertex2f(300.0f, 300.0f);    glVertex2f(450.0f, 100.0f);    glVertex2f(600.0f, 200.0f);    glVertex2f(800.0f, 30.0f);  glEnd();  glutSwapBuffers();} void ProcessMenu(int value){ switch (value)  {   case 1:    { //开启混合     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);      glEnable(GL_BLEND);      glEnable(GL_POINT_SMOOTH);      glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);      glEnable(GL_LINE_SMOOTH);      glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);      glEnable(GL_POLYGON_SMOOTH);      glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST); break;    }     case 2:    { //关闭混合     glDisable(GL_BLEND);      glDisable(GL_POINT_SMOOTH);      glDisable(GL_LINE_SMOOTH);      glDisable(GL_POLYGON_SMOOTH); break;    } default: break;  }  glutPostRedisplay();} int main(int args, char **argv){  glutInit(&args, argv);  glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);  glutInitWindowSize(SCREEN_X, SCREEN_Y);  glutCreateWindow("smoother");   //右键菜单   int menuID = glutCreateMenu(ProcessMenu);  glutAddMenuEntry("antialiasing", 1);  glutAddMenuEntry("normal", 2);  glutAttachMenu(GLUT_RIGHT_BUTTON);  glutDisplayFunc(RenderScene);  glutReshapeFunc(ChangeSize);  SetupRC();  glutMainLoop(); return 0;}

image

多重采样

反走样可以是图元的边缘变得平滑,看起更加自然和逼真。点和线的平滑处理被广泛的支持,但是多边形的平滑处理并不是在所有的平台上都得到支持。即使GL_POLYGON_SMOOTH是可用的,但是使用起来没有你想象中的那么方便。因为是基于混合操作的,你需要对图元从前到后进行排序。

OpenGl增加了一个新特性 多重采样 (OpenGL1.3以上的版本) 来解决这个问题。

如果多重采样被支持的话,会在已经包含颜色、深度、模板值的帧缓冲区添加一个额外的缓冲区中。图元上的每一个像素会被多次采样,结果存储到这个缓冲区中。每次像素的更新,样本会被重新解析出一个值。当然,这会产生额外的内存和处理器的开销。

要使用多重采样,首先要获得一个支持多重采样缓冲区的渲染环境。在GLUT中可以在glutInitDisplayMode中,增加一个GLUT_MULTISAMPLE字段来获得一个多重采样的渲染环境。

glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGB | GLUT_DEPTH | GLUT_MULTISMAPLE);

开启或关闭多重采样:

glEnable(GL_MULTSAMPLE); glDisable(GL_MULTSAMPLE);

效果对比:

image 

当开启多重采样时,点、线、多边形的平滑处理将会被忽略。即点、线、多边形的平滑处理不能和多重采样同时存在。在给定的OpenGL实现上,如果点和线的平滑处理效果会比多重采样效果更好。那可以先关闭多重采样,使用点和线的平滑效果,然后再开启多重采样用于处理实心图元的锯齿。

glEnable(GL_POINT_SMOOTH);glDisable(GL_MULTISAMPLE); //.. 画点 //.. glDisable(GL_POINT_SMOOTH);glEnable(GL_MULTISAMPLE);


如果没有设置多重采样的缓冲区,多重采样将不可用。

PS:打开或关闭OpenGL的特性会修改驱动程序的内部状态,这种状态的改变可能会对渲染的性能造成影响。为了提升性能,一般会把相同状态的绘制命令放在一起(状态排序)

多重采样缓冲区默认情况下使用片段的RGB值,不包含alpha值。可以用glEnable来改变。

GL_SAMPLE_ALPHA_TO_COVERAGE ——使用alpha值

GL_SAMPLE_ALPHA_TO_ONE —— 设置alpha值为1,并使用它。

GL_SAMPLE_COVERAGE——使用glSampleCoverage的设置。

当启用了GL_SAMPLE_COVERAGE时,glSampleCoverage函数指定了一个特定的值,它会和片段覆盖值进行与操作。

void glSampleCoverage(GLclampf value, GLboolean invert);

具体的多重采样的效果和OpenGl的具体实现有关。

多重采样示例:

#include "gltools.h" #include "math3d.h" #include "glframe.h" #include <math.h> #define SPHERE_NUM 30GLfloat fNoLight[] = {0.0f, 0.0f, 0.0f, 1.0f};GLfloat fLowLight[] = {0.25f, 0.25f, 0.25f, 1.0f};GLfloat fBrightLight[] = {1.0f, 1.0f, 1.0f, 1.0f};GLfloat fLightPos[4] = {-100.0f, 100.0f, 50.0f, 1.0f};GLFrame camara;GLFrame sphere[SPHERE_NUM];M3DMatrix44f mShadowMatrix; void SetupRC(){  glClearColor(fLowLight[0], fLowLight[1], fLowLight[2], fLowLight[3]);  M3DVector3f vPoints[3] = {{0.0f, -0.4f, 0.0f},  { 10.0f, -0.4f, 0.0f  }, { 5.0f, -0.4f, -5.0f} };   int iSphere;   //剔除多边形背面   glCullFace(GL_BACK);  glFrontFace(GL_CCW);  glEnable(GL_CULL_FACE);  glEnable(GL_DEPTH);   //设置光照   glLightModelfv(GL_LIGHT_MODEL_AMBIENT, fNoLight);  glLightfv(GL_LIGHT0, GL_AMBIENT, fLowLight);  glLightfv(GL_LIGHT0, GL_DIFFUSE, fBrightLight);  glLightfv(GL_LIGHT0, GL_SPECULAR, fBrightLight);  glEnable(GL_LIGHTING);  glEnable(GL_LIGHT0);   //用平面上的3个点来取得平面的矩阵   M3DVector4f vPlaneEquation;  m3dGetPlaneEquation(vPlaneEquation, vPoints[0], vPoints[1], vPoints[2]);   //计算投影矩阵   m3dMakePlanarShadowMatrix(mShadowMatrix, vPlaneEquation, fLightPos);   //开启颜色追踪   glEnable(GL_COLOR_MATERIAL);  glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);  glMateriali(GL_FRONT, GL_SHININESS, 128);   //随机产生球体的位置   for (iSphere = 0; iSphere < SPHERE_NUM; iSphere++)  {    sphere[iSphere].SetOrigin((float)(((rand() % 400) - 200) * 0.1f), 0.0f,      (float)((rand() % 400) - 200) * 0.1f);  }   //开启多重采样,默认是开启的   glEnable(GL_MULTISAMPLE);} void DrawGround(){  GLfloat fExtent = 20.0f;  GLfloat step = 1.0f;  GLfloat y = -0.4f;  GLfloat x, z; for (x = -fExtent; x <= fExtent; x += step)  {    glBegin(GL_TRIANGLE_STRIP);      glNormal3f(0.0f, 1.0f, 0.0f); for (z = fExtent; z >= -fExtent; z -= step)      {        glVertex3f(x, y, z);        glVertex3f(x + step, y, z);      }    glEnd();  }} void DrawInhabitants(GLint nShadow){   static GLfloat yRot = 0.0f;  GLint i;   //判断是否是阴影   if (nShadow == 0)  {    yRot += 0.5f;    glColor3f(0.0f, 1.0f, 0.0f);  } else {    glColor3f(0.0f, 0.0f, 0.0f);  }   //画球体   for (i = 0; i < SPHERE_NUM; i++)  {    glPushMatrix();    sphere[i].ApplyActorTransform();    glutSolidSphere(0.3f, 17, 9);    glPopMatrix();  }  glPushMatrix();   //平移   glTranslatef(0.0f, 0.1f, -2.5f);   if (nShadow == 0)    {      glColor3f(0.0f, 0.0f, 1.0f);    }     //旋转的球体     glPushMatrix();      glRotatef(-yRot * 2.0f, 0.0f, 1.0f, 0.0f);      glTranslatef(1.0f, 0.0f, 0.0f);      glutSolidSphere(0.1f, 17, 9);    glPopMatrix();     //非阴影,开启镜面全反射     if (nShadow == 0)    {      glColor3f(1.0f, 0.0f, 0.0f);      glMaterialfv(GL_FRONT, GL_SPECULAR, fBrightLight);    }     //画花环     glRotatef(yRot, 0.0f, 1.0f, 0.0f);    gltDrawTorus(0.35, 0.15, 61, 37);    glMaterialfv(GL_FRONT, GL_SPECULAR, fNoLight);  glPopMatrix();} void RenderScene(){  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);  glPushMatrix();   //应用照相机变换   camara.ApplyCameraTransform();   //设置光源位置   glLightfv(GL_LIGHT0, GL_POSITION, fLightPos);   //地面颜色   glColor3f(0.6f, 0.4f, 0.1f);   //画地面   DrawGround();   //画阴影, 关闭光照   glDisable(GL_DEPTH_TEST);    glDisable(GL_LIGHTING);    glPushMatrix();     //乘以阴影矩阵     glMultMatrixf(mShadowMatrix);      DrawInhabitants(1);    glPopMatrix();     //开启光照     glEnable(GL_LIGHTING);    glEnable(GL_DEPTH_TEST);    DrawInhabitants(0);  glPopMatrix();  glutSwapBuffers();} void ChangeSize(GLsizei w, GLsizei h){ if (h == 0)    h = 1;  glViewport(0, 0, w, h);  GLfloat faspect = (GLfloat)w/(GLfloat)h;  glMatrixMode(GL_PROJECTION);  glLoadIdentity();    gluPerspective(35.0f, faspect, 1.0f, 50.0f);  glMatrixMode(GL_MODELVIEW);  glLoadIdentity();  glutPostRedisplay();} void TimerFunc(int value){  glutPostRedisplay();  glutTimerFunc(10, TimerFunc, 1);} void ProcessMenu(int value){ if (value == 1)  {    glEnable(GL_MULTISAMPLE);  } else {    glDisable(GL_MULTISAMPLE);  }  glutPostRedisplay();} int main(int args, char **argv){  glutInit(&args, argv);  glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE | GLUT_MULTISAMPLE);  glutInitWindowSize(800, 600);  glutCreateWindow("multisample");  glutDisplayFunc(RenderScene);  glutReshapeFunc(ChangeSize);  glutTimerFunc(30, TimerFunc, 1);  SetupRC();   int menuID = glutCreateMenu(ProcessMenu);  glutAddMenuEntry("enable multisample", 1);  glutAddMenuEntry("disable multisample", 2);  glutAttachMenu(GLUT_RIGHT_BUTTON);  glutMainLoop();   return 0;}


image

0 0
原创粉丝点击