OpenGL超級寶典筆記——反走樣

反走樣

OpenGL的混合還能夠用於反走樣。在絕大多數狀況下,一個渲染片斷映射到屏幕上的一個像素。在屏幕上的像素是一個小方格。被着色的像素和未被着色的像素區分很是地明顯。在這種狀況下,可能會產生鋸齒。鋸齒是計算機生成圖像的嚴重缺陷,使得圖像看起來不天然。函數

image

(沒有開啓反走樣)oop

image

(開啓了反走樣)性能

爲了消除圖元的鋸齒,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;
}

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


image

相關文章
相關標籤/搜索