iOS全景图片之OpenGL学习笔记

OpenGL版本

在开发OpenGL项目前,需要根据业务需求选择合适的版本。在初始化EAGLContext时指定ES版本号。
EAGLContext *context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];

EAGLContext

与UIKit中CGContextRef相似,EAGLContext相当于OpenGL绘制句柄或者上下文,在绘制试图之前,需要指定使用创建的上下文绘制
[EAGLContextsetCurrentContext:view.context];

创建和配置GLKit视图

可以以编程方式或使用Interface Builder创建和配置GLKView对象。在使用它绘制之前,必须将其与EAGLContext对象相关联。

  • 以编程方式创建视图时,首先创建上下文,然后将其传递给视图的initWithFrame:context:方法。
  • 从故事板加载视图后,创建上下文并将其设置为视图的上下文属性的值。

GLKit视图会自动创建和配置自己的OpenGL ES framebuffer对象和renderbuffers。可以使用视图的可绘制属性来控制这些对象的属性。如果更改GLKit视图的大小,比例因子或可绘制属性,则会在下次绘制内容时自动删除并重新创建相应的framebuffer对象和renderbuffers。

绘制GLKit视图

GLKView_diagram_2x.png

如图所示,概述了绘制OpenGL ES内容的三个步骤:准备OpenGL ES基础设施,发布绘图命令,并将呈现的内容呈现给Core Animation进行显示。 GLKView类实现了第一和第三步。对于第二步,您将实现一个绘图方法,如下代码中的示例所示。

- (void)drawRect:(CGRect)rect
{
    // Clear the framebuffer
    glClearColor(0.0f, 0.0f, 0.1f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // Draw using previously configured texture, shader, uniforms, and vertex array
    glBindTexture(GL_TEXTURE_2D, _planetTexture);
    glUseProgram(_diffuseShading);
    glUniformMatrix4fv(_uniformModelViewProjectionMatrix, 1, 0, _modelViewProjectionMatrix.m);
    glBindVertexArrayOES(_planetMesh);
    glDrawElements(GL_TRIANGLE_STRIP, 256, GL_UNSIGNED_SHORT);
}

注意:glClear函数提示OpenGL ES可以丢弃任何现有的帧缓冲区内容,避免了昂贵的内存操作将以前的内容加载到内存中。为了确保最佳性能,您应该在绘制之前始终调用此函数。

定义要绘制图片的顶点坐标和纹理坐标

OpenGL ES中坐标系是和iOS常用的Quartz 2D坐标系是不一样的,Quartz 2D坐标系属于右手坐标系,OpenGL ES属于左手坐标系。先说一下左手坐标系和右手坐标系。
伸出左手,让拇指和食指成“L”形状,拇指向右,食指向上,中指指向起那方,这时就建立了一个“左手坐标系”,拇指、食指和中指分表代表x、y、z轴的正方向。“右手坐标系”就是用右手,如下图所示:

“左手坐标系”和“右手坐标系”.png

在iOS开发中,屏幕左上角是坐标原点,往右是x轴正方向,往下是y轴正方向。而在OpenGL ES中,屏幕的中点是坐标原点,往右是x轴正方向,往下是y轴正方向,其中z轴的正方向是从屏幕往外的方向,如下图所示:

OpenGL ES坐标系.png

根据OpenGL ES的坐标系,我们定义一下要绘制的图片的几个顶点,顶点坐标和纹理坐标是放在一个GLfloat数组中管理的,定义一组顶点数据的跨度为5,其中前三个存储顶点坐标,后两个存储纹理坐标,下图一共定义了4个顶点,就是矩形的四个顶点,需要注意的是,虽然坐标都是0.5,但是绘制出来的图形并不是正方形,因为我们用来最终显示的是iPhone屏幕,手机的长和宽并不相等。

顶点坐标和纹理坐标.png

OpenGL ES不能绘制多边形,只能绘制点,线,三角形,OpenGL可以绘制多边形,由于我们绘制的图片是一个矩形,又两个三角形构成,就是下图中的两个顶点索引(0,1,3)和(1,2,3)组成的三角形拼成一个矩形。

顶点索引.png
- (void)setupOpenGL {
    
    [EAGLContext setCurrentContext:_context];
    glEnable(GL_DEPTH_TEST);
    // 顶点
    GLfloat *vVertices  = NULL;
    // 纹理
    GLfloat *vTextCoord = NULL;
    // 索引
    GLushort *indices   = NULL;
    int numVertices     = 0;
    _numIndices         = esGenSphere(200, 1.0, &vVertices, &vTextCoord, &indices, &numVertices);
    // 加载顶点索引数据
    // 创建索引buffer并将indices的数据放入
    glGenBuffers(1, &_vertexIndicesBuffer);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vertexIndicesBuffer);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, _numIndices*sizeof(GLushort), indices, GL_STATIC_DRAW);
    // 加载顶点坐标
    // 创建顶点buffer并将vVertices中的数据放入
    glGenBuffers(1, &_vertexBuffer);
    glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
    glBufferData(GL_ARRAY_BUFFER, numVertices*3*sizeof(GLfloat), vVertices, GL_STATIC_DRAW);
    //设置顶点属性,对顶点的位置,颜色,坐标进行赋值
    glEnableVertexAttribArray(GLKVertexAttribPosition);
    glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*3, NULL);
 
    // 创建纹理buffer并将vTextCoord数据放入
    glGenBuffers(1, &_vertexTexCoord);
    glBindBuffer(GL_ARRAY_BUFFER, _vertexTexCoord);
    glBufferData(GL_ARRAY_BUFFER, numVertices*2*sizeof(GLfloat), vTextCoord, GL_DYNAMIC_DRAW);
    
    //设置纹理属性,对纹理的位置,颜色,坐标进行赋值
    glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
    glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*2, NULL);

    // 将图片转换成为纹理信息
    NSString *imagePath = [[NSBundle mainBundle] pathForResource:self.imageName ofType:self.imageNameType];
    
    // 由于OpenGL的默认坐标系设置在左下角, 而GLKit在左上角, 因此需要转换
    NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES],
                             GLKTextureLoaderOriginBottomLeft,
                             nil];
    
    _textureInfo = [GLKTextureLoader textureWithContentsOfFile:imagePath options:options error:nil];
    
    // 设置着色器的纹理
    _effect                    = [[GLKBaseEffect alloc] init];
    _effect.texture2d0.enabled = GL_TRUE;
    _effect.texture2d0.name    = _textureInfo.name;
}

