4.OpenGL ES 加载图片

尝试用可编程管线简单地加载一张图片。

准备工作

修改main.storyboard默认View、Controller,关联View,CCViewController继承自UIViewController,CCView继承自UIView
4.OpenGL ES 加载图片_第1张图片
image.png

创建可编程管线的顶点着色器文件以及片元着色器文件
4.OpenGL ES 加载图片_第2张图片
image.png

值得注意的是,文件命名及后缀不重要,只要不与系统的文件后缀冲突,以及不影响自己辨别均可

顶点着色器(shaderv.vsh)

自己写记得把中文注释去掉,因为随时可能发生报错的情况

//Vertex shader
//顶点数据
attribute vec4 position;
//纹理
attribute vec2 textCoordinate;

//旋转矩阵
uniform mat4 rotateMatrix;

//将纹理数据传递到片元着色器去
varying lowp vec2 varyTextCoord;

void main(){
    //将textCoordinate 通过 varyTextCoord 传递到片元着色器中去
    varyTextCoord = textCoordinate;
    
    vec4 vPos = position;
    
    //将顶点应用旋转变换
    vPos = vPos * rotateMatrix;
    
    //内建变量,gl_Position 必须赋值
    gl_Position = vPos;   
}
片元着色器(shaderf.fsh)
//OpenGL ES 中的三种修饰类型 uniform attribute varying

//GLSL是一门写着色器程序的语言OpenGL shading language
//OpenGL 固定管线:高级API,通过传递参数实现效果
//可编程管线:能让你对OpenGL渲染过程中其中一些着色环节进行自定义
//可以自定义的环节
/*
 1.顶点着色器->处理每个顶点,确定位置,以及变换
 2.片元着色器->片元,定义每一个像素

 不采用GLBaseEffect,使用编译链接自定义shader,用简单GLSL来实现顶点着色器\片元着色器,并且实现图形简单变换
 思路:
    1.创建图层
    2.创建上下文
    3.清空缓存区
    4.设置RenderBuffer,FrameBuffer
    5.kai'shi'hui'zhi
 */
 
/*
 -------------------------uniform--------------------------------------
    uniform是由外部application程序传递给Vertex Shader,fragment Shader变量
    A.由application通过glUniform**()函数传递值的
    B.在Vertex Shader,fragment Shader内部中,类似C语言的const,它不能被shader修改
 注意:Uniform变量,shader只能用,不能改!!!
 
 例如:
    Uniform mat4 viewProjectMatrix;
    Uniform mat4 viewMatrix;
    Uniform vec3 lightPosition;
 
 
 -------------------------attribute--------------------------------------
 
 一般attribute来表示顶点坐标\法线\纹理坐标\顶点颜色
 
 attribute vec4 a_position;
 attribute vec2 a_textCoord0;
 
 注意:attribute只能在Vertex shader中使用,不能再fragment shader中声明attribute变量,也不能被fragment shader使用
 
 -------------------------------varying-------------------------------------
 varying,在vertex和fragement shader之间传递数据用
 varying vec2 a_textCoord0
 */

varying lowp vec2 varyTextCoord;

//2D纹理贴图
uniform sampler2D colorMap;

void main(){
    //内建变量gl_FragColor必须赋值
    gl_FragColor = texture2D(colorMap,varyTextCoord);
}
CCView.m
#import 

@interface CCView ()

/** CAEAGLLayer */
@property(nonatomic,strong)CAEAGLLayer *myEAGLayer;

/** Context */
@property(nonatomic,strong)EAGLContext *myContext;

/** RenderColor */
@property(nonatomic,assign)GLuint myColorRenderBuffer;

/** FrameColor */
@property(nonatomic,assign)GLuint myColorFrameBuffer;

/** program */
@property(nonatomic,assign)GLuint myProgram;

@end
//入口函数,需要调用的步骤
-(void)layoutSubviews{
    //1.设置图层
    [self setUpLayer];
 
    //2.创建上下文
    [self setUpContext];
    
    //3.清空缓冲区
    [self deleteRenderAndFrameBuffer];
    //4.设置RenderBuffer
    [self setUPRenderBuffer];
    //5.设置FrameBuffer
    [self setUpFrameBuffer];
    
    //6.开始绘制
    [self renderLayer];
}
1.设置图层
-(void)setUpLayer{
    self.myEAGLayer = (CAEAGLLayer *)self.layer;
    
    //2.设置比例因子
    [self setContentScaleFactor:[UIScreen mainScreen].scale];
    
    //3.默认不透明,想要其可见,需设置为不透明
    self.myEAGLayer.opaque = YES;
    
    //4.描述因子
    /*
     kEAGLDrawablePropertyRetainedBacking
     绘图表面显示之后是否保留其内容,一般设置为false;
     它是一个key值,通过一个nsnumber包装bool值
     kEAGLDrawablePropertyColorFormat
     绘制对象内部的颜色缓冲区格式
     kEAGLColorFormatRGBA8  ,4*8,32位的颜色
     kEAGLColorFormatRGB565; 16位
     const kEAGLColorFormatSRGBA8 SRGB
     */
    self.myEAGLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:false],kEAGLDrawablePropertyRetainedBacking,kEAGLColorFormatRGBA8,kEAGLDrawablePropertyColorFormat, nil];
}


