OpenGL ES学习笔记之四(创建球体)

今天我们创建一个球体,后期会在此基础上实现一个全景视频播放器。之前的三篇笔记我们都是通过两种方式来实现OpenGL ES的学习,即一种方式是使用GLKit另一种方式不使用GLKit。这是为了方便理解OpenGL ES才这样做的,因为苹果对OpenGL ES进行了部分封装使其使用起来更方便了,但如果直接用GLKit学习不利于对OpenGL ES的理解所以前面我都是在实现同一个功能的时候用两种方式来实现。两种方式的区别基本上在前三篇的学习中已经讲解的差多了,基本没有其他区别了,所以以后的学习笔记不再使用两种方式来实现而是直接用GLKit来实现,下面进入正题。

学习的代码都在我的github仓库欢迎大家学习指教!

一、生成顶点数据

这次我们的代码也是在上一次学习笔记的基础之上修改来的,这里只讲不同点。上次实现的立方体和这次实现的球体已经很像了。只要传入的顶点数据不一样就可以了!其实学到这里你会发现OpenGL ES只是入门难,入门后里面的东西很简单。有点跑题了,现在用代码实现顶点数据。我们先要知道以下知识:

  • 我们可以通过三角函数实现一个圆的坐标数据
  • 如果我们把多个圆叠放在一起就可以实现一他圆柱体
  • 如果我们把这多个叠放圆的半径也按三角函数的方式实现就可以实现一个球体

通过上面的分析我们大体已经知道怎么生成这个球体的数据了,全靠顶点来实现球体有点不现实,我们可以通过少量的顶点连接成的三角形来实现球体。顶点越多生成的球体越平滑但也不是没有极限,当顶点大于一定值的时候再多的顶点也看不出差别来反而会影响性能。所以我们这里有81 x 81个顶点来实现一个球体。生成顶点的方法如下:

#define kDevidCount 80

/**
 绘制一个球的顶点

 @param num 传入要生成的顶点的一层的个数(最后生成的顶点个数为 num * num)
 @return 返回生成后的顶点
 */
- (Vertex *)getBallDevidNum:(GLint) num{
    
    if (num % 2 == 1) {
        return 0;
    }
    
    GLfloat delta = 2 * M_PI / num; // 分割的份数
    GLfloat ballRaduis = 0.8; // 球的半径
    GLfloat pointZ;
    GLfloat pointX;
    GLfloat pointY;
    GLfloat textureY;
    GLfloat textureX;
    GLfloat textureYdelta = 1.0 / (num / 2);
    GLfloat textureXdelta = 1.0 / num;
    GLint layerNum = num / 2.0 + 1; // 层数
    GLint perLayerNum = num + 1; // 要让点再加到起点所以num + 1
    
    Vertex * cirleVertex = malloc(sizeof(Vertex) * perLayerNum * layerNum);
    memset(cirleVertex, 0x00, sizeof(Vertex) * perLayerNum * layerNum);
    
    // 层数
    for (int i = 0; i < layerNum; i++) {
        // 每层的高度(即pointY),为负数让其从下向上创建
        pointY = -ballRaduis * cos(delta * i);
        
        // 每层的半径
        GLfloat layerRaduis = ballRaduis * sin(delta * i);
        // 每层圆的点,
        for (int j = 0; j < perLayerNum; j++) {
            // 计算
            pointX = layerRaduis * cos(delta * j);
            pointZ = layerRaduis * sin(delta * j);
            textureX = textureXdelta * j;
            textureY = textureYdelta * i;
            
            cirleVertex[i * perLayerNum + j] = (Vertex){pointX, pointY, pointZ, textureX, textureY};
        }
    }
    
    return cirleVertex;
}


// 顶点数据索引
- (GLuint *)getBallVertexIndex:(GLint)num{
    
    // 每层要多原点两次
    GLint sizeNum = sizeof(GLuint) * (num + 1) * (num + 1);
    
    GLuint * ballVertexIndex = malloc(sizeNum);
    memset(ballVertexIndex, 0x00, sizeNum);
    GLint layerNum = num / 2 + 1;
    GLint perLayerNum = num + 1; // 要让点再加到起点所以num + 1
    
    for (int i = 0; i < layerNum; i++) {
       
        if (i + 1 < layerNum) {
            
            for (int j = 0; j < perLayerNum; j++) {
                
                // i * perLayerNum * 2每层的下标是原来的2倍
                ballVertexIndex[(i * perLayerNum * 2) + (j * 2)] = i * perLayerNum + j;
                // 后一层数据
                ballVertexIndex[(i * perLayerNum * 2) + (j * 2 + 1)] = (i + 1) * perLayerNum + j;
            }
        } else {
            
            for (int j = 0; j < perLayerNum; j++) {
                // 后最一层数据单独处理
                ballVertexIndex[i * perLayerNum * 2 + j] = i * perLayerNum + j;
            }
        }
    }
    
    return ballVertexIndex;
}

在原来设置VBO数据的方法里设置上面的方法来生成顶点数据和顶点索引数据。代码如下:

/**
 设置顶点缓存VBO
 */
- (void)setupBufferVBO {
    
    // 获取球的顶点和索引
    _cirleVertex = [self getBallDevidNum:kDevidCount];
    _vertextIndex = [self getBallVertexIndex:kDevidCount];
    
    // 设置VBO
    glGenBuffers(1, &_bufferVBO);
    glBindBuffer(GL_ARRAY_BUFFER, _bufferVBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * (kDevidCount + 1) * (kDevidCount / 2 + 1), _cirleVertex, GL_STATIC_DRAW);
    
    glGenBuffers(1, &_bufferIndexVBO);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _bufferIndexVBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * kDevidCount * (kDevidCount + 1), _vertextIndex, GL_STATIC_DRAW);
    
    glEnableVertexAttribArray(GLKVertexAttribPosition);
    glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid *)NULL);
   
    // 设置纹理坐标
    glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
    glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLfloat *)NULL + 3);
    // 释放顶点数据
    free(_cirleVertex);
    free(_vertextIndex);
}

二、修改渲染方法

由于顶点数据的大小变化了,渲染方法里也得修改新的顶点数据个数的计算方法,代码如下:

- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect{
    
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    [self.effect prepareToDraw];
    
    // 绘制一个球
    glDrawElements(GL_TRIANGLE_STRIP, kDevidCount * (kDevidCount + 1), GL_UNSIGNED_INT, 0);
}

由于这次是用的GLKit所以代码会简单很多,渲染方法里的代码也很少,其他的代码可以看上一篇笔记。现在看一下运行结果。

球体.gif

球体上面的纹理可能不太合适,大家可以在网上搜一个地球的纹理试一下效果会比现在好一点。

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

推荐阅读更多精彩内容