OpenGL / OpenGL ES Start: OpenGL ES rendering picture

Series Recommended Reading:
OpenGL / OpenGL ES Getting Started: graphics API and professional terms to resolve
OpenGL / OpenGL ES entry: the rendering process and fixed storage shader
OpenGL / OpenGL ES Getting Started: image rendering implementation and rendering problems
OpenGL / OpenGL ES Getting Started: Basic transformation - acquaintance vectors / matrices
OpenGL / OpenGL ES entry: Probe texture - analytical common API
OpenGL / OpenGL ES entry: textures - case analysis and texture coordinates (pyramid)
OpenGL / OpenGL ES entry: vertex shader and fragment shader (OpenGL transition ES OpenGL)
OpenGL / OpenGL ES entry: GLKit and API Introduction to
OpenGL / OpenGL ES entry: GLKit and use cases

OpenGL ES rendering picture

In previous articles, we use OpenGL, GLKitetc. to render a picture, this article we use OpenGL ESto render a picture display.

Framebuffer object (FRAMEBUFFER)

OpenGLThe frame buffer to draw the desired state of an object is encapsulated, the frame buffer become the target ( the FBO ). Although the frame buffer contains the name of a "buffer zone" word, but in fact it is not a buffer. In fact, there is associated with a frame buffer object real memory storage space. Framebuffer object is a container, which can hold other objects do have memory storage and can be rendered, such as rendering buffer ( the RBO ), and a texture buffer ( TBO ). In this manner, frame buffer objects can be stored in OpenGLthe required state and the surface to bind together the output line.

Use FBO , the need to add images to render to a FBO . Once a FBO is created, settings and binding. Most OpenGLoperations like rendering to a window as execution, but the output will be stored bound to FBO image.

Only one FBO can bind to draw, and only one of them FBO can bind to read.

Create a new FBO

GLuint buffer;
glGenFramebuffers(1, &buffer);
// 然后在绑定一个新的FBO来修改和使用
glBindFramebuffer(GL_FRAMEBUFFER, buffer);
// 将渲染缓存区RenderBuffer通过glFramebufferRenderbuffer函数绑定到 GL_COLOR_ATTACHMENT0上。
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, buffer);
复制代码

After generating the frame buffer, it is necessary to renderbufferfollow framebufferbinding, calls glFramebufferRenderbuffera function to bind to the corresponding attachment points, the latter acting to draw

To bind to GL_FRAMEBUFFERthe target, then all read and write operations will affect the frame buffer to the frame buffer currently bound. Also can separate from the frame buffer to bind to the target read or write, were used GL_READ_FRAMEBUFFER, or GL_DRAW_FRAMEBUFFERto do it. If bound to GL_READ_FRAMEBUFFER, all the read operations can be performed, like glReadPixelsthis function uses; bound to GL_DRAW_FRAMEBUFFERthe, allows rendering, emptying and other write operations. Most of the time you do not have to be separated by, usually the two are bound to GL_FRAMEBUFFERthe line .

FBO destroyed in finished using the FBO , or when they are cleared before exiting, you want to delete.

glDeleteFramebuffers(1, &buffer);
复制代码

Render buffer object (RenderBuffer, RBO)

An renderbufferobject is a 2D image buffer assigned by the application. renderbufferDistribution and storage can be used to color, depth, stencil, or values. In one it can also framebufferbe used as attachment color, depth, stencil. A renderbufferis similar to a window screen system provides a drawing surface, you may be given FBOan arbitrary selection of desired RBOcombinations.

And FBOsimilar, RBOneed to be bound to modify. When you bind render only legitimate destination buffer GL_RENDERBUFFER.

gluint buffer;
glGenRenderbuffers(1, &buffer);
glBindRenderbuffer(GL_RENDERBUFFER, buffer);
复制代码

OpenGL ES rendering picture code examples

Case goals

  • A EAGLrendering surface creation screen
  • Loading the vertex / fragment shader
  • Creating a program object, and the link vertex / fragment shader and linking the program object
  • Set Viewport
  • Clear the color buffer
  • Rendering simple primitives
  • The contents of the color buffer in EAGLthe rendering window performance