+(Class)layerClass{
    return [CAEAGLLayer class];
}
2.创建上下文
-(void)setUpContext{
    //1.指定API版本
    EAGLRenderingAPI api = kEAGLRenderingAPIOpenGLES2;
    
    //2.创建图形上下文
    EAGLContext *context = [[EAGLContext alloc] initWithAPI:api];
    
    //3.创建是否成功
    if (context == NULL) {
        NSLog(@"Create Context Failed!!!");
        return;
    }
    
    //4.设置图形上下文
    if (![EAGLContext setCurrentContext:context]) {
        NSLog(@"setCurrentContext Failed!");
        return;
    }
    //5.将局部变成全局
    self.myContext = context;
}
3.清空缓冲区
 
-(void)deleteRenderAndFrameBuffer{
    /*
     可参考PPT
     buffer氛围FrameBuffer和RenderBuffer 2大类
     frameBuffer(FBO)相当于RenderBuffer的管理者
     
     rendeerbuffer又分为三类:colorbuffer,depthbuffer,setncilbuffer
     
     常用函数
     1.绑定buffer标识
     glGenBuffers(<#GLsizei n#>, <#GLuint *buffers#>)
     glGenRenderbuffers(<#GLsizei n#>, <#GLuint *renderbuffers#>)
     glGenFramebuffers(<#GLsizei n#>, <#GLuint *framebuffers#>)
     2.绑定空间
     glBindBuffer(<#GLenum target#>, <#GLuint buffer#>)
     glBindRenderbuffer(<#GLenum target#>, <#GLuint renderbuffer#>)
     glBindFramebuffer(<#GLenum target#>, <#GLuint framebuffer#>)
     3.删除缓冲区空间
     glDeleteBuffers(1, &_myColorRenderBuffer);
     */
    glDeleteBuffers(1, &_myColorRenderBuffer);
    self.myColorRenderBuffer = 0;
    glDeleteBuffers(1, &_myColorFrameBuffer);
    self.myColorFrameBuffer = 0;
}
4.设置RenderBuffer
-(void)setUPRenderBuffer{
    //1.定义一个缓冲区标记
    GLuint buffer;
    //2.申请一个缓冲区标记
    glGenRenderbuffers(1, &buffer);
    
    //3.
    self.myColorRenderBuffer = buffer;
    
    //4将标识符绑定GL_RENDERBUFFER
    glBindRenderbuffer(GL_RENDERBUFFER, self.myColorRenderBuffer);
    
    //5.
    [self.myContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:self.myEAGLayer];
}
5. 设置FrameBuffer
-(void)setUpFrameBuffer{
    //1.定义一个缓存区标记
    GLuint buffer;

    //2.三个作用都一样都是申请缓冲区标记
    glGenFramebuffers(1, &buffer);
//    glGenRenderbuffers(<#GLsizei n#>, <#GLuint *renderbuffers#>)
//    glGenBuffers(<#GLsizei n#>, <#GLuint *buffers#>)
    //3.
    self.myColorFrameBuffer = buffer;
    //4.
    glBindFramebuffer(GL_FRAMEBUFFER, self.myColorFrameBuffer);
    
    //5.将myColorRenderBuffer通过glFrameBufferRenderBuffer绑定到附着点上,GL_COLOR_ATTACHEMENT0(颜色附着点)
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, self.myColorRenderBuffer);
    
    //6.frame buffer仅仅是管理者,不需要分配空间;render buffer的存储空间的分配,对于不同的render buffer,使用不同的API进行分配,而只有分配空间的时候,render buffer句柄才确定其类型
    
    //myColorRenderBuffer渲染缓存区分配存储空间
    [self.myContext renderbufferStorage:GL_FRAMEBUFFER fromDrawable:self.myEAGLayer];
}
6. 开始绘制
-(void)renderLayer{
    //开始写顶点着色器/片元着色器
    //Vertex shader
    //fragment Shader
    
    //已经写好了顶点shaderv.vsh/片元shaderf.fsh
    glClearColor(0, 1, 0, 1);
    glClear(GL_COLOR_BUFFER_BIT);
    
    
    //2.设置视口大小
    CGFloat scale = [UIScreen mainScreen].scale;
    glViewport(self.frame.origin.x *scale, self.frame.origin.y*scale, self.frame.size.width*scale, self.frame.size.height*scale);
    //3.读取顶点、片元着色器程序
    //获取路径
    NSString *vertFile = [[NSBundle mainBundle] pathForResource:@"shaderv" ofType:@"vsh"];
    NSString *fragFile = [[NSBundle mainBundle] pathForResource:@"shaderf" ofType:@"fsh"];
    NSLog(@"vertFile:%@",vertFile);
    NSLog(@"fragFile:%@",fragFile);
    
    //4.加载shader
    self.myProgram = [self LoadShader:vertFile withFrag:fragFile];
    
    //5.链接
    glLinkProgram(self.myProgram);
    
    //获取link的状态是否失败
    GLint linkStatus;
    glGetProgramiv(self.myProgram, GL_LINK_STATUS, &linkStatus);
    if (linkStatus == GL_FALSE) {
        //获取失败信息
        GLchar message[512];
        glGetProgramInfoLog(self.myProgram, sizeof(message), 0, &message[0]);
        //将C字符串转换为OC字符串
        NSString *messageStr = [NSString stringWithUTF8String:message];
        NSLog(@"Program Link Error: %@",messageStr);
        return;
    }
    //5.使用program
    glUseProgram(self.myProgram);
    //6.设置顶点、纹理坐标
    //前3个是顶点坐标,后2个是纹理坐标
    GLfloat attrArr[] =
    {
         0.5f, -0.5f, 1.0f,     1.0f, 0.0f,
        -0.5f,  0.5f, 1.0f,     0.0f, 1.0f,
        -0.5f, -0.5f, 1.0f,     0.0f, 0.0f,
         0.5f,  0.5f, 1.0f,     1.0f, 1.0f,
        -0.5f,  0.5f, 1.0f,     0.0f, 1.0f,
         0.5f, -0.5f, 1.0f,     1.0f, 0.0f,
    };
    //--处理顶点数据
    GLuint attrBuffer;
    //申请一个缓冲标记
    glGenBuffers(1, &attrBuffer);
    //绑定缓冲区
    glBindBuffer(GL_ARRAY_BUFFER, attrBuffer);
    //将顶点缓冲区从CPU内存复制到GPU内存中
    glBufferData(GL_ARRAY_BUFFER, sizeof(attrArr), attrArr, GL_DYNAMIC_DRAW);
    
    //1.
    GLuint position = glGetAttribLocation(self.myProgram, "position");
    //2.
    glEnableVertexAttribArray(position);
    //3.设置读取方式
    glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, NULL);

    //处理纹理数据
    //1.获取纹理的位置-Program
    GLuint textCoor = glGetAttribLocation(self.myProgram, "textCoordinate");
    
    //2.同顶点
    glEnableVertexAttribArray(textCoor);
    //3.//最后一个参数,起点
    glVertexAttribPointer(textCoor, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, (GLfloat *)NULL + 3);
    
    //加载纹理
    //通过一个自定义的方法加载纹理
    [self setupTexture:@"timg-2"];
    
    //1.直接用3D数学的公式来实现旋转
    //2.Uniform
    
    //旋转!!!矩阵 -> Uniform 传递到vsh,fsh
    
    //旋转180度-->转弧度
    float radius = 180 * 3.1415925f/180.0f;
    //旋转矩阵公式
    float s = sin(radius);
    float c = cos(radius);
    //构建旋转矩阵-围绕z轴旋转-列矩阵
    GLfloat zRotation[16] = {
        c,-s,0  ,  0,
        s, c,0  ,  0,
        0, 0,1.0,  0,
        0, 0,0  ,1.0,
    };
    //获取位置
    GLuint rotate = glGetUniformLocation(self.myProgram, "rotateMatrix");
    
    //将旋转矩阵通过Uniform传递进去
    
    glUniformMatrix4fv(rotate, 1, GL_FALSE, (GLfloat *)&zRotation[0]);
    
    //绘制,三角形绘制,从0开始,6个顶点
    glDrawArrays(GL_TRIANGLES, 0, 6);
    
    [self.myContext presentRenderbuffer:GL_RENDERBUFFER];
}

