美文网首页OpenGL ES
第十六节—GLKit使用索引绘图

第十六节—GLKit使用索引绘图

作者: L_Ares | 来源:发表于2020-10-11 17:44 被阅读0次

本文为L_Ares个人写作,如需转载请表明原文出处。

逻辑和GLSL是一样的,只不过GLKit提供了GLKBaseEffect,所以不用自己来实现shader的代码,其他的逻辑是一模一样的。

直接上代码,如果有需要的小伙伴记得仔细看备注,我觉得非常的详细了。

//
//  ViewController.m
//  06GLKit索引绘图
//
//  Created by EasonLi on 2020/10/10.
//

#import "ViewController.h"

@interface ViewController ()

//图形上下文
@property (nonatomic,strong) EAGLContext *myContext;

//渲染效果设置器
@property (nonatomic,strong) GLKBaseEffect *myEffect;

//要绘制的三角形的数量
@property (nonatomic,assign) GLint count;

//绕X轴的旋转弧度
@property (nonatomic,assign) GLfloat xDegree;
//绕Y轴的旋转弧度
@property (nonatomic,assign) GLfloat yDegree;
//绕Z轴的旋转弧度
@property (nonatomic,assign) GLfloat zDegree;

//是否绕X轴旋转
@property (nonatomic,assign) BOOL roundX;
//是否绕Y轴旋转
@property (nonatomic,assign) BOOL roundY;
//是否绕Z轴旋转
@property (nonatomic,assign) BOOL roundZ;

//定时器
@property (nonatomic,strong) dispatch_source_t timer;

@end

@implementation ViewController

- (void)viewDidLoad {
    
    [super viewDidLoad];
    
    //初始化变量
    [self initialData];
    
    //设置图层和上下文
    [self setUpContextAndLayer];
    
    [self toRenderScence];
    
    // Do any additional setup after loading the view.
}

#pragma mark - 初始化数据
- (void)initialData
{
    self.myContext = nil;
    self.myEffect = nil;
    self.count = 0;
    self.xDegree = 0.f;
    self.yDegree = 0.f;
    self.zDegree = 0.f;
    self.roundX = NO;
    self.roundY = NO;
    self.roundZ = NO;
    double seconds = 0.1;
    self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
    dispatch_source_set_timer(self.timer, DISPATCH_TIME_NOW, seconds * NSEC_PER_SEC, 0.0 * NSEC_PER_SEC);
}

#pragma mark - 设置图层和上下文
- (void)setUpContextAndLayer
{
    //新建图形上下文
    self.myContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
    
    //将controller的view直接赋值给GLKView,创建GLKView
    GLKView *view = (GLKView *)self.view;
    //设置view的上下文
    view.context = self.myContext;
    //设置图层的颜色格式
    view.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888;
    //设置图层的深度缓冲区大小
    view.drawableDepthFormat = GLKViewDrawableDepthFormat24;
    
    //设置当前图形的上下文是myContext
    [EAGLContext setCurrentContext:self.myContext];
    
    //开启深度测试
    glEnable(GL_DEPTH_TEST);
    
}

#pragma mark - 渲染图形
- (void)toRenderScence
{
    
    //设置顶点数组
    /**
     前3个数据是顶点坐标
     中间3个数据是顶点颜色
     最后2个是纹理坐标
     */
    GLfloat vertexArr[] =
    {
        //左上
        -0.5f,0.5f,0.f,   0.f,0.f,0.5f,   0.f,1.f,
        //右上
        0.5f,0.5f,0.f,    0.f,0.5f,0.f,   1.f,1.f,
        //左下
        -0.5f,-0.5f,0.f,  0.5f,0.f,0.f,   0.f,0.f,
        //右下
        0.5f,-0.5f,0.f,   0.5f,0.5f,0.f,  1.f,0.f,
        //中间顶点
        0.f,0.f,0.8f,      0.5f,0.5f,0.5f,  0.5f,0.5f
    };

    //索引数组
    GLuint indexArr[] =
    {
        0,3,2,
        0,1,3,
        0,2,4,
        0,4,1,
        1,4,3,
        2,3,4
    };
    
    //要绘制的三角形个数
    self.count = sizeof(indexArr) / sizeof(GLuint);
    
    //将顶点数组放入数组类型的顶点缓冲区
    GLuint vertexBuffer;
    //申请缓冲区
    glGenBuffers(1, &vertexBuffer);
    //绑定缓冲区
    glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
    //创建并初始化缓冲区对象
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertexArr), &vertexArr, GL_STATIC_DRAW);
    
    //将索引数组放入索引缓冲区
    GLuint indexBuffer;
    //申请缓冲区
    glGenBuffers(1, &indexBuffer);
    //绑定缓冲区
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
    //创建并初始化缓冲区对象
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indexArr), indexArr, GL_STATIC_DRAW);
    
    //打开顶点属性通道
    glEnableVertexAttribArray(GLKVertexAttribPosition);
    //设置顶点数据的读取方式
    glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 8, NULL);
    
    //打开颜色属性通道
    glEnableVertexAttribArray(GLKVertexAttribColor);
    //设置颜色数据的读取方式
    glVertexAttribPointer(GLKVertexAttribColor, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 8, (GLfloat *)NULL + 3);
    
    //打开纹理属性通道
    glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
    //设置纹理数据的读取方式
    glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 8, (GLfloat *)NULL + 6);
    
    //载入纹理并且使用GLKBaseEffect
    [self loadTextureAndSetEffect];
    
    //设置投影矩阵和模型视图矩阵
    [self setUpModelViewProjectionMatrix];
    
    //开启GCD的定时器
    [self startGCDTimer];
    
}

