OpenGL-由实例理解概念

Title: OpenGL-由实例理解概念
Date: 2016-07-10 22:00
Modified: 2016-07-10 22:00
Category: OpenGL/ES
Tags: OpenGL
Slug: opengl_baserender
Authors: lizhonghuan
Summary:本篇讲一下关于OpenGL图形管线的一些基础知识,刚开始的内容会比较枯燥,后面会用几个例子讲解几种渲染方法,下载git上的例子可以在xcode上看到几种渲染的效果。可展示的效果有,正面和背面剔除、深度测试、多边形偏移、裁剪、混合、多重采样。

本篇讲一下关于OpenGL图形管线的一些基础知识,刚开始的内容会比较枯燥,后面会用几个例子讲解几种渲染方法,下载git上的例子可以在xcode上看到几种渲染的效果。可展示的效果有,正面和背面剔除、深度测试、多边形偏移、裁剪、混合、多重采样。

git地址:点击这里(这里做一下说明,在研究例子的过程中,请无视掉GLTools库,该库是原书作者对gl的一次封装,内部实现接下来会逐层展开,我们现在只讨论与gl有关的内容,后期的话我们可以自己实现封装。)

将点连接起来

上篇讲了运用gl画线函数画出直线与折线,本篇作一些补充。

  • 点的大小

void glPointSize(GLfloat size);

该函数可以指定绘制点的像素,不过并不是所有点的大小都能够支持,使用前请确认指定的点大小是可用的;利用下面这段代码可以获得点大小的范围,以及最小间隔:

GLfloat sizes[2];   //存储支持的点的大小范围
GLfloat step;           //存储支持点的大小增量

//获取支持的点的大小范围和增量
glGetFloatv(GL_POINT_SIZE_RANGE,sizes);
glGetFloatv(GL_POINT_SIZE_GRANULARITY,&step);

点总是正方形的像素,改变点大小情况也如此;

另外还可以通过使用程序点大小模式来设置点大小,这样的话需要在顶点着色器或几何着色器代码中设置点的大小,这两种编程方法将在后面讲到;

glEnable(GL_PROGRAM_POINT_SIZE);
//该模式允许通过着色器程序修改点大小
gl_PointSize = 5.0;
//在着色器程序中,修改该内建变量的值
  • 线的宽度

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展示了以上几种图形的画法;

基础渲染方式

- 油画法

绘制三角形时,如果出现覆盖的情况,通常的做法是对三角形进行排序,然后首先画那些较远的三角形,再在上方渲染那些较近的三角形,这种方式称做“油画法”;这种方法在图形处理中是非常低效的;

- 正面和背面剔除

前面讲到三角形有正面与背面的区分,对其进行区分的原因之一就是为了进行剔除;选择不必要的面进行剔除会极大地提高性能;剔除按如下方式开启:


glEnable(GL_CULL_FACE);     //开启
glDisable(GL_CULL_FACE);    //关闭

我们并没有指明剔除的面,如下:


void glCullFace(GLenum mode);

GL_FRONT    //剔除正面

GL_BACK //剔除背面

GL_FRONT_AND_BACK //正反面全部剔除

- 深度测试

在绘制一个像素时,将一个值(z值)分配给它,表示它到观察者的距离;当另外一个像素在同样位置进行绘制时,新像素z将与原来的进行比较,我们只绘制z值更小的像素;

启用深度测试:

glEnable(GL_DEPTH_TEST);

- 多边形模式

函数glPolygonMode允许将多边形渲染成实体、轮廓或只有点,而且可以选择在多边形的正反面上启用该模式;


void glPolygonMode(GLenum face, GLenum mode);

//face

GL_FRONT

GL_BACK

GL_FRONT_AND_BACK

//mode

GL_FILL     //默认值,实心

GL_LINE     //轮廓

GL_POINT        //点

本文例子GeoTest会展示这些效果;

- 多边形偏移

举个例子,我们可能想要绘制一架大型飞机,然后在飞机上一个较小的但却与飞机在同一物理空间的图形,这叫做“贴花”;这个小的图形的深度值将会与原来飞机的深度缓冲区中的值相同,这将导致深度测试不可预料的通过或者失败,这种情况叫做z冲突;

