iOS OpenGL GLKView GLKit 详解


//
//  ViewController.m
//  test_opengl_02
//
//  Created by jeffasd on 16/8/5.
//  Copyright © 2016年 jeffasd. All rights reserved.
//

#import "ViewController.h"

 顶点坐标既法线和纹理
//GLfloat squareVertexData[48] = {
//    
//    0.5f, 0.5f, -0.9f,     0.0f, 0.0f, 1.0f,   1.0f, 1.0f,//0
//    -0.5f, 0.5f, -0.9f,     0.0f, 0.0f, 1.0f,   0.0f, 1.0f,//1
//    0.5f, -0.5f, -0.9f,     0.0f, 0.0f, 0.0f,   1.0f, 0.0f,//2
//    0.5f, -0.5f, -0.9f,     0.0f, 0.0f, 0.0f,   1.0f, 0.0f,//2
//    -0.5f, 0.5f, -0.9f,     0.0f, 0.0f, 1.0f,   0.0f, 1.0f,//1
//    -0.5f, -0.5f, -0.9f,     0.0f, 0.0f, 1.0f,   0.0f, 0.0f//3
//};

// 顶点坐标既法线和纹理
//GLfloat squareVertexData[48] = {
//    
//    0.5f, 0.5f, -0.9f,     0.0f, 0.0f, 1.0f,   1.0f, 1.0f,//0
//    -0.5f, 0.5f, -0.9f,     0.0f, 0.0f, 1.0f,   0.0f, 1.0f,//1
//    0.5f, -0.5f, -0.9f,     0.0f, 0.0f, 0.0f,   1.0f, 0.0f,//2
//    -0.5f, -0.5f, -0.9f,     0.0f, 0.0f, 1.0f,   0.0f, 0.0f,//3
//    0.5f, -0.5f, -0.9f,     0.0f, 0.0f, 0.0f,   1.0f, 0.0f,//2
//    -0.5f, 0.5f, -0.9f,     0.0f, 0.0f, 1.0f,   0.0f, 1.0f//1
//    
//};

typedef struct {
    GLfloat Positon[3];//位置
    GLfloat Normal[3];//法线
    GLfloat Color[4];//颜色
    GLfloat TexCoord[2];//纹理
} Vertex;

// 顶点坐标既法线和纹理
const Vertex squareVertexData[] = {
    
    {0.5f, 0.5f, -0.9f, 0.0f, 0.0f, 1.0f,  1.0f, 0.0f, 0.0f, 1.0f,  1.0f, 1.0f},//0
    {-0.5f, 0.5f, -0.9f, 0.0f, 0.0f, 1.0f,  1.0f, 0.0f, 0.0f, 1.0f,  0.0f, 1.0f},//1
    {0.5f, -0.5f, -0.9f, 0.0f, 0.0f, 0.0f,  1.0f, 0.0f, 0.0f, 1.0f,  1.0f, 0.0f},//2
    {-0.5f, -0.5f, -0.9f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f,  0.0f, 0.0f}//3
    
};


const GLubyte Indices[] = {
    0, 1, 2,
    2, 1, 3
};

//每行顶点数据的排列含义是:
//
//顶点X、顶点Y,顶点Z、法线X、法线Y、法线Z、纹理S、纹理T。
//
//在后面解析此数组时,将参考此规则。
//
//顶点位置用于确定在什么地方显示,法线用于光照模型计算,纹理则用在贴图中。
//
//一般约定为“顶点以逆时针次序出现在屏幕上的面”为“正面”。
//
//世界坐标是OpenGL中用来描述场景的坐标,Z+轴垂直屏幕向外,X+从左到右,Y+轴从下到上,是右手笛卡尔坐标系统。我们用这个坐标系来描述物体及光源的位置。

@interface ViewController ()

@property (nonatomic, strong) EAGLContext *context;
@property (nonatomic, strong) GLKBaseEffect *effect;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
//    self.view.backgroundColor = [UIColor whiteColor];
    
    
    self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
    
    if (!_context) {
        NSLog(@"faile to init context");
    }
    
    GLKView *glkView = (GLKView *)self.view;
    glkView.context = self.context;
    
//    drawableColorFormat
//    你的OpenGL上下文有一个缓冲区,它用以存储将在屏幕中显示的颜色。你可以使用其属性来设置缓冲区中每个像素的颜色格式。
//    缺省值是GLKViewDrawableColorFormatRGBA8888,即缓冲区的每个像素的最小组成部分(-个像素有四个元素组成 RGBA)使用8个bit(如R使用8个bit)(所以每个像素4个字节 既 4*8 个bit)。这非常好,因为它给了你提供了最广泛的颜色范围,让你的app看起来更好。
//    但是如果你的app允许更小范围的颜色,你可以设置为GLKViewDrawableColorFormatRGB565,从而使你的app消耗更少的资源(内存和处理时间)。
    glkView.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888;
    
    
