OpenGL

十三、利用GLKit、GLSL使用纹理+颜色绘制一个金字塔

2020-08-04  本文已影响0人  夏天的枫_

利用GLKit绘制

使用GLKit相对于GLSL来说,步骤少许多,毕竟有集成库的加成,少撸许多码。

实现步骤有:

初始化上下文


- (void)initContext
{
      self.view.backgroundColor = [UIColor whiteColor];
    // 初始化上下文
    self.mContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];

    // 初始化GLKView实例
    self.glkView = (GLKView *)self.view;
    self.glkView.frame = self.view.frame;
    self.glkView.context = self.mContext;
    // 设置代理
    self.glkView.delegate = self;
    self.glkView.backgroundColor = [UIColor whiteColor];
    // 设置颜色、深度格式
    self.glkView.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888;
    self.glkView.drawableDepthFormat = GLKViewDrawableDepthFormat24;
    // 设置当前上下文
    if (![EAGLContext setCurrentContext:self.mContext]) {
        NSLog(@"setCurrentContext failed");
    }
    
    // 开启深度测试
    glEnable(GL_DEPTH_TEST);
}

渲染显示
单使用颜色渐变时,替换顶点数据 + 设置读取方式更改步长

/**
 渲染显示
 */
- (void)renderDisplay
{
//    单使用颜色渐变时,替换顶点数据 + 设置读取方式更改步长
//    GLfloat attrArr[] =
//    {
//        -0.5f, 0.5f, 0.0f,      1.0f, 0.0f, 1.0f, //左上
//        0.5f, 0.5f, 0.0f,       1.0f, 0.0f, 1.0f, //右上
//        -0.5f, -0.5f, 0.0f,     1.0f, 1.0f, 1.0f, //左下
//
//        0.5f, -0.5f, 0.0f,      1.0f, 1.0f, 1.0f, //右下
//        0.0f, 0.0f, 1.0f,       0.0f, 1.0f, 0.0f, //顶点
//    };
    // 顶点数据,颜色值,纹理坐标
    GLfloat attrArr[] =
    {
        -0.5f, 0.5f, 0.0f,      1.0f, 0.0f, 1.0f,       0.0f, 1.0f,//左上
        0.5f, 0.5f, 0.0f,       1.0f, 0.0f, 1.0f,       1.0f, 1.0f,//右上
        -0.5f, -0.5f, 0.0f,     1.0f, 1.0f, 1.0f,       0.0f, 0.0f,//左下

        0.5f, -0.5f, 0.0f,      1.0f, 1.0f, 1.0f,       1.0f, 0.0f,//右下
        0.0f, 0.0f, 1.0f,       0.0f, 1.0f, 0.0f,       0.5f, 0.5f,//顶点
    };
    
    //2.绘图索引
    GLuint indices[] =
    {
        0, 3, 2,
        0, 1, 3,
        0, 2, 4,
        0, 4, 1,
        2, 3, 4,
        1, 4, 3,
    };
      
    self.count = sizeof(indices) / sizeof(GLuint);
    
    // 处理顶点数据
    GLuint buffer;
    glGenBuffers(1, &buffer);
    glBindBuffer(GL_ARRAY_BUFFER, buffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(attrArr), attrArr, GL_DYNAMIC_DRAW);

    // 创建一个索引绘图的缓存标志 并绑定它
    GLuint index;
    glGenBuffers(1, &index);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index);
    // 将数据从CPU内存copy只GPU显存
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_DYNAMIC_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);
    // 加载纹理
    [self loadTexture];
    
}
/**
 加载纹理
 */
- (void)loadTexture
{
    // 获得图片路径
    NSString * imgFilePath = [[NSBundle mainBundle] pathForResource:@"miao" ofType:@"jpg"];
    UIImage * image = [UIImage imageWithContentsOfFile:imgFilePath];
    
    NSDictionary * options = @{GLKTextureLoaderOriginBottomLeft:@YES};
    // 加载纹理数据
    GLKTextureInfo * textureInfo = [GLKTextureLoader textureWithCGImage:image.CGImage options:options error:nil];
    self.mEffect = [[GLKBaseEffect alloc] init];
    self.mEffect.texture2d0.enabled = GL_TRUE;
    self.mEffect.texture2d0.name = textureInfo.name;
    
    // 设置透视矩阵
    CGSize size = self.view.bounds.size;
    float aspect = fabs(size.width/size.height);
    GLKMatrix4 projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(90), aspect, 0.1, 100.0f);
    self.mEffect.transform.projectionMatrix = projectionMatrix;
    // 设置模型视图矩阵
    self.mEffect.transform.modelviewMatrix = GLKMatrix4Translate(GLKMatrix4Identity, 0, 0, -3);
    
}