另外一种情况,我们想要在绘制的实心几何图形上突出它的边;以上这些z冲突的情况下,通常我们解决的办法是当深度值相同时,适当的对深度进行偏移而并不改变实际3D空间中的物理位置:

void glPolygonOffset(GLfloat factor, GLfloat units);
//函数通过 Depth offset = (DZ * factor) + (r * units) 公式计算新的深度值;
//其中,DZ是深度值相对于多边形屏幕区域的变化量,r是使深度缓冲区值产生变化的最小值;

例子Primitives的DrawWireFramedBatch函数代码中展示了偏移的使用;

- 裁剪

裁剪是将渲染限制在一个较小的矩形中,如要开启裁剪:

glEnalbe(GL_SCISSOR_TEST);

指定窗口:

void glScissor(GLint x, GLint y, GLsizei width, GLsizei height);
//x,y指定裁剪框左下角,width height则指定宽度和高度

例子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);
    

- 混合

当深度值相同时,使用混合也可以使下层的颜色值不会被清除;开启混合使用:

glEnable(GL_BLEND);

目标颜色:已经存储在颜色缓冲区中的颜色值;

源颜色:作为当前渲染命令的结果进入颜色缓冲区的颜色;

混合功能开启后,目标颜色和源颜色的组合方式是由混合方程式控制的;

Cf = (Cs * S) + (Cd * D)

Cf是最终计算产生的颜色,Cs是源颜色,Cd是目标颜色,S、D分别是源和目标混合因子;

注意Cs和Cd都是向量,以上进行的是向量乘法和加法;

混合因子是用函数进行设置的:

glBlendFunc(GLenum S, GLenum D);
//S D都是枚举值,详细请自行google,这里给出枚举值

混合方程式并不是唯一的,如需改变则:

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)

另外:

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

glEnable(GL_POINT_SMOOTH);就是开启抗锯齿;

glHint(GLenum target, GLenum mode)函数中target参数指定希望进行修改的行为类型,mode参数告诉OpenGL我们最关心的是什么,或渲染速度或输出质量;该函数是gl唯一一个行为完全依赖生产商的函数,总结呢是跟渲染的性能有关的函数,具体情况具体分析吧,这种函数一般很难实际看出效果;

-多重采样

最后讲一下多重采样,上边讲到的平滑处理在点和直线上是广泛支持的,但是多边形的平滑处理并没有在所有平台上都得到实现,然而在GL_POLYGON_SMOOTH时,由于抗锯齿处理是基于混合操作的,需要对从前到后所有图元进行排序,这显然相当麻烦,使用多重采样可以解决这个问题;

多重采样时,当某一点上的像素进行更新时,gl会在一个缓冲区内对该像素上的值进行采样,结果会通过采样值产生一个单独的值,这可能对性能造成一定的影响;

打开多重采样:

glEnable(GL_MULTISAMPLE);

值得注意的一点是,当启用多重采样时,点、直线和多边形的平滑特性会被忽略;

多重采样的采样值是会被保存在一个单独的缓存区内的,如果没有多重采样缓存区,OpenGL就当作该功能是被禁用的;

状态排序

打开或关闭不同的OpenGL特性将会修改驱动程序的内部状态,这种状态的改变可能会对渲染的性能造成影响。对性能非常敏感的程序员常常会不辞辛苦地对所有绘图命令进行排序,这样需要相同状态的几何图形就可以在一起绘制。这种状态排序是在游戏中常用的提高速度的方法之一。

多重采样缓和区在默认情况下使用片断的RGB值,并不包括颜色的alpha成分;我们可以调用glEnable来修改这个行为

GL_SAMPLE_ALPHA_TO_COVERAGE         //使用alpha值

GL_SAMPLE_ALPHA_TO_ON                   //将alpha值设为1并使用它

GL_SAMPLE_COVERAGE                      //使用glSampleCoverage设置的值

void glSampleCoverage(GLclampf value, GLboolean invert);函数允许指定一个特定的值,它是与片断覆盖值进行按位与操作的结果;

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容