OpenGL ES内容(2)

2020-09-30  本文已影响0人  ZZ_军哥

废话不多说.开始写代码,用OpenGL ES加载一张图片(代码已经经过我测试)
导入系统库

#import <GLKit/GLKit.h> //由iOS提供,里面提供了一些对OpenGL ES封装的内容
#import <OpenGLES/ES3/gl.h>  //OpenGL ES 系统库
#import <OpenGLES/ES3/glext.h> //OpenGL ES 系统库
@interface TestView ()
@property(nonatomic,strong)CAEAGLLayer *myLayer; //显示的layer
@property(nonatomic,strong)EAGLContext *myContext; //图形上下文
@property(nonatomic,assign)GLuint myProgram; //链接两个shader的连接器
@property(nonatomic,assign)GLuint frameBuffer; //帧缓冲区
@property(nonatomic,assign)GLuint renderBuffer; //渲染缓冲区
@end

新建一个TestView类,添加到VC的View上,重写TestView 的Layer系统方法,将其替换为显示OpenGl ES的图层

+ (Class)layerClass
{
    return [CAEAGLLayer class];
}

在layoutSubviews里分了5个阶段内容,前面4个阶段基本是固定死的,不会有啥变动
主要是第5个阶段

- (void)layoutSubviews
{
    //1.设置图层
    [self setUpLayer];
    //2.设置上下文
    [self setUpContext];
    //3.设置渲染缓冲区
    [self setUpRenderBuffer];
    //4.设置帧缓冲区
    [self setUpFrameBuffer];
    //5.开始渲染
    [self render];
}

1.设置图层

- (void)setUpLayer
{
    //将CALayer变成CAEAGLLayer
    self.myLayer = (CAEAGLLayer *)self.layer;
  //设置View的比例系数
    [self setContentScaleFactor:[UIScreen mainScreen].scale];
  //设置CAEAGLLayer的颜色空间
    self.myLayer.drawableProperties = @{kEAGLDrawablePropertyColorFormat:kEAGLColorFormatRGBA8,kEAGLDrawablePropertyRetainedBacking:@(false)};
}

2.设置上下文

- (void)setUpContext
{
    //创建一个ES3.0的上下文,并将其设置为当前上下文
    self.myContext = [[EAGLContext alloc]initWithAPI:kEAGLRenderingAPIOpenGLES3];
    [EAGLContext setCurrentContext:self.myContext];
}

3.设置渲染缓冲区

- (void)setUpRenderBuffer
{
    //申请渲染缓存区标识
    glGenRenderbuffers(1, &_renderBuffer);
    //绑定渲染缓存区
    glBindRenderbuffer(GL_RENDERBUFFER, self.renderBuffer);
    //将标识符绑定到GL_RENDERBUFFER
    [self.myContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:self.myLayer];
}

4.设置帧缓存区

- (void)setUpFrameBuffer
{
    //申请帧缓冲区标识
    glGenFramebuffers(1, &_frameBuffer);
    //绑定帧缓冲区
    glBindFramebuffer(GL_FRAMEBUFFER, self.frameBuffer);
    //将_renderBuffer 装配到GL_COLOR_ATTACHMENT0 附着点上
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, self.renderBuffer);
}

5.接下来就是在render中的代码
第一步:

   //1.设置背景色绿色(RGBA)
    glClearColor(0, 1, 0, 1);
    //2.清理颜色缓存区
    glClear(GL_COLOR_BUFFER_BIT);
    //3.创建视口
    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);

第二步.配置顶点和纹理坐标

    GLfloat vertice[] = {
        
        //下面的三角形
        -0.55,0.5,0,   1,0,  //左上角
        -0.55,-0.5,0,  0,0,  //左下角
        0.45,-0.5,0,   1,0,  //右下角
        //上面的三角形
        -0.45,0.5,0,   1,0,  //左上角
        0.55,0.5,0,    1,1,   //右上角
        0.55,-0.5,0,    1,1   //右下角
    };

第三步:编写着色器,新建两个empty文件,shader.vsh和shader.fsh,文件名和后缀名按你喜好随便写.
1.shader.vsh中的代码:注意本来这个文件中时不能有注释的,否则编译该字符串时容易失败,只是为了讲解写的注释

//1.申明position 和 textureCoord 用来接收外部传过来的顶点坐标和纹理坐标
attribute vec3 position;
attribute vec2 textureCoord;
//2.将纹理坐标从顶点着色器传入到片元着色器的通道
varying lowp vec2 varingPosition;

void main()
{
    varingPosition = textureCoord;
    //3. gl_Position为OpenGL ES内部已经创建的变量,顶点数据一定要给它,否则无效
    gl_Position = position;
}

2.shader.fsh的代码:注意一点,片元主色器中未申明的变量不会配给精度,需要自己设置精度,否则运算会出错.而顶点着色器会配默认精度

//由于片元着色器的没有申明精度的变量不会默认精度,如果没有精度,会出错,全局申明float类型为高精度
precision highp float;
//和顶点着色器中的通道必须一样
varying lowp vec2 varingPosition;
//纹理采样器
uniform lowp sampler2D colorMap;
void main()
{
    /*gl_FragColor为内建变量,颜色赋值给该变量,OpenGL ES才能获取
     texture2D为内建函数,根据采样器和顶点获取纹理数据
    */
    gl_FragColor = texture2D(colorMap,varingPosition);
}