/**
 设置GCD定时器
 */
- (void)setGCDTimer
{
    double seconds = 0.1;
    _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
    dispatch_source_set_timer(_timer, DISPATCH_TIME_NOW, seconds * NSEC_PER_SEC, 0.0);
    dispatch_source_set_event_handler(_timer, ^{
       
        self.XDegree += 0.1f * self.XBool;
      
        self.YDegree += 0.1f * self.YBool;
      
        self.ZDegree += 0.1f * self.ZBool ;
        
        [self reRender];
    });
    dispatch_resume(_timer);
}
#pragma mark - GLKViewDelegate
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
    // 设置清屏颜色
    glClearColor(0.3, 0.4, 0.5, 1);
    // 清楚深度、颜色缓存区
    glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
    // 准备绘制
    [self.mEffect prepareToDraw];
    // 绘制
    glDrawElements(GL_TRIANGLES, self.count, GL_UNSIGNED_INT, 0);
    
}
/**
 重新渲染
 */
- (void)reRender
{
    // 平移模型
    GLKMatrix4 modelViewMatrix = GLKMatrix4Translate(GLKMatrix4Identity, 0, 0, -2.5);
    // 旋转
    modelViewMatrix = GLKMatrix4RotateX(modelViewMatrix, self.XDegree);
    modelViewMatrix = GLKMatrix4RotateY(modelViewMatrix, self.YDegree);
    modelViewMatrix = GLKMatrix4RotateZ(modelViewMatrix, self.ZDegree);
    // 设置效果
    self.mEffect.transform.modelviewMatrix = modelViewMatrix;
    
    [self.glkView display];
}

GLSL方式绘制

这是在之前索引绘图 & 自定义着色器 绘制金字塔案例上实现的;

实现步骤

渲染显示


/**
 渲染显示
 */
