OpenGL的混合還可以用於反走樣。在絕大多數情況下,一個渲染片段映射到屏幕上的一個像素。在屏幕上的像素是一個小方格。被著色的像素和未被著色的像素區分非常地明顯。在這種情況下,可能會產生鋸齒。鋸齒是計算機生成圖像的嚴重缺陷,使得圖像看起來不自然。
(沒有開啟反走樣)
(開啟了反走樣)
為了消除圖元的鋸齒,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;
}
反走樣可以是圖元的邊緣變得平滑,看起更加自然和逼真。點和線的平滑處理被廣泛的支持,但是多邊形的平滑處理並不是在所有的平台上都得到支持。即使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;
}
OpenGL超級寶典 第4版 中文版PDF+英文版+源代碼 見 http://www.linuxidc.com/Linux/2013-10/91413.htm
OpenGL編程指南(原書第7版)中文掃描版PDF 下載 http://www.linuxidc.com/Linux/2012-08/67925.htm
OpenGL 渲染篇 http://www.linuxidc.com/Linux/2011-10/45756.htm
Ubuntu 13.04 安裝 OpenGL http://www.linuxidc.com/Linux/2013-05/84815.htm
OpenGL三維球體數據生成與繪制【附源碼】 http://www.linuxidc.com/Linux/2013-04/83235.htm
Ubuntu下OpenGL編程基礎解析 http://www.linuxidc.com/Linux/2013-03/81675.htm
如何在Ubuntu使用eclipse for c++配置OpenGL http://www.linuxidc.com/Linux/2012-11/74191.htm
更多《OpenGL超級寶典學習筆記》相關知識 見 http://www.linuxidc.com/search.aspx?where=nkey&keyword=34581