Image rendering processes for implementing the

Specific code implementation

Create a shader file

In Xcode, create a new blank document, as follows

Then name

Name of the file they can easily define, and its own suffix can also be easily defined as a string, it would be best able to associate, for example, where the vertex shader named vsh .

Vertex shader code

// 顶点坐标
attribute vec4 position;
// 纹理坐标
attribute vec2 textCoordinate;
// 纹理坐标
varying lowp vec2 varyTextCoord;
void main() {
    varyTextCoord = textCoordinate;
    gl_Position = position;
}
复制代码

Here is best not to file written comments, inexplicable error may occur. And there is no code hints, it is necessary to ensure the accuracy of this code.

In mainfunction, the varyingmodified varyTextCoordtransmitted to the fragment shader texture coordinates. Finally, to the built-in variable gl_Positionassignment, remember not to forget this step, otherwise all operations are in vain.

Code fragment shader

// 纹理坐标
varying lowp vec2 varyTextCoord;
// 纹理采样器(获取对应的纹理ID)
uniform sampler2D colorMap;
void main() {
    gl_FragColor = texture2D(colorMap, varyTextCoord);
}
复制代码

texture2D(纹理采样器,纹理坐标)Get the texture coordinates corresponding to a pixel, gl_FragColorbut also built-in variables, this step can not forget to write.

Set layer

    // 创建特殊图层
    self.myEagLayer = (CAEAGLLayer *)self.layer;
    // 设置scale
    [self setContentScaleFactor:[[UIScreen mainScreen]scale]];
    // 设置属性
    self.myEagLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:@false,kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8,kEAGLDrawablePropertyColorFormat,nil];
复制代码

CAEAGLLayerIs designed to provide OpenGLa special layer used exclusively, furthermore necessary to be rewritten layerClass, the layer CALayerreplaced CAEAGLLayer.

+ (Class)layerClass {
    return [CAEAGLLayer class];
}
复制代码

If you do not write, the following error message appears:

Then set the properties described, kEAGLDrawablePropertyRetainedBackingand kEAGLDrawablePropertyColorFormat.

kEAGLDrawablePropertyRetainedBackingAfter drawing showing a display surface, whether to retain its contents.
kEAGLDrawablePropertyColorFormatThe drawing represents the internal surface of the color buffer formats, this keyvalue corresponding to a NSStringspecific color buffer specified object. Default kEAGLColorFormatRGBA8.

kEAGLColorFormatRGBA8: 32-bit RGBA color, 4 * 8 = 32 bits
kEAGLColorFormatRGB565: 16-bit RGB color
kEAGLColorFormatSRGBA8: the sRGB represent standard red, green, and blue, i.e., CRT displays, LCD displays, projectors, printers, and other devices in the color reproduction three basic pigments used. sRGB color space based on the independent color coordinates, can make use of different colors corresponding to the same transmission apparatus in a color coordinate system, without being affected by each of these devices having different color coordinates.

Setting the context

    // 指定OpenGL ES 渲染API版本,我们使用3.0
    EAGLRenderingAPI api = kEAGLRenderingAPIOpenGLES3;
    // 创建图形上下文
    EAGLContext *context = [[EAGLContext alloc]initWithAPI:api];
    // 判断是否创建成功
    if (!context) {
        NSLog(@"Create context failed!");
        return;
    }
    // 设置图形上下文
    if (![EAGLContext setCurrentContext:context]) {
        NSLog(@"setCurrentContext failed!");
        return;
    }
    // 将局部context,变成全局的
    self.myContext = context;
复制代码

Empty the cache

    glDeleteBuffers(1, &_myColorRenderBuffer);
    self.myColorRenderBuffer = 0;
    glDeleteBuffers(1, &_myColorFrameBuffer);
    self.myColorFrameBuffer = 0;
复制代码

bufferDivided framebufferand renderbuffer. Which framebufferis equivalent to renderbufferthe manager. frame buffer objectThat is called FBO. renderbufferThey can be divided into three colorBuffertypes: depthBuffer, , stencilBuffer.