//启用顶点位置(坐标)数组,之前说过opengl是状态机,需要什么状态就启动什么状态
glEnableVertexAttribArray(GLKVertexAttribPosition);

 GLfloat vertexs[] = {
        -0.5, -0.5, 0,     0.0, 0.0,   //左下
        -0.5,  0.5, 0,     0.0, 1.0,   //左上
         0.5,  0.5, 0,     1.0, 1.0,   //右上
         0.5, -0.5, 0,     1.0, 0.0,   //右下
    };
启用通用顶点属性
 /*
  index:指定通用顶点数据的索引,这个值的范围从0到支持的最大顶点属性数量减1
  功能:用于启用通用顶点属性
*/
void glEnableVertexAttribArray(GLuint index);
禁止通用顶点属性
 /*
  index:指定通用顶点数据的索引,这个值的范围从0到支持的最大顶点属性数量减1
*/
void glDisableVertexAttribArray(GLuint index);
顶点数组设置值
index: 通用顶点属性索引
size: 顶点数组中为顶点属性指定的分量数量,取值范围1~4
type: 数据格式 ,两个函数都包括的有效值是
      GL_BYTE  GL_UNSIGNED_BYTE  GL_SHORT  GL_UNSIGNED_SHORT  GL_INT  GL_UNSIGNED_INT
      glVertexAttribPointer还包括的值为:GL_HALF_FLOAT GL_FLOAT 等
normalized: 仅glVertexAttribPointer使用,表示非浮点数据类型转换成浮点值时是否应该规范化
stride: 每个顶点由size指定的顶点属性分量顺序存储。stride指定顶点索引i和i+1表示的顶点之间的偏移。
    如果为0,表示顺序存储。如果不为0,在取下一个顶点的同类数据时,需要加上偏移。
ptr: 如果使用“顶点缓冲区对象”,表示的是该缓冲区内的偏移量。

void glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *ptr);
// 取值为“整数”版本
void glVertexAttribIPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *ptr);

这里由于我们使用iOS封装好的GLkit框架,不需要我们设置着色器程序,里面有内置的设置好的index位置,就是下面的变量:

typedef NS_ENUM(GLint, GLKVertexAttrib)
{
    GLKVertexAttribPosition,
    GLKVertexAttribNormal,
    GLKVertexAttribColor,
    GLKVertexAttribTexCoord0,
    GLKVertexAttribTexCoord1
} NS_ENUM_AVAILABLE(10_8, 5_0);

GLKit里面的GLKVertexAttribPosition和GLKVertexAttribTexCoord0分别表示顶点坐标和纹理坐标两个变量的属性索引。(顶点索引不是顶点数据,这里我们不需要管这个值)

启用顶点数组和指定顶点属性.png

上面的程序最需要注意的是变量的偏移很重要,如果把GLfloat写成CGFloat,会导致图片渲染不出来。

参考文献:
http://www.jianshu.com/p/23e938fab9ca
http://www.jianshu.com/p/954339d57541

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

推荐阅读更多精彩内容