3.将两个shader文件字符串加载运行到program中

- (GLuint)loadProgramVerFile:(NSString *)verFile fragFile:(NSString *)fragFile
{
    //声明顶点,片元着色器变量
    GLuint vershader,fragmentShader;
    //创建program
    GLint program = glCreateProgram();
    //将两个着色器中的字符串编译成程序
    [self compileShader:&vershader file:verFile type:GL_VERTEX_SHADER];
    [self compileShader:&fragmentShader file:fragFile type:GL_FRAGMENT_SHADER];
    //将两个着色器程序附着在program中,后面用program对着色器进行操作
    glAttachShader(program, vershader);
    glAttachShader(program, fragmentShader);
    //删除已经附着完的shader
    glDeleteShader(vershader);
    glDeleteShader(fragmentShader);
    return program;
}
- (void)compileShader:(GLuint *)shader file:(NSString *)file type:(GLint)type
{
    //获取字符串
    NSString *str = [NSString stringWithContentsOfFile:file encoding:NSUTF8StringEncoding error:nil];
    //转化为C字符串
    const GLchar *source = (GLchar *)[str UTF8String];
    //创建着色器
    *shader = glCreateShader(type);
    //将文件中的字符串载入到shader中
    glShaderSource(*shader, 1, &source, NULL);
    //编译shader生成程序
    glCompileShader(*shader);
}

第四部:render方法中

    //2.编译链接两个着色器
    NSString *verFile = [[NSBundle mainBundle]pathForResource:@"shader.vsh" ofType:nil];
    NSString *fragFile = [[NSBundle mainBundle]pathForResource:@"shader.fsh" ofType:nil];
 
    //将两个着色器文件传入链接成program
    self.myProgram = [self loadProgramVerFile:verFile fragFile:fragFile];
    //链接program(有可能失败,因为你的shader文件代码可能写错)
    glLinkProgram(self.myProgram);
    //获取连接状态
    GLint linkStatus;
    glGetProgramiv(self.myProgram, GL_LINK_STATUS, &linkStatus);
    if (linkStatus == GL_FALSE) {
        GLchar message[1000];
        //获取连接错误信息
        glGetProgramInfoLog(self.myProgram, sizeof(message), NULL, message);
        NSString *log = [[NSString alloc]initWithUTF8String:message];
        NSLog(@"链接失败:%@",log);
        return;
    }
    //开始使用program
    glUseProgram(self.myProgram);

第五部,开始将顶点信息传入到着色器中,

    //将顶点数据从内存复制到现存
    GLuint buffer;
    glGenBuffers(1, &buffer);
    glBindBuffer(GL_ARRAY_BUFFER, buffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertice), vertice, GL_STATIC_DRAW);
    //获取顶点着色器中的 顶点属性通道 attribute vec3 position;传入的变量名和shader文件的名称一样
    GLint position = glGetAttribLocation(self.myProgram, "position");
    //开启position属性传输通道,默认是关闭的
    glEnableVertexAttribArray(position);
    //将顶点数据传入到shader中
    glVertexAttribPointer(position, 6, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*5, NULL);
    //纹理坐标的传入和顶点一样
    GLint textureCoord = glGetAttribLocation(self.myProgram, "textureCoord");
    glEnableVertexAttribArray(textureCoord);
        glVertexAttribPointer(position, 6, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*5, (GLfloat *)NULL+3);
    //设置纹理内容
    [self setUpTexture];
    //获取着色器
    GLint colorMap = glGetUniformLocation(self.myProgram, "colorMap");
    //第一个纹理,在设置纹理时也设置为0
    glUniform1f(colorMap, 0);
    
    //开始绘制图像 1.三角形 2.从0开始 3.6个顶点
    glDrawArrays(GL_TRIANGLES, 0, 6);
    
    //将绘制好的内容呈现到屏幕上来
    [self.myContext presentRenderbuffer:GL_RENDERBUFFER];

第五步:在家纹理

- (void)setUpTexture
{
    //图片解压缩,变成位图,具体可以网上搜,本文不讲解
    CGImageRef imageRef = [UIImage imageNamed:@"girl"].CGImage;
    size_t width = CGImageGetWidth(imageRef);
    size_t height = CGImageGetHeight(imageRef);
    GLubyte *datas = calloc(width*height*4, sizeof(GLubyte));
    //将图片转化为位图
    CGContextRef contextRef = CGBitmapContextCreate(datas, width, height, 8, width*4, CGImageGetColorSpace(imageRef), kCGImageAlphaPremultipliedLast);
    CGRect rect = CGRectMake(0, 0, width, height);
    CGContextDrawImage(contextRef, rect, imageRef);
    
    //绑定纹理,将其设置成第0个纹理,与glUniform1f(colorMap, 0);对应
    glBindTexture(GL_TEXTURE0, 0);
    float w = width;
    float h = height;
    //设置纹理格式
    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);
    //加载纹理数据
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, datas);
    free(contextRef);
    free(datas);
}

上一篇下一篇

猜你喜欢

热点阅读