混合功能--抗锯齿源码示例(二维图形)
// Smoother.cpp
// OpenGL SuperBible
// Demonstrates point and line antialiasing
// Program by Richard S. Wright Jr.
#include
// OpenGL toolkit
#include
#ifdef __APPLE__
#include
#else
#define FREEGLUT_STATIC
#include
#endif
GLShaderManager shaderManager;
GLFrustum viewFrustum;
GLBatch smallStarBatch;
GLBatch mediumStarBatch;
GLBatch largeStarBatch;
GLBatch mountainRangeBatch;
GLBatch moonBatch;
// Array of small stars
#define SMALL_STARS 100
#define MEDIUM_STARS 40
#define LARGE_STARS 15
#define SCREEN_X 800
#define SCREEN_Y 600
///
// Reset flags as appropriate in response to menu selections
void ProcessMenu(int value)
{
switch(value)
{
case 1:
// Turn on antialiasing, and give hint to do the best
// job possible.
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:
// Turn off blending and all smoothing
glDisable(GL_BLEND);
glDisable(GL_LINE_SMOOTH);
glDisable(GL_POINT_SMOOTH);
break;
default:
break;
}
// Trigger a redraw
glutPostRedisplay();
}
///
// Called to draw scene
void RenderScene(void)
{
// Clear the window
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Everything is white
GLfloat vWhite [] = { 1.0f, 1.0f, 1.0f, 1.0f };
shaderManager.UseStockShader(GLT_SHADER_FLAT, viewFrustum.GetProjectionMatrix(), vWhite);
// Draw small stars
glPointSize(1.0f);
smallStarBatch.Draw();
// Draw medium sized stars
glPointSize(4.0f);
mediumStarBatch.Draw();
// Draw largest stars
glPointSize(8.0f);
largeStarBatch.Draw();
// Draw the "moon"
moonBatch.Draw();
// Draw distant horizon
glLineWidth(3.5);
mountainRangeBatch.Draw();
moonBatch.Draw();
// Swap buffers
glutSwapBuffers();
}
// This function does any needed initialization on the rendering
// context.
void SetupRC()
{
M3DVector3f vVerts[SMALL_STARS]; // SMALL_STARS is the largest batch we are going to need
int i;
shaderManager.InitializeStockShaders();
// Populate star list
smallStarBatch.Begin(GL_POINTS, SMALL_STARS);
for(i = 0; i < SMALL_STARS; i++)
{
vVerts[i][0] = (GLfloat)(rand() % SCREEN_X);
vVerts[i][1] = (GLfloat)(rand() % (SCREEN_Y - 100)) + 100.0f;
vVerts[i][2] = 0.0f;
}
smallStarBatch.CopyVertexData3f(vVerts);
smallStarBatch.End();
// Populate star list
mediumStarBatch.Begin(GL_POINTS, MEDIUM_STARS);
for(i = 0; i < MEDIUM_STARS; i++)
{
vVerts[i][0] = (GLfloat)(rand() % SCREEN_X);
vVerts[i][1] = (GLfloat)(rand() % (SCREEN_Y - 100)) + 100.0f;
vVerts[i][2] = 0.0f;
}
mediumStarBatch.CopyVertexData3f(vVerts);
mediumStarBatch.End();
// Populate star list
largeStarBatch.Begin(GL_POINTS, LARGE_STARS);
for(i = 0; i < LARGE_STARS; i++)
{
vVerts[i][0] = (GLfloat)(rand() % SCREEN_X);
vVerts[i][1] = (GLfloat)(rand() % (SCREEN_Y - 100)) + 100.0f;
vVerts[i][2] = 0.0f;
}
largeStarBatch.CopyVertexData3f(vVerts);
largeStarBatch.End();
M3DVector3f vMountains[12] = { 0.0f, 25.0f, 0.0f,
50.0f, 100.0f, 0.0f,
100.0f, 25.0f, 0.0f,
225.0f, 125.0f, 0.0f,
300.0f, 50.0f, 0.0f,
375.0f, 100.0f, 0.0f,
460.0f, 25.0f, 0.0f,
525.0f, 100.0f, 0.0f,
600.0f, 20.0f, 0.0f,
675.0f, 70.0f, 0.0f,
750.0f, 25.0f, 0.0f,
800.0f, 90.0f, 0.0f };
mountainRangeBatch.Begin(GL_LINE_STRIP, 12);
mountainRangeBatch.CopyVertexData3f(vMountains);
mountainRangeBatch.End();
// The Moon
GLfloat x = 700.0f; // Location and radius of moon
GLfloat y = 500.0f;
GLfloat r = 50.0f;
GLfloat angle = 0.0f; // Another looping variable
moonBatch.Begin(GL_TRIANGLE_FAN, 34);
int nVerts = 0;
vVerts[nVerts][0] = x;
vVerts[nVerts][1] = y;
vVerts[nVerts][2] = 0.0f;
for(angle = 0; angle < 2.0f * 3.141592f; angle += 0.2f) {
nVerts++;
vVerts[nVerts][0] = x + float(cos(angle)) * r;
vVerts[nVerts][1] = y + float(sin(angle)) * r;
vVerts[nVerts][2] = 0.0f;
}
nVerts++;
vVerts[nVerts][0] = x + r;;
vVerts[nVerts][1] = y;
vVerts[nVerts][2] = 0.0f;
moonBatch.CopyVertexData3f(vVerts);
moonBatch.End();
// Black background
glClearColor(0.0f, 0.0f, 0.0f, 1.0f );
}
void ChangeSize(int w, int h)
{
// Prevent a divide by zero
if(h == 0)
h = 1;
// Set Viewport to window dimensions
glViewport(0, 0, w, h);
// Establish clipping volume (left, right, bottom, top, near, far)
viewFrustum.SetOrthographic(0.0f, SCREEN_X, 0.0f, SCREEN_Y, -1.0f, 1.0f);
}
int main(int argc, char* argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(800, 600);
glutCreateWindow("Smoothing Out The Jaggies");
// Create the Menu
glutCreateMenu(ProcessMenu);
glutAddMenuEntry("Antialiased Rendering",1);
glutAddMenuEntry("Normal Rendering",2);
glutAttachMenu(GLUT_RIGHT_BUTTON);
glutReshapeFunc(ChangeSize);
glutDisplayFunc(RenderScene);
GLenum err = glewInit();
if (GLEW_OK != err) {
fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
return 1;
}
SetupRC();
glutMainLoop();
return 0;
}
此源码示例运用了混合的功能,展示了平滑像素的效果。对比两个效果图,明显毛刺被平滑了。
源码解析:
此示例实际上是使用点、线以及图元画了一幅星星、月亮和山脉的黑白画,通过开启平滑的功能实现图形的平滑。
#define SMALL_STARS 100
#define MEDIUM_STARS 40
#define LARGE_STARS 15 //定义了各种星星的数量
#define SCREEN_X 800
#define SCREEN_Y 600 //定义窗口大小
1、 void SetupRC()
{
M3DVector3f vVerts[SMALL_STARS]; //定义小星星的顶点
int i;
shaderManager.InitializeStockShaders();
//开始设置小星星的批次图元,并根据小星星的数量,循环设置星星的顶点坐标
smallStarBatch.Begin(GL_POINTS, SMALL_STARS);
for(i = 0; i < SMALL_STARS; i++)
{
vVerts[i][0] = (GLfloat)(rand() % SCREEN_X);
vVerts[i][1] = (GLfloat)(rand() % (SCREEN_Y - 100)) + 100.0f;
vVerts[i][2] = 0.0f;
}
smallStarBatch.CopyVertexData3f(vVerts);
smallStarBatch.End();
mediumStarBatch.Begin(GL_POINTS, MEDIUM_STARS);
for(i = 0; i < MEDIUM_STARS; i++)
{
vVerts[i][0] = (GLfloat)(rand() % SCREEN_X);
vVerts[i][1] = (GLfloat)(rand() % (SCREEN_Y - 100)) + 100.0f;
vVerts[i][2] = 0.0f;
}
mediumStarBatch.CopyVertexData3f(vVerts);
mediumStarBatch.End();
largeStarBatch.Begin(GL_POINTS, LARGE_STARS);
for(i = 0; i < LARGE_STARS; i++)
{
vVerts[i][0] = (GLfloat)(rand() % SCREEN_X);
vVerts[i][1] = (GLfloat)(rand() % (SCREEN_Y - 100)) + 100.0f;
vVerts[i][2] = 0.0f;
}
largeStarBatch.CopyVertexData3f(vVerts);
largeStarBatch.End();
//设置山脉图形的顶点坐标,把顶点连起来看起来像山脉,并设置批次图元
M3DVector3f vMountains[12] = { 0.0f, 25.0f, 0.0f,
50.0f, 100.0f, 0.0f,
100.0f, 25.0f, 0.0f,
225.0f, 125.0f, 0.0f,
300.0f, 50.0f, 0.0f,
375.0f, 100.0f, 0.0f,
460.0f, 25.0f, 0.0f,
525.0f, 100.0f, 0.0f,
600.0f, 20.0f, 0.0f,
675.0f, 70.0f, 0.0f,
750.0f, 25.0f, 0.0f,
800.0f, 90.0f, 0.0f };
//根据上面的12个顶点,画相连的线带 P67
mountainRangeBatch.Begin(GL_LINE_STRIP, 12);
mountainRangeBatch.CopyVertexData3f(vMountains);
mountainRangeBatch.End();
//设置月亮的位置坐标、半径和弧度
GLfloat x = 700.0f;
GLfloat y = 500.0f;
GLfloat r = 50.0f;
GLfloat angle = 0.0f;
//月亮的绘制是使用34个三角形扇拼接而成,
moonBatch.Begin(GL_TRIANGLE_FAN, 34);
//初始化坐标
int nVerts = 0;
vVerts[nVerts][0] = x;
vVerts[nVerts][1] = y;
vVerts[nVerts][2] = 0.0f;
//循环弧度值画圆(2.0f*3.141592f就是2π,即一个圆周)
for(angle = 0; angle < 2.0f * 3.141592f; angle += 0.2f) {
//设置批次图元的坐标(是拼接圆周的三角形的坐标),x和y的坐标等于初始坐标加上弧度的余弦值、弧度的正弦与半径的成绩(),并++三角形扇的个数。
nVerts++;
vVerts[nVerts][0] = x + float(cos(angle)) * r;
vVerts[nVerts][1] = y + float(sin(angle)) * r;
vVerts[nVerts][2] = 0.0f;
}
nVerts++;
//最终的三角形扇图元的拷贝,并完成批次图元的设置
vVerts[nVerts][0] = x + r;
vVerts[nVerts][1] = y;
vVerts[nVerts][2] = 0.0f;
moonBatch.CopyVertexData3f(vVerts);
moonBatch.End();
glClearColor(0.0f, 0.0f, 0.0f, 1.0f );//设置背景的清除颜色是黑色
}
三角形扇:可以看书理解一下三角形扇行程过程 p71
2、void ChangeSize(int w,int h)
在函数中设置投影模式是正投影,设置裁剪范围,在范围内的图形会被显示在屏幕上
viewFrustum.SetOrthographic(0.0f, SCREEN_X, 0.0f, SCREEN_Y, -1.0f, 1.0f);
3、void RenderScene(void)
{glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//设置所有将要渲染的批次图元的颜色都是白色,并选择着色器(平面着色器),提供投影矩阵。把所有批次图元提交给着色器
GLfloat vWhite [] = { 1.0f, 1.0f, 1.0f, 1.0f };
shaderManager.UseStockShader(GLT_SHADER_FLAT, viewFrustum.GetProjectionMatrix(), vWhite);
glPointSize(1.0f); //设置点的大小
smallStarBatch.Draw();
glPointSize(4.0f);
mediumStarBatch.Draw();
glPointSize(8.0f);
largeStarBatch.Draw();
moonBatch.Draw();
glLineWidth(3.5); //设置线宽
mountainRangeBatch.Draw();
moonBatch.Draw();
glutSwapBuffers();
}
4、void ProcessMenu(int value) //在main函数中创建的菜单栏的回调函数
{
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_LINE_SMOOTH);
glDisable(GL_POINT_SMOOTH);
break;
default:
break;
}
//触发重绘(RenderScene)
glutPostRedisplay();
}
小结
抗锯齿(平滑)功能其实是混合功能的一个用途,在开启平滑功能前,要开启混合功能,关闭同样如此。此示例展示2维图形,在ChangeSize方法中设置了正投影的模式。我觉得其中要理解的地方是“月亮”的绘制,它应用了三角形扇的拼接,要理解顶点连接的顺序以及根据圆周的弧度值与弧长来判断顶点的坐标。再就是应用了平面着色器,需要给着色器提供一个投影矩阵。