OpnGL的初识

前言

开发OpenGL App一年多了, 平时就是在各种赶项目, 赶功能; 做了不少笔记, 踩了不少坑, 但是仅仅是工作需要就去学习哪块, 很少静下来做总结;

今年年春换了一家东家, 开发直播类App, 需要视频处理等知识, 正好总结一番;

目前发现,在OpenGL的世界里 相对于渲染管线来说, 数学才是真正的大头;

相比于复杂的理论知识, 我们直接手上吧.


  • 本文环境

    • Xcode 9.0

    • OpenGLES 3.0

基本操作步骤

  • 导入GLKit框架

  • 直接集成GLKViewController, 或者GLKView

  • 创建OpenGL上下文 EAGLContext

  • 创建程序对象ShaderProgram

    • 连接顶点着色器片段着色器
  • 指定清屏颜色,填充到ColorBuffer


导入框架, 直接集成与GLKViewController

#import 

@interface ViewController : GLKViewController

@end

创建OpenGL 上下文

  • EAGLContext
    • 调度中枢

    • 保存绘制命令, 状态, 资源所有调度;

    • 因为OpenGL 是状态机机制, 故需要一个调度中枢的存在

    • 这里直接使用OpenGLES 3.0

#pragma mark - setupContext
- (void)setupContext {
    
    // 1.0 创建OpenGl上下文
    self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
    
#if DEBUG
    if (!self.context) {
        NSLog(@"Create OpenGLES3.0 context fail.");
    }
#endif
}
  • 设置Context为当前上下文, 并传递到glview
[EAGLContext setCurrentContext:self.context];
 
GLKView *glView = (GLKView *)self.view;
glView.context = self.context;

创建程序对象Program

  • 创建顶点着色器, 片段着色器

    • 着色器最终以字符串的形式传递到program

    • 下面是最简单的两个着色器, 有影响即可, 暂无需深入

创建glsl
着色器
  • 顶点着色器Vertxt.glsl
// 顶点着色器

// 输入变量
attribute vec4 position;
attribute vec4 color;

// 输出变量
varying vec4 fragColor;

void main(void) {
    
    // 传递到片段着色器
    fragColor = color;
    
    // 内建变量
    gl_Position = position;
}
  • 片段着色器Fragment.glsl
// 片段着色器

varying lowp vec4 fragColor;

void main(void) {

    // 内建变量
    gl_FragColor = fragColor;
}
  • 创建program固定套路

    • 创建program

    • 加载着色器, 并关联到program

    • 链接porgram

    • 释放shader资源

GLuint program, vertShader, fragShader;
    
// 创建program
program = glCreateProgram();

// 加载shader (顶点着色器, 片段着色器)
*shader = glCreateShader(type);    
glShaderSource(*shader, 1, &source, NULL);
glCompileShader(*shader);

// 关联着色器到program
glAttachShader(program, vertShader);
glAttachShader(program, fragShader);

// 链接programe
glLinkProgram(program);

// 释放shader资源
glDetachShader(program, vertShader);
glDeleteShader(vertShader);

glDetachShader(program, fragShader);
glDeleteShader(fragShader);

渲染

  • 实现GLKViewDelegate代理方法

    • 该方法会被定时调用默认是1s/60帧
  • 设置清屏颜色

#pragma mark - GLKViewDelegate
- (void)update {
    
}

- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
    
    // 设置清屏颜色, 并填充到colorBuffer
    glClearColor(0.0, 0.0, 1.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);
}

全流程已经实现, 运行即可.

题外话, 如果对上述一些关键名字不甚理解的话, 也无妨; 大概有个影响即可;

毕竟后面见多了, 也就熟悉了.


Others

除了使用GLKViewController, 是否还有其他方法?

当然, 还可以直接使用UIViewController, 重写- (void)loadView方法, 返回GLKView.

- (void)loadView {
    self.view = [[GLKView alloc] initWithFrame:[UIScreen mainScreen].bounds];
}