封装的辅助函数

-(GLuint)LoadShader:(NSString *)vert withFrag:(NSString *)frag{
    //1.定义2个临时着色器变量对象
    GLuint verShader,fragShader;
    GLuint program = glCreateProgram();
    
    //2.编译shader
    [self compileShader:&verShader type:GL_VERTEX_SHADER file:vert];
    [self compileShader:&fragShader type:GL_FRAGMENT_SHADER file:frag];
    
    //3.创建目标程序
    glAttachShader(program, verShader);
    glAttachShader(program, fragShader);
    
    //4.释放
    glDeleteShader(verShader);
    glDeleteShader(fragShader);
    
    return program;
}

//编译shader
-(void)compileShader:(GLuint *)shader type:(GLenum)type file:(NSString *)file{
    //读取shader路径
    NSString *content = [NSString stringWithContentsOfFile:file encoding:NSUTF8StringEncoding error:nil];
    //将oc字符串转为C字符串
    const GLchar *source = (GLchar *)[content UTF8String];
    //创建shader
    *shader = glCreateShader(type);
    //将着色器的代码,附到shader上
    glShaderSource(*shader, 1, &source, NULL);
    
    //将着色器代码编译成目标代码
    glCompileShader(*shader);
}

你可能感兴趣的:(4.OpenGL ES 加载图片)