Set RenderBuffer

    GLuint buffer;
    glGenRenderbuffers(1, &buffer);
    self.myColorRenderBuffer = buffer;
    
    // 将标识符绑定到GL_RENDERBUFFER
    glBindRenderbuffer(GL_RENDERBUFFER, self.myColorRenderBuffer);
    // 将可绘制对象drawable object's  CAEAGLLayer的存储绑定到OpenGL ES renderBuffer对象
    [self.myContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:self.myEagLayer];
复制代码

Set FrameBuffer

    GLuint buffer;
    glGenRenderbuffers(1, &buffer);
    self.myColorFrameBuffer = buffer;
    
    // 将标识符绑定到GL_FRAMEBUFFER
    glBindFramebuffer(GL_FRAMEBUFFER, self.myColorFrameBuffer);
    
    // 将渲染缓存区myColorRenderBuffer 通过glFramebufferRenderbuffer函数绑定到 GL_COLOR_ATTACHMENT0上。
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, self.myColorRenderBuffer);
复制代码

After generating the frame buffer, it is necessary to renderbufferfollow framebufferbinding, calls glFramebufferRenderbuffera function to bind to the corresponding attachment points, the latter acting to draw

draw

Routine step drawing:

  • Set clear color screen, the screen is cleared, the viewport size
    // 设置清屏颜色
    glClearColor(0.3f, 0.45f, 0.5f, 1.0f);
    // 清除屏幕
    glClear(GL_COLOR_BUFFER_BIT);
    // 设置视口大小
    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);
复制代码
  • Reading the vertex shader, fragment shader
    NSString *vertFile = [[NSBundle mainBundle]pathForResource:@"shaderv" ofType:@"vsh"];
    NSString *fragFile = [[NSBundle mainBundle]pathForResource:@"shaderf" ofType:@"fsh"];
复制代码
  • Load shader
- (GLuint)loadShaders:(NSString *)vert Withfrag:(NSString *)frag {
    // 定义2个临时着色器对象
    GLuint verShader, fragShader;
    // 创建program
    GLint program = glCreateProgram();
    
    // 编译顶点着色程序、片元着色器程序
    //参数1:编译完存储的底层地址
    //参数2:编译的类型,GL_VERTEX_SHADER(顶点)、GL_FRAGMENT_SHADER(片元)
    //参数3:文件路径
    [self compileShader:&verShader type:GL_VERTEX_SHADER file:vert];
    [self compileShader:&fragShader type:GL_FRAGMENT_SHADER file:frag];
    
    // 创建最终的程序
    glAttachShader(program, verShader);
    glAttachShader(program, fragShader);
    
    // 释放不需要的shader
    glDeleteShader(verShader);
    glDeleteShader(fragShader);
    return program;
}
复制代码
  • Compiled shader
- (void)compileShader:(GLuint *)shader type:(GLenum)type file:(NSString *)file {
    // 读取文件路径字符串
    NSString* content = [NSString stringWithContentsOfFile:file encoding:NSUTF8StringEncoding error:nil];
    const GLchar* source = (GLchar *)[content UTF8String];
    
    // 创建一个shader(根据type类型)
    *shader = glCreateShader(type);
    
    // 将着色器源码附加到着色器对象上。
    //参数1:shader,要编译的着色器对象 *shader
    //参数2:numOfStrings,传递的源码字符串数量 1个
    //参数3:strings,着色器程序的源码(真正的着色器程序源码)
    //参数4:lenOfStrings,长度,具有每个字符串长度的数组,或NULL,这意味着字符串是NULL终止的
    glShaderSource(*shader, 1, &source,NULL);
    
    // 把着色器源代码编译成目标代码
    glCompileShader(*shader);
}
复制代码
  • Link, useglLinkProgram
    // 链接(这里的self.myPrograme就是在上面加载得到的)
    glLinkProgram(self.myPrograme);
    GLint linkStatus;
    // 获取链接状态
    glGetProgramiv(self.myPrograme, GL_LINK_STATUS, &linkStatus);
    if (linkStatus == GL_FALSE) {
        // 打印报错信息
        GLchar message[512];
        glGetProgramInfoLog(self.myPrograme, sizeof(message), 0, &message[0]);
        NSString *messageString = [NSString stringWithUTF8String:message];
        NSLog(@"Program Link Error:%@",messageString);
        return;
    }
    
    NSLog(@"Program Link Success!");
    // 使用program
    glUseProgram(self.myPrograme);