- (void)renderDisplay
{
    
    glClear(GL_COLOR_BUFFER_BIT);
    glClearColor(0.4, 0.6, 0.7, 1);
    
    CGFloat scale = [[UIScreen mainScreen] scale];
    // 设置视口
    CGPoint orgin = self.frame.origin;
    CGSize size = self.frame.size;
    glViewport(orgin.x * scale, orgin.y * scale, size.width * scale, size.height * scale);
    
    // 获取顶点着色程序、片元着色程序文件文职
    NSString * shaderVertex = [[NSBundle mainBundle] pathForResource:@"shaderv" ofType:@"glsl"];
    NSString * shaderFragment = [[NSBundle mainBundle] pathForResource:@"shaderf" ofType:@"glsl"];
    // 判断program是否存在,否则清空program
    if (self.mProgram) {
        glDeleteProgram(self.mProgram);
        self.mProgram = 0;
    }
    
    // 加载程序到program中来
    self.mProgram = [self loadShaderV:shaderVertex frag:shaderFragment];
    // 链接程序与着色器
    glLinkProgram(self.mProgram);
    // 获取链接状态,为true 则glUseProgram
    GLint linkStatus;
    glGetProgramiv(self.mProgram, GL_LINK_STATUS, &linkStatus);
    if (linkStatus == GL_FALSE) {
        GLchar message[256];
        glGetProgramInfoLog(self.mProgram, sizeof(message), 0, &message[0]);
        NSString * messageInfo = [NSString stringWithUTF8String:message];
        NSLog(@"program link error:%@",messageInfo);
        return;
    }
    NSLog(@"program link success");
    glUseProgram(self.mProgram);
    
    //====准备顶点数据 & 索引数组=====
//    GLfloat attrArr[] =
//    {
//        -0.5f, 0.5f, 0.0f,      1.0f, 0.0f, 1.0f, //左上0
//        0.5f, 0.5f, 0.0f,       1.0f, 0.0f, 1.0f, //右上1
//        -0.5f, -0.5f, 0.0f,     1.0f, 1.0f, 1.0f, //左下2
//        
//        0.5f, -0.5f, 0.0f,      1.0f, 1.0f, 1.0f, //右下3
//        0.0f, 0.0f, 1.0f,       0.0f, 1.0f, 0.0f, //顶点4
//    };
    
    // 顶点数据,颜色值,纹理坐标
    GLfloat attrArr[] =
    {
        -0.5f, 0.5f, 0.0f,      1.0f, 0.0f, 1.0f,       0.0f, 1.0f,//左上
        0.5f, 0.5f, 0.0f,       1.0f, 0.0f, 1.0f,       1.0f, 1.0f,//右上
        -0.5f, -0.5f, 0.0f,     1.0f, 1.0f, 1.0f,       0.0f, 0.0f,//左下
        
        0.5f, -0.5f, 0.0f,      1.0f, 1.0f, 1.0f,       1.0f, 0.0f,//右下
        0.0f, 0.0f, 1.0f,       0.0f, 1.0f, 0.0f,       0.5f, 0.5f,//顶点
    };
    
    //(2).索引数组
    GLuint indices[] =
    {
        0, 3, 2,
        0, 1, 3,
        0, 2, 4,
        0, 4, 1,
        2, 3, 4,
        1, 4, 3,
    };
    
    // 判断顶点缓存区是否为空,如果为空则申请一个缓存区标识符
    if (self.mVertices == 0) {
        glGenBuffers(1, &_mVertices);
    }
    
    // =====处理顶点数据======
    // 将 _mVertices 绑定到 GL_ARRAY_BUFFER
    glBindBuffer(GL_ARRAY_BUFFER, _mVertices);
    // 把顶点数据从CPU内存copy到GPU显存中处理
    glBufferData(GL_ARRAY_BUFFER, sizeof(attrArr), attrArr, GL_DYNAMIC_DRAW);
    // 将顶点数据通过myPrograme中的传递到顶点着色程序的position
    GLuint position = glGetAttribLocation(self.mProgram, "position");
    
    // 打开通道
    glEnableVertexAttribArray(position);
    // 设置读取方式
    NSLog(@"CGFloat %lu\n GLfloat :%lu",sizeof(CGFloat),sizeof(GLfloat));
    glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 8, NULL);
    
    // =====处理顶点颜色值=====
    GLuint positionColor = glGetAttribLocation(self.mProgram, "positionColor");
    glEnableVertexAttribArray(positionColor);
    glVertexAttribPointer(positionColor, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 8, (GLfloat *)NULL + 3);

    // 处理纹理数据
    GLuint textCoor = glGetAttribLocation(self.mProgram, "textCoordinate");
    glEnableVertexAttribArray(textCoor);
    glVertexAttribPointer(textCoor, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 8 ,(GLfloat *)NULL + 6);
    // 加载为例
    [self loadTexture:@"miao.jpg"];
    /**
     GLint location,获取纹理位置
     GLint x,从0开始,有几个纹理,就传第几个纹理
     */
    glUniform1i(glGetUniformLocation(self.mProgram, "colorMap"), 0);
    
    // 根据program 找到 projectionMatrix、modelViewMatrix
    GLuint projectionMatrix = glGetUniformLocation(self.mProgram, "projectionMatrix");
    GLuint modelViewMatrix = glGetUniformLocation(self.mProgram, "modelViewMatrix");
    
    
    float width = self.frame.size.width;
    float height = self.frame.size.height;
    
    // 创建4 * 4 投影矩阵
    KSMatrix4 _projectionMatrix;
    ksMatrixLoadIdentity(&_projectionMatrix);
    // 长宽比
    float aspect = width/height;
    
    // 获取透视矩阵
    /*
    参数1:矩阵
    参数2:视角,度数为单位
    参数3:纵横比
    参数4:近平面距离
    参数5:远平面距离
    */
    ksPerspective(&_projectionMatrix, 30.0, aspect, 5.0f, 20.0f);
    //将投影矩阵传递到顶点着色器
    /*
     location:指要更改的uniform变量的位置
     count:更改矩阵的个数
     transpose:是否要转置矩阵,并将它作为uniform变量的值。必须为GL_FALSE
     value:执行count个元素的指针,用来更新指定uniform变量
     */
    glUniformMatrix4fv(projectionMatrix, 1, GL_FALSE, (CGFloat *)&_projectionMatrix.m[0][0]);
    
    // 创建一个model视图矩阵
    KSMatrix4 _modelViewMatrix;
    // 获取单元矩阵
    ksMatrixLoadIdentity(&_modelViewMatrix);
    // z轴平移-10
    ksTranslate(&_modelViewMatrix,0.0,0.0,-10.0);
    // 创建一个4 * 4 矩阵,旋转矩阵
    KSMatrix4 _rotationMatrix;
    // 初始化为单元矩阵
    ksMatrixLoadIdentity(&_rotationMatrix);
    
    // 旋转
    // 绕x轴
    ksRotate(&_rotationMatrix, xDegree, 1.0, 0.0, 0.0);
    // 绕y轴
    ksRotate(&_rotationMatrix, yDegree, 0.0, 1.0, 0.0);
    // 绕z轴
    ksRotate(&_rotationMatrix, zDegree, 0.0, 0.0, 1.0);
    // 把变换矩阵相乘 将 _modelViewMatrix 矩阵 与 _rotationMatrix 矩阵相乘,结合到模型视图
    ksMatrixMultiply(&_modelViewMatrix, &_rotationMatrix, &_modelViewMatrix);
    //(7)将模型视图矩阵传递到顶点着色器
    /*
     location:指要更改的uniform变量的位置
     count:更改矩阵的个数
     transpose:是否要转置矩阵,并将它作为uniform变量的值。必须为GL_FALSE
     value:执行count个元素的指针,用来更新指定uniform变量
     */
    glUniformMatrix4fv(modelViewMatrix, 1, GL_FALSE, (GLfloat *)&_modelViewMatrix.m[0][0]);
    
    // 开启背面剔除
    glEnable(GL_CULL_FACE);
    
    // 使用索引绘图
    /*
     void glDrawElements(GLenum mode,GLsizei count,GLenum type,const GLvoid * indices);
     参数列表:
     mode:要呈现的画图的模型
                GL_POINTS
                GL_LINES
                GL_LINE_LOOP
                GL_LINE_STRIP
                GL_TRIANGLES
                GL_TRIANGLE_STRIP
                GL_TRIANGLE_FAN
     count:绘图个数
     type:类型
             GL_BYTE
             GL_UNSIGNED_BYTE
             GL_SHORT
             GL_UNSIGNED_SHORT
             GL_INT
             GL_UNSIGNED_INT
     indices:绘制索引数组

     */
    
    glDrawElements(GL_TRIANGLES, sizeof(indices) / sizeof(indices[0]), GL_UNSIGNED_INT, indices);
    // 本地视口显示渲染缓冲区
    [self.mContext presentRenderbuffer:GL_RENDERBUFFER];
    // 开启定时器
    [self gcdTimer];
}

