前言
開發(fā)OpenGL App
一年多了, 平時就是在各種趕項目, 趕功能; 做了不少筆記, 踩了不少坑, 但是僅僅是工作需要就去學習哪塊, 很少靜下來做總結;
今年年春換了一家東家, 開發(fā)直播類App
, 需要視頻處理等知識, 正好總結一番;
目前發(fā)現,在OpenGL
的世界里 相對于渲染管線
來說, 數學
才是真正的大頭;
相比于復雜的理論知識, 我們直接手上吧.
-
本文環(huán)境
Xcode 9.0
OpenGLES 3.0
基本操作步驟
導入
GLKit
框架直接集成
GLKViewController
, 或者GLKView
創(chuàng)建
OpenGL上下文
EAGLContext
-
創(chuàng)建程序對象
ShaderProgram
- 連接
頂點著色器
和片段著色器
- 連接
指定清屏顏色,填充到
ColorBuffer
導入框架, 直接集成與GLKViewController
#import <GLKit/GLKit.h>
@interface ViewController : GLKViewController
@end
創(chuàng)建OpenGL 上下文
-
EAGLContext
調度中樞
保存繪制命令, 狀態(tài), 資源所有調度;
因為
OpenGL 是狀態(tài)機機制
, 故需要一個調度中樞的存在這里直接使用
OpenGLES 3.0
#pragma mark - setupContext
- (void)setupContext {
// 1.0 創(chuàng)建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;
創(chuàng)建程序對象Program
-
創(chuàng)建
頂點著色器, 片段著色器
著色器最終以字符串的形式傳遞到
program
下面是最簡單的兩個著色器, 有影響即可, 暫無需深入
- 頂點著色器
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;
}
-
創(chuàng)建
program
固定套路創(chuàng)建
program
加載著色器, 并關聯到
program
鏈接
porgram
釋放
shader
資源
GLuint program, vertShader, fragShader;
// 創(chuàng)建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`一致
-
此時發(fā)現代理方法只會執(zhí)行一次
- 需創(chuàng)建定時器, 重復調用代理方法
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];
}
一層層往下扒的話, 發(fā)現真正用來渲染的是CAEAGLLayer
;
GLKView
對CAEAGLLayer做了一層封裝
;
GLKViewController
對GLKView
做了一層封裝, 設置自身為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];
}
- 創(chuàng)建一個
FrameBuffer
, 并關聯ColorBuffer
// 設置渲染buffer, RenderBuffer分為三大模塊: colorBuffer, depthBuffer, stencilBuffer
- (void)setupRenderBuffer {
// 創(chuàng)建1個buffer, 返回的id不為0, 0為系統保留
glGenRenderbuffers(1, &_colorBuffer);
// 綁定buffer為當前渲染緩沖對象
glBindRenderbuffer(GL_RENDERBUFFER, _colorBuffer);
// 為_colorBuffer分配存儲空間
[self.context renderbufferStorage:GL_RENDERBUFFER fromDrawable:_eaglLayer];
}
// 創(chuàng)建幀緩存對象(framebuffer object), FBO
- (void)setupFrameBuffer {
// 創(chuàng)建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
相關知識, 發(fā)現有OpenGL經驗
能較好上手, 而且無論是Directx11
還是OpenGL
, Metal
圖像渲染流程, 數學運用手段都是一樣的;
為甚么還要學習OpenGL
?
如Swift
一般, OpenGL API
什么時候才會真正被棄用?
市面上OpenGL
學習書籍以及論壇相比于Metal
更豐富, 完善以及面向新手.
對于紅寶書OpenGL編程指南
, 藍寶書OpenGL超級寶典
;
藍寶書雖然事無巨細
,但更面向新手;
而紅寶書默認你熟悉整個渲染管線流程
;
后續(xù)會陸續(xù)發(fā)布其他文章, 如果有錯誤, 還請指正;