复制代码
  • Provided vertex, texture coordinates, and processing
    // 前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,
    };
    
    
    // -----处理顶点数据--------
    // 1.顶点缓存区
    GLuint attrBuffer;
    // 2.申请一个缓存区标识符
    glGenBuffers(1, &attrBuffer);
    // 3.将attrBuffer绑定到GL_ARRAY_BUFFER标识符上
    glBindBuffer(GL_ARRAY_BUFFER, attrBuffer);
    // 4.把顶点数据从CPU内存复制到GPU上
    glBufferData(GL_ARRAY_BUFFER, sizeof(attrArr), attrArr, GL_DYNAMIC_DRAW);

    // 将顶点数据通过myPrograme中的传递到顶点着色程序的position
    // 1.glGetAttribLocation,用来获取vertex attribute的入口的.
    // 2.告诉OpenGL ES,通过glEnableVertexAttribArray,
    // 3.最后数据是通过glVertexAttribPointer传递过去的。
    
    // 注意:第二参数字符串必须和shaderv.vsh中的输入变量:position保持一致
    GLuint position = glGetAttribLocation(self.myPrograme, "position");
    
    // 设置合适的格式从buffer里面读取数据
    glEnableVertexAttribArray(position);
    
    // 设置读取方式
    //参数1:index,顶点数据的索引
    //参数2:size,每个顶点属性的组件数量,1,2,3,或者4.默认初始值是4.
    //参数3:type,数据中的每个组件的类型,常用的有GL_FLOAT,GL_BYTE,GL_SHORT。默认初始值为GL_FLOAT
    //参数4:normalized,固定点数据值是否应该归一化,或者直接转换为固定值。(GL_FALSE)
    //参数5:stride,连续顶点属性之间的偏移量,默认为0;
    //参数6:指定一个指针,指向数组中的第一个顶点属性的第一个组件。默认为0
    glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, NULL);
    
    
    // ----处理纹理数据-------
    // glGetAttribLocation,用来获取vertex attribute的入口的.
    //注意:第二参数字符串必须和shaderv.vsh中的输入变量:textCoordinate保持一致
    GLuint textCoor = glGetAttribLocation(self.myPrograme, "textCoordinate");
    
    // 设置合适的格式从buffer里面读取数据
    glEnableVertexAttribArray(textCoor);
    
    // 设置读取方式
    //参数1:index,顶点数据的索引
    //参数2:size,每个顶点属性的组件数量,1,2,3,或者4.默认初始值是4.
    //参数3:type,数据中的每个组件的类型,常用的有GL_FLOAT,GL_BYTE,GL_SHORT。默认初始值为GL_FLOAT
    //参数4:normalized,固定点数据值是否应该归一化,或者直接转换为固定值。(GL_FALSE)
    //参数5:stride,连续顶点属性之间的偏移量,默认为0;
    //参数6:指定一个指针,指向数组中的第一个顶点属性的第一个组件。默认为0
    glVertexAttribPointer(textCoor, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*5, (float *)NULL + 3);
复制代码