- (void)loadTextureAndSetEffect
{
    //获取纹理文件的路径
    NSString *textureFile = [[NSBundle mainBundle] pathForResource:@"test" ofType:@"png"];
    //设置纹理坐标的原点为左下角
    NSDictionary *optional = @{GLKTextureLoaderOriginBottomLeft:@1};
    //初始化纹理信息
    GLKTextureInfo *textureInfo = [GLKTextureLoader textureWithContentsOfFile:textureFile options:optional error:nil];
    
    //初始化GLKBaseEffect对象
    self.myEffect = [[GLKBaseEffect alloc] init];
    //允许使用第一个纹理
    self.myEffect.texture2d0.enabled = GL_TRUE;
    //纹理名称
    self.myEffect.texture2d0.name = textureInfo.name;
    
}

- (void)setUpModelViewProjectionMatrix
{
    //计算宽高比
    CGSize size = self.view.bounds.size;
    //fabs就是C中的返回绝对值
    float aspect = fabs(size.width/size.height);
    //计算投影矩阵
    GLKMatrix4 projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(90.f), aspect, 0.1f, 10.f);
    /**
     就是设置缩放的,用不用都行,随意,当然有想要翻转各轴的的话,可以使用
     projectionMatrix = GLKMatrix4Scale(projectionMatrix, 1.0f, 1.0f, 1.0f);
     */
    //设置透视投影的投影矩阵
    self.myEffect.transform.projectionMatrix = projectionMatrix;
    
    //设置模型视图矩阵,可以选择稍微平移一下,不想要平移一下的话,可以直接初始化
    //那就是一个单元矩阵,平移用GLKMatrix4Translate函数
    GLKMatrix4 modelViewMatrix = GLKMatrix4Identity;
    
    //设置模型视图矩阵
    self.myEffect.transform.modelviewMatrix = modelViewMatrix;
    
}

- (void)startGCDTimer
{
    dispatch_source_set_event_handler(self.timer, ^{
        self.xDegree += 0.1 * self.roundX;
        self.yDegree += 0.1 * self.roundY;
        self.zDegree += 0.1 * self.roundZ;
    });
    dispatch_resume(self.timer);
}

#pragma mark - GLKViewController的代理
//如果Controller是GLKViewController的子类,并且提供了实现,那么就用update,不然
//就用glkViewControllerUpdate
- (void)update
{
    //旋转就是把模型视图矩阵乘以旋转矩阵,然后再把模型视图矩阵重新赋值给
    //GLKBaseEffect的modelviewMatrix
    
    //所以先初始化一个新的modelview矩阵
    //把金字塔移动一些,不然看不到了
    GLKMatrix4 modelViewMatrix = GLKMatrix4Translate(GLKMatrix4Identity, 0.0f, 0.0f, -3.0f);
    //X轴变换
    modelViewMatrix = GLKMatrix4RotateX(modelViewMatrix, self.xDegree);
    //Y轴变换
    modelViewMatrix = GLKMatrix4RotateY(modelViewMatrix, self.yDegree);
    //Z轴变换
    modelViewMatrix = GLKMatrix4RotateZ(modelViewMatrix, self.zDegree);
    
    //赋值给GLKBaseEffect对象的modelViewMatrix
    self.myEffect.transform.modelviewMatrix = modelViewMatrix;
    
}

#pragma mark - GLKView的代理
//用来绘制的,就是渲染,按照渲染的步骤来
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
    //设置清屏颜色
    glClearColor(0.3f, 0.3f, 0.3f, 1.f);
    //清空缓冲区
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    
    //一定要记得GLKBaseEffect在绘制前要使用prepareToDraw
    [self.myEffect prepareToDraw];
    
    //利用索引绘图开始绘制
    //最后一个参数是索引数组的地址,但是我们直接把索引都传入了索引缓冲区里面了,所以
    //直接写0就得了,反正也不从那里获取。
    glDrawElements(GL_TRIANGLES, self.count, GL_UNSIGNED_INT, 0);
}

- (IBAction)xClick:(id)sender {
    self.roundX = !self.roundX;
}

- (IBAction)yClick:(id)sender {
    self.roundY = !self.roundY;
}

- (IBAction)zClick:(id)sender {
    self.roundZ = !self.roundZ;
}

#pragma mark - 销毁
- (void)dealloc{
    
    if ([EAGLContext currentContext] == self.myContext) {
        [EAGLContext setCurrentContext:nil];
    }
    
    dispatch_source_cancel(_timer);
    _timer = nil;
    
}

@end

效果图如下图1.1所示:

1.1.png

相关文章

网友评论

    本文标题:第十六节—GLKit使用索引绘图

    本文链接:https://www.haomeiwen.com/subject/ynfjpktx.html