- (void)setupGLKView {
    GLKView *glView = (GLKView *)self.view;
    
    // 此时需设置代理, 如果是`GLKViewController`已经默认设置代理为本身, 如`UITablviewController`
    glView.delegate = self;
    glView.context = self.context;
}

// 其他和使用`GLKViewController`一致
  • 此时发现代理方法只会执行一次

    • 需创建定时器, 重复调用代理方法
const NSInteger DefaultFramesPerSecond = 30;

// 手动刷新
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(draw)];
[self.displayLink setPreferredFramesPerSecond:MAX(1, 60.0f / DefaultFramesPerSecond)];
[self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];

- (void)draw {
    GLKView *glView = (GLKView *)self.view;
    [self update];
    
    // 不能手动调用`- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect`; 需调用`display`
    [glView display];
}

一层层往下扒的话, 发现真正用来渲染的是CAEAGLLayer;

GLKViewCAEAGLLayer做了一层封装;

GLKViewControllerGLKView做了一层封装, 设置自身为GLKView代理;

苹果到底对GLKView做什么?

首先是GLKView的layer是CAEAGLLayer

+ (Class)layerClass {

    // OpenGL在iOS中,只能在CAEAGLLayer类型的layer上绘制内容
    return [CAEAGLLayer class];
}
// 设置layer的属性
- (void)setupLayer{

    _eaglLayer = (CAEAGLLayer *)self.layer;
    
    // 设置layer为不透明, 其默认为透明
    _eaglLayer.opaque = YES;
    
    // 设置绘制属性
    _eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
                                     [NSNumber numberWithBool:NO],
                                     kEAGLDrawablePropertyRetainedBacking,
                                     kEAGLColorFormatRGBA8,
                                     kEAGLDrawablePropertyColorFormat,
                                     nil];
}
  • 创建一个FrameBuffer, 并关联ColorBuffer
// 设置渲染buffer, RenderBuffer分为三大模块: colorBuffer, depthBuffer, stencilBuffer
- (void)setupRenderBuffer {

    // 创建1个buffer, 返回的id不为0, 0为系统保留
    glGenRenderbuffers(1, &_colorBuffer);
    
    // 绑定buffer为当前渲染缓冲对象
    glBindRenderbuffer(GL_RENDERBUFFER, _colorBuffer);
    
    // 为_colorBuffer分配存储空间
    [self.context renderbufferStorage:GL_RENDERBUFFER fromDrawable:_eaglLayer];
}

// 创建帧缓存对象(framebuffer object), FBO
- (void)setupFrameBuffer {
    
    // 创建framebuffer
    glGenFramebuffers(1, &_frameBuffer); 
    glBindFramebuffer(GL_FRAMEBUFFER, _frameBuffer);
    
    // 将_colorBuffer到装配到FBO的GL_COLOR_ATTACHMENT0装配点上
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _colorBuffer);
    
}
  • 渲染

    • 将当前context指定的colorBuffer渲染到屏幕
- (void)renderTest {

    // 设置清屏颜色R, G, B, A
    glClearColor(0.0, 1.0, 0.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);
    
    // 将context中指定的buffer渲染到屏幕上
    [self.context presentRenderbuffer:GL_RENDERBUFFER];
}
  • 运行结果
运行结果

目前OpnGL状况

WWDC 18已经把OpnGL 相关API标记为弃用; 说不定什么时候就被移除掉了, 苹果正大力推荐自己的Metal;

前段时间, 看过Metal相关知识, 发现有OpenGL经验能较好上手, 而且无论是Directx11还是OpenGL, Metal图像渲染流程, 数学运用手段都是一样的;

为甚么还要学习OpenGL?
Swift一般, OpenGL API什么时候才会真正被弃用?
市面上OpenGL学习书籍以及论坛相比于Metal更丰富, 完善以及面向新手.

对于红宝书OpenGL编程指南, 蓝宝书OpenGL超级宝典;
蓝宝书虽然事无巨细,但更面向新手;
而红宝书默认你熟悉整个渲染管线流程;


后续会陆续发布其他文章, 如果有错误, 还请指正;

你可能感兴趣的:(OpnGL的初识)