Summary:本篇讲一下关于OpenGL图形管线的一些基础知识,刚开始的内容会比较枯燥,后面会用几个例子讲解几种渲染方法,下载git上的例子可以在xcode上看到几种渲染的效果。可展示的效果有,正面和背面剔除、深度测试、多边形偏移、裁剪、混合、多重采样。
git地址:点击这里(这里做一下说明,在研究例子的过程中,请无视掉GLTools库,该库是原书作者对gl的一次封装,内部实现接下来会逐层展开,我们现在只讨论与gl有关的内容,后期的话我们可以自己实现封装。)
将点连接起来
上篇讲了运用gl画线函数画出直线与折线,本篇作一些补充。
- 点的大小
void glPointSize(GLfloat size);
该函数可以指定绘制点的像素,不过并不是所有点的大小都能够支持,使用前请确认指定的点大小是可用的;利用下面这段代码可以获得点大小的范围,以及最小间隔:
1 | GLfloat sizes[2]; //存储支持的点的大小范围 |
点总是正方形的像素,改变点大小情况也如此;
另外还可以通过使用程序点大小模式来设置点大小,这样的话需要在顶点着色器或几何着色器代码中设置点的大小,这两种编程方法将在后面讲到;
1 | glEnable(GL_PROGRAM_POINT_SIZE); |
- 线的宽度
void glLineWidth(GLfloat width);
该函数可以指定线段的宽度,它是改变线段宽度的唯一方式;
- 线带
连续的从一个顶点绘制线段,以形成一个连接起来的线带,使用GL_LINE_STRIP
可绘制一组连接起来的线段;
- 线环
如果想要使上面的线带是闭合的,那么使用GL_LINE_LOOP
会是不错的选择;
- 三角形环绕
指定三角形时,点绘制的顺序与方向是不同的,使用这种结合来指定的方式叫做环绕。
默认情况下,OpenGL认为具有逆时针方向环绕的多边形是正面的,如想修改默认的行为:
glFrontFace(GL_CW);
GL_CW
参数告诉OpenGL顺时针环绕多边形将被认为是正面的;如需恢复逆时针,可以使用参数GL_CCW
;
- 三角形带
当我们需要一串相连的三角形时,可以使用GL_TRIANGLE_STRIP
图元绘制相连的三角形,这样可以节省大量时间;
- 三角形扇
使用GL_TRIANGLE_FAN
可以创建一组围绕一个中心点的相连三角形;
本文地址工程目录Primitives展示了以上几种图形的画法;
基础渲染方式
- 油画法
绘制三角形时,如果出现覆盖的情况,通常的做法是对三角形进行排序,然后首先画那些较远的三角形,再在上方渲染那些较近的三角形,这种方式称做“油画法”;这种方法在图形处理中是非常低效的;
- 正面和背面剔除
前面讲到三角形有正面与背面的区分,对其进行区分的原因之一就是为了进行剔除;选择不必要的面进行剔除会极大地提高性能;剔除按如下方式开启:
1 |
|
我们并没有指明剔除的面,如下:
1 |
|
- 深度测试
在绘制一个像素时,将一个值(z值)分配给它,表示它到观察者的距离;当另外一个像素在同样位置进行绘制时,新像素z将与原来的进行比较,我们只绘制z值更小的像素;
启用深度测试:
glEnable(GL_DEPTH_TEST);
- 多边形模式
函数glPolygonMode允许将多边形渲染成实体、轮廓或只有点,而且可以选择在多边形的正反面上启用该模式;
1 |
|
本文例子GeoTest会展示这些效果;
- 多边形偏移
举个例子,我们可能想要绘制一架大型飞机,然后在飞机上一个较小的但却与飞机在同一物理空间的图形,这叫做“贴花”;这个小的图形的深度值将会与原来飞机的深度缓冲区中的值相同,这将导致深度测试不可预料的通过或者失败,这种情况叫做z冲突;
另外一种情况,我们想要在绘制的实心几何图形上突出它的边;以上这些z冲突的情况下,通常我们解决的办法是当深度值相同时,适当的对深度进行偏移而并不改变实际3D空间中的物理位置:
1 | void glPolygonOffset(GLfloat factor, GLfloat units); |
例子Primitives的DrawWireFramedBatch
函数代码中展示了偏移的使用;
1 | ``` |
void glScissor(GLint x, GLint y, GLsizei width, GLsizei height);
//x,y指定裁剪框左下角,width height则指定宽度和高度1
2
例子Scissor展示了该函数的用法:
// Clear blue window
glClearColor(0.0f, 0.0f, 1.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
// Now set scissor to smaller red sub region
glClearColor(1.0f, 0.0f, 0.0f, 0.0f);
glScissor(100, 100, 600, 400);
glEnable(GL_SCISSOR_TEST);
glClear(GL_COLOR_BUFFER_BIT);
// Finally, an even smaller green rectangle
glClearColor(0.0f, 1.0f, 0.0f, 0.0f);
glScissor(200, 200, 400, 200);
glClear(GL_COLOR_BUFFER_BIT);
// Turn scissor back off for next render
glDisable(GL_SCISSOR_TEST);
1 |
|
glBlendFunc(GLenum S, GLenum D);
//S D都是枚举值,详细请自行google,这里给出枚举值1
2
混合方程式并不是唯一的,如需改变则:
void glBlendEquation(GLenum mode);
GL_FUNC_ADD //Cf = (Cs S) + (Cd D)
GL_FUNC_SUBTRACT //Cf = (Cs S) - (Cd D)
GL_FUNC_REVDRSE_SUBTRACT //Cf = (Cs D) - (Cd S)
GL_MIN //Cf = min(Cs,Cd)
GL_MAX //Cf = max(Cs,Cd)1
2
3
4
5
6
7
8
9
10
11
12
13
14
另外:
`void glBlendFuncSeparate(GLenum srcRGB,GLenum dstRGB,GLenum srcAlpha,GLenum dstAlpha);`
可以指定源和目标的RGB颜色与Alpha颜色不同的混合函数;
`vodi glBlendColor(GLclampf red,GLclampf green,GLclampf blue,GLclampf alpha);`
可以指定混合一个常量混合颜色,初始为黑色(0,0,0,0);
### - 抗锯齿
由于像素是正方形的,混合时通常可以相当清楚地看到两种颜色的分界,它们常常被称为锯齿,为了消除锯齿,使用混合功能并开启锯齿,使边缘变得平滑;具体使用方法如下代码:
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;
}
1 |
|
GL_SAMPLE_ALPHA_TO_COVERAGE //使用alpha值
GL_SAMPLE_ALPHA_TO_ON //将alpha值设为1并使用它
GL_SAMPLE_COVERAGE //使用glSampleCoverage设置的值`
void glSampleCoverage(GLclampf value, GLboolean invert);
函数允许指定一个特定的值,它是与片断覆盖值进行按位与操作的结果;