/// 加载纹理
/// @param imageName 纹理名称
- (GLuint)loadTexture:(NSString *)imageName
{
    // 1.将UIImage转化为CGImageRef
    CGImageRef spriteImage = [UIImage imageNamed:imageName].CGImage;
    if (!spriteImage) {
        NSLog(@"load image failed");
        return 1;
    }
    
    // 2.获得图片大小,尺寸
    size_t width = CGImageGetWidth(spriteImage);
    size_t height = CGImageGetHeight(spriteImage);
    // 获取图片字节数,其中RGBA占4个八位 byteData = width * height * 4
    GLubyte * spriteData = (GLubyte *)calloc(width * height * 4, sizeof(GLubyte));
    
    /**
     3.创建上下文
     para1: data,指向要渲染的绘制图像的内存地址
     para2: width,bitmap的宽度,单位为像素
     para3: height,bitmap的高度,单位为像素
     para4: bitPerComponent,内存中像素的每个组件的位数,比如32位RGBA,就设置为8
     para5: bytesPerRow,bitmap的没一行的内存所占的比特数
     para6: colorSpace,bitmap上使用的颜色空间  kCGImageAlphaPremultipliedLast:RGBA
     
     */
    CGContextRef spriteContext = CGBitmapContextCreate(spriteData, width, height, 8, width * 4, CGImageGetColorSpace(spriteImage), kCGImageAlphaPremultipliedLast);
    // 4.在CGContextRef 将图片绘制出来
    
    CGRect rect = CGRectMake(0, 0, width, height);
    
    // 5.使用默认方式绘制
    /**
     CGContextDrawImage 使用的是Core Graphics框架,坐标系与UIKit 不一样。UIKit框架的原点在屏幕的左上角,Core Graphics框架的原点在屏幕的左下角。
     CGContextDrawImage
     参数1:绘图上下文
     参数2:rect坐标
     参数3:绘制的图片
     */
    CGContextDrawImage(spriteContext, rect, spriteImage);
    
    // 平移到x,y
    CGContextTranslateCTM(spriteContext, rect.origin.x, rect.origin.y);
    // 再平移图片高度
    CGContextTranslateCTM(spriteContext, 0, rect.size.height);
    // 沿y轴翻转
    CGContextScaleCTM(spriteContext, 1.0, -1.0);
    // 再平移至原位置
    CGContextTranslateCTM(spriteContext, -rect.origin.x, -rect.origin.y);
    CGContextDrawImage(spriteContext, rect, spriteImage);
    // 6.画图完毕后释放上下文
    CGContextRelease(spriteContext);
    // 7.绑定纹理到默认的纹理ID
    glBindTexture(GL_TEXTURE_2D, 0);
    // 8.设置纹理属性
    /**
     参数1:纹理纬度
     参数2:线性过滤、为s,t坐标设置模式
     参数3:wrapMode,环绕模式
     */
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    
    float fWidht = width,fHeight = height;
    
    // 9.载入纹理
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fWidht, fHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, spriteData);
    
    // 10.释放spriteData
    free(spriteData);
    return 0;
    
    
}

效果

github GLKit 纹理+颜色 金字塔
github GLSL 纹理+颜色 金字塔

上一篇 下一篇

猜你喜欢

热点阅读