//    drawableDepthFormat
//    你的OpenGL上下文还可以(可选地)有另一个缓冲区,称为深度缓冲区。这帮助我们确保更接近观察者的对象显示在远一些的对象的前面(意思就是离观察者近一些的对象会挡住在它后面的对象)。
//    其缺省的工作方式是:OpenGL把接近观察者的对象的所有像素存储到深度缓冲区,当开始绘制一个像素时,它(OpenGL)首先检查深度缓冲区,看是否已经绘制了更接近观察者的什么东西,如果是则忽略它(要绘制的像素,就是说,在绘制一个像素之前,看看前面有没有挡着它的东西,如果有那就不用绘制了)。否则,把它增加到深度缓冲区和颜色缓冲区。
//    你可以设置这个属性,以选择深度缓冲区的格式。缺省值是GLKViewDrawableDepthFormatNone,意味着完全没有深度缓冲区。
//    但是如果你要使用这个属性(一般用于3D游戏),你应该选择GLKViewDrawableDepthFormat16或GLKViewDrawableDepthFormat24。这里的差别是使用GLKViewDrawableDepthFormat16将消耗更少的资源,但是当对象非常接近彼此时,你可能存在渲染问题()。
    glkView.drawableDepthFormat = GLKViewDrawableDepthFormat24;
    
//    将此“EAGLContext”实例设置为OpenGL的“当前激活”的“Context”。这样,以后所有“GL”的指令均作用在这个“Context”上。随后,发送第一个“GL”指令:激活“深度检测”。
    [EAGLContext setCurrentContext:_context];
    glEnable(GL_DEPTH_TEST);//发送第一个“GL”指令:激活“深度检测”。
    
//    创建一个GLK内置的“着色效果”,并给它提供一个光源,光的颜色为绿色。
    
//    iOS的OpenGL中里有2个着色器,
//    一个是GLKBaseEffect,为了方便OpenGL ES 1.0转移到2.0的通用着色器。
//    一个是OpenGL ES 2.0新添加的可编程着色器,使用跨平台的着色语言
    //    //实例化基础效果实例,如果没有GLKit与GLKBaseEffect类,就需要为这个简单的例子编写一个小的GPU程序,使用2.0的Shading Language,而GLKBaseEffect会在需要的时候自动的构建GPU程序。
    
    self.effect = [[GLKBaseEffect alloc] init];
    self.effect.light0.enabled = GL_TRUE;
//    self.effect.light0.diffuseColor = GLKVector4Make(1.0f, 0.4f, 0.4f, 1.0f);
    self.effect.light0.diffuseColor = GLKVector4Make(1.0f, 0.0f, 0.0f, 1.0f);
    
//    //实例化基础效果实例,如果没有GLKit与GLKBaseEffect类,就需要为这个简单的例子编写一个小的GPU程序,使用2.0的Shading Language,而GLKBaseEffect会在需要的时候自动的构建GPU程序。
//    self.baseEffect = [[GLKBaseEffect alloc] init];
//    self.baseEffect.useConstantColor = GL_TRUE;
//    
//    //控制渲染像素颜色的方式有多种。这个应用的GLKBaseEffect实例使用一个恒定不变的白色来渲染三角形。
//    //下面代码中使用的再GLKit中定义的用于保存4个颜色元素值的C数据结构体GLKVector4来设置这个恒定值。
//    self.baseEffect.constantColor = GLKVector4Make(1.0f, 1.0f, 1.0f, 1.0f);
    
    
//    将顶点数据写入通用的顶点属性存储区
//
    //1.写入过程
//    首先将数据保存进GUP的一个缓冲区中,然后再按一定规则,将数据取出,复制到各个通用顶点属性中。
//    注:如果顶点数据只有一种类型(如单纯的位置坐标),换言之,在读数据时,不需要确定第一个数据的内存位置(总是从0开始),则不必事先保存进缓冲区。
//    
//    2.顶点数组保存进缓冲区
    //Vertex Data
    GLuint buffer;
    glGenBuffers(1, &buffer);
    glBindBuffer(GL_ARRAY_BUFFER, buffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(squareVertexData), squareVertexData, GL_STATIC_DRAW);
    //    这几行代码表示的含义是:声明一个缓冲区的标识(GLuint类型)à让OpenGL自动分配一个缓冲区并且返回这个标识的值à绑定这个缓冲区到当前“Context”à最后,将我们前面预先定义的顶点数据“squareVertexData”复制进这个缓冲区中。
    
