OpenGL的混合还能够用于反走样。在绝大多数状况下,一个渲染片断映射到屏幕上的一个像素。在屏幕上的像素是一个小方格。被着色的像素和未被着色的像素区分很是地明显。在这种状况下,可能会产生锯齿。锯齿是计算机生成图像的严重缺陷,使得图像看起来不天然。函数
(没有开启反走样)oop
(开启了反走样)性能
为了消除图元的锯齿,OpenGL使用混合把像素的目标颜色与周边像素的颜色进行混合。在图元的边缘上,像素的颜色会稍微延伸到相邻的像素上。spa
开启反走样,首先要开启alpha混合。.net
glEnable(GL_BLEND);3d
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);code
固然还能够经过glBlendEquation来改变混合方程。默认状况下是混合方程被设置为GL_ADD. 而后选择开启点反走样,线反走样,多边形反走样。orm
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; }
反走样能够是图元的边缘变得平滑,看起更加天然和逼真。点和线的平滑处理被普遍的支持,可是多边形的平滑处理并非在全部的平台上都获得支持。即便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);
效果对比:
当开启多重采样时,点、线、多边形的平滑处理将会被忽略。即点、线、多边形的平滑处理不能和多重采样同时存在。在给定的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 30 GLfloat 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; }