This part of the code, in the previous chapter are explained in detail, so I will not explain here focused, comments in the code, it can help you too well understood.

  • Load textures
    // 将 UIImage 转换为 CGImageRef
    CGImageRef spriteImage = [UIImage imageNamed:fileName].CGImage;
    
    //判断图片是否获取成功
    if (!spriteImage) {
        NSLog(@"Failed to load image %@", fileName);
        exit(1);
    }
    
    // 读取图片的大小,宽和高
    size_t width = CGImageGetWidth(spriteImage);
    size_t height = CGImageGetHeight(spriteImage);
    
    // 获取图片字节数 宽*高*4(RGBA)
    GLubyte * spriteData = (GLubyte *) calloc(width * height * 4, sizeof(GLubyte));
    
    // 创建上下文
    /*
     参数1:data,指向要渲染的绘制图像的内存地址
     参数2:width,bitmap的宽度,单位为像素
     参数3:height,bitmap的高度,单位为像素
     参数4:bitPerComponent,内存中像素的每个组件的位数,比如32位RGBA,就设置为8
     参数5:bytesPerRow,bitmap的没一行的内存所占的比特数
     参数6:colorSpace,bitmap上使用的颜色空间  kCGImageAlphaPremultipliedLast:RGBA
     */
    CGContextRef spriteContext = CGBitmapContextCreate(spriteData, width, height, 8, width*4,CGImageGetColorSpace(spriteImage), kCGImageAlphaPremultipliedLast);
    

    // 在CGContextRef上--> 将图片绘制出来
    /*
     CGContextDrawImage 使用的是Core Graphics框架,坐标系与UIKit 不一样。UIKit框架的原点在屏幕的左上角,Core Graphics框架的原点在屏幕的左下角。
     CGContextDrawImage 
     参数1:绘图上下文
     参数2:rect坐标
     参数3:绘制的图片
     */
    CGRect rect = CGRectMake(0, 0, width, height);
   
    // 使用默认方式绘制
    CGContextDrawImage(spriteContext, rect, spriteImage);
   
    // 画图完毕就释放上下文
    CGContextRelease(spriteContext);
    
    // 绑定纹理到默认的纹理ID(
    glBindTexture(GL_TEXTURE_2D, 0);
    
    // 设置纹理属性
    /*
     参数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 fw = width, fh = height;
    
    // 载入纹理2D数据
    /*
     参数1:纹理模式,GL_TEXTURE_1D、GL_TEXTURE_2D、GL_TEXTURE_3D
     参数2:加载的层次,一般设置为0
     参数3:纹理的颜色值GL_RGBA
     参数4:宽
     参数5:高
     参数6:border,边界宽度
     参数7:format
     参数8:type
     参数9:纹理数据
     */
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fw, fh, 0, GL_RGBA, GL_UNSIGNED_BYTE, spriteData);
    
    //11.释放spriteData
    free(spriteData);   
复制代码

The above code is drawn out of the picture is upside down, because there are comments in the code, CGContextDrawImageusing a Core Graphicsframework, coordinate system and UIKitis not the same. UIKitThe origin of the frame in the upper left corner of the screen, Core Graphicsthe lower left corner of the screen frame of origin.

Picture flip solve the problem

With the following codes, this problem can be modified

    CGRect rect = CGRectMake(0, 0, width, height);
    CGContextTranslateCTM(spriteContext, 0, rect.size.height);
    CGContextScaleCTM(spriteContext, 1.0, -1.0);
    CGContextDrawImage(spriteContext, rect, spriteImage);
复制代码

Explain the reasons:
The first step:
CGContextTranslateCTM(spriteContext, 0, rect.size.height);moving in the y-axis direction rect.size.heightdistance

Step:
CGContextScaleCTM(spriteContext, 1.0, -1.0);, this code indicates rotates y-axis direction, to the following figure

After two steps above, the graphics can be flipped over.

Texture sampler is provided, the drawing, from the display to the screen rendering buffer

    // 设置纹理采样器 sampler2D
    glUniform1i(glGetUniformLocation(self.myPrograme, "colorMap"), 0);
    
    glDrawArrays(GL_TRIANGLES, 0, 6);
    
    // 从渲染缓冲区显示到屏幕上
    [self.myContext presentRenderbuffer:GL_RENDERBUFFER];
复制代码

The final renderings:

Unturned:

Flip back:

Reproduced in: https: //juejin.im/post/5cfa680b6fb9a07ef63fced8

Guess you like

Origin blog.csdn.net/weixin_34301132/article/details/91475062