//    注:参数“GL_STATIC_DRAW”,它表示此缓冲区内容只能被修改一次,但可以无限次读取。
    
    GLuint elementBuffer;
    glGenBuffers(1, &elementBuffer);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementBuffer);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Indices), Indices, GL_STATIC_DRAW);
    
    
//    3、将缓冲区的数据复制进通用顶点属性中
    glEnableVertexAttribArray(GLKVertexAttribPosition);
//    glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, 4*8, 0);
    glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), 0);
//    首先,激活顶点属性(默认它的关闭的)。“GLKVertexAttribPosition”是顶点属性集中“位置Position”属性的索引。
//    
//    顶点属性集中包含五种属性:位置、法线、颜色、纹理0,纹理1。
//    
//    它们的索引值是0到4。
//    
//    激活后,接下来使用“glVertexAttribPointer”方法填充数据。
//    
//    参数含义分别为:
//    
//    顶点属性索引(这里是位置)、3个分量的矢量、类型是浮点(GL_FLOAT)、填充时不需要单位化(GL_FALSE)、在数据数组中每行的跨度是32个字节(4*8=32。从预定义的数组中可看出,每行有8个GL_FLOAT浮点值,而GL_FLOAT占4个字节,因此每一行的跨度是4*8)。
//    
//    最后一个参数是一个偏移量的指针,用来确定“第一个数据”将从内存数据块的什么地方开始。
    
    
//    在前面预定义的顶点数据数组中,还包含了法线和纹理坐标,所以参照上面的方法,将剩余的数据分别复制进通用顶点属性中。
    glEnableVertexAttribArray(GLKVertexAttribNormal);
    glVertexAttribPointer(GLKVertexAttribNormal, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid *) (sizeof(GLfloat) * 3));
//    glVertexAttribPointer(GLKVertexAttribNormal, 3, GL_FLOAT, GL_FALSE, 4*8, (char *) + 12);
    
    
//    GLKVertexAttribColor
    
    glEnableVertexAttribArray(GLKVertexAttribColor);
    //glVertexAttribPointer 指定了渲染时索引值为 GLKVertexAttribColor 的顶点属性数组的数据格式和位置
    glVertexAttribPointer(GLKVertexAttribColor, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid *) (sizeof(GLfloat) * 6));
    
    glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
//    glVertexAttribPointer(GLKVertexAttribTexCoord0, 3, GL_FLOAT, GL_FALSE, 4*8, (char *) + 24);
    //glVertexAttribPointer 指定了渲染时索引值为 GLKVertexAttribTexCoord0 的顶点属性数组的数据格式和位置
    glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid *) (sizeof(GLfloat) * 10));
    
}


//这两个方法每帧都执行一次(循环执行),一般执行频率与屏幕刷新率相同(但也可以更改)。
//
//第一次循环时,先调用“glkView”再调用“update”。
//
//一般,将场景数据变化放在“update”中,而渲染代码则放在“glkView”中。

//一般,将场景数据变化放在“update”中,而渲染代码则放在“glkView”中。
- (void)update{
    
}

//渲染场景
//一般,将场景数据变化放在“update”中,而渲染代码则放在“glkView”中。
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect{
    
//    glClearColor(0.0, 1.0, 0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    //    iOS的OpenGL中里有2个着色器,
    //    一个是GLKBaseEffect,为了方便OpenGL ES 1.0转移到2.0的通用着色器。
    //    一个是OpenGL ES 2.0新添加的可编程着色器,使用跨平台的着色语言
    //    //实例化基础效果实例,如果没有GLKit与GLKBaseEffect类,就需要为这个简单的例子编写一个小的GPU程序,使用2.0的Shading Language,而GLKBaseEffect会在需要的时候自动的构建GPU程序。
    [self.effect prepareToDraw];

//    这里使用GLKBaseEffect来做着色器
    
    
//    triangle 三角形
//    glDrawArrays(GL_TRIANGLES, 0, 6);//GL_ARRAY_BUFFER
    
    int count = sizeof(Indices) / sizeof(Indices[0]);
    
    glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_BYTE, 0);
    
//    前两行为渲染前的“清除”操作,清除颜色缓冲区和深度缓冲区中的内容,并且填充淡蓝色背景(默认背景是黑色)。
//    
//    “prepareToDraw”方法,是让“效果Effect”针对当前“Context”的状态进行一些配置,它始终把“GL_TEXTURE_PROGRAM”状态定位到“Effect”对象的着色器上。此外,如果Effect使用了纹理,它也会修改“GL_TEXTURE_BINDING_2D”。
//    
//    
//    
//    接下来,用“glDrawArrays”指令,让OpenGL“画出”两个三角形(拼合为一个正方形)。OpenGL会自动从通用顶点属性中取出这些数据、组装、再用“Effect”内置的着色器渲染。
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值