大家好敌呈,歡迎來到聽風(fēng)的OpenGL日常刺洒。
寫在前面
上篇主要講OpenGLES FrameBuffer的使用捍岳;理解本篇主要理解下面這幅圖前鹅,所以文中會(huì)再出現(xiàn)一次:
快速添加到View
我們要自己渲染頁(yè)面毒租,建一個(gè)單視圖的App赘被,接下來創(chuàng)建一個(gè)TFGLView:UIView
悴侵,令該View
指針指向視圖控制的view
瞧剖;
@interface ViewController ()
@property (nonatomic, strong) TFGLView* mView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.mView = (TFGLView*)self.view;
}
@end
添加OpenGLES上下文
OpenGLES在iOS端有自己的實(shí)現(xiàn),直接在Build Phases
里添加:
在TFGLView
里引用,這里我們使用ES2.0版本:
#import <OpenGLES/ES2/gl.h>
OpenGLES繪制在View的特殊圖層CAEAGLLayer:CALayer
上抓于,由于它是CALayer的子類做粤,所以直接更改View
的layerClass
:
+(Class)layerClass
{
return [CAEAGLLayer class];
}
同修改VC的view類似,我們也要對(duì)layer作修改捉撮,設(shè)置全局layer屬性:
@property(nonatomic,strong)CAEAGLLayer *myEagLayer;
另外我們需要獲取上下文來保存OpenGL的狀態(tài):
@property(nonatomic,strong)EAGLContext *myContext;
接下來在layoutSubviews
方法中我們?cè)O(shè)置圖層以及上下文怕品;
- 圖層
-(void)setupLayer
{
self.myEagLayer = (CAEAGLLayer *)self.layer;
/*
kEAGLDrawablePropertyRetainedBacking 表示繪圖表面顯示后,是否保留其內(nèi)容
kEAGLDrawablePropertyColorFormat 可繪制表面的內(nèi)部顏色緩存區(qū)格式.
這個(gè)key對(duì)應(yīng)的值是一個(gè)NSString指定特定顏色緩存區(qū)對(duì)象,默認(rèn)是kEAGLColorFormatRGBA8
*/
self.myEagLayer.drawableProperties = @{
kEAGLDrawablePropertyRetainedBacking:@false,
kEAGLDrawablePropertyColorFormat:kEAGLColorFormatRGBA8
};
}
這里注意下layer的drawableProperties
屬性巾遭,之后具體用到了再修改肉康;
- 上下文
-(void)setupContext
{
//1.指定OpenGL ES 渲染API版本,我們使用2.0
EAGLRenderingAPI api = kEAGLRenderingAPIOpenGLES2;
//2.創(chuàng)建圖形上下文
EAGLContext *context = [[EAGLContext alloc]initWithAPI:api];
//3.判斷是否創(chuàng)建成功
if (!context) {
NSLog(@"Create context failed!");
return;
}
//4.設(shè)置圖形上下文
if (![EAGLContext setCurrentContext:context]) {
NSLog(@"setCurrentContext failed!");
return;
}
//5.將局部context灼舍,變成全局的
self.myContext = context;
}
進(jìn)入GL渲染部分(本文主要部分)
完成了以上環(huán)境設(shè)置吼和,就可以進(jìn)入正式OpenGLES環(huán)節(jié),在此之前我們要先認(rèn)識(shí)兩個(gè)緩沖區(qū)骑素;
兩個(gè)緩沖區(qū)
這篇文章我們要用這兩個(gè)緩沖區(qū)來完成操作炫乓,先來了解一下這兩個(gè)緩沖區(qū):
在應(yīng)用程序調(diào)用OpenGLES之前,都要?jiǎng)?chuàng)建一個(gè)上下文和一個(gè)繪圖表面献丑,而在EAGL末捣、GLFW、GLAD等中都默認(rèn)提供了一個(gè)上下文和繪圖表面(即幀緩沖區(qū))阳距;
frame buffer object
Frame Buffer Object(FBO)擴(kuò)展塔粒,被推薦用于把數(shù)據(jù)渲染到紋理對(duì)象,即綁定到紋理對(duì)象筐摘;用于寫入顏色值的顏色緩沖卒茬、用于寫入深度信息的深度緩沖和允許我們根據(jù)一些條件丟棄特定片段的模板緩沖。這些緩沖結(jié)合起來叫做幀緩沖(Framebuffer)咖熟,它被儲(chǔ)存在內(nèi)存中圃酵。OpenGL允許我們定義我們自己的幀緩沖,也就是說我們能夠定義我們自己的顏色緩沖馍管,甚至是深度緩沖和模板緩沖郭赐。
對(duì)于這個(gè)對(duì)象的理解,我們現(xiàn)在可能并不能很好的感知确沸,具體到實(shí)際場(chǎng)景會(huì)更好理解捌锭,之后遇到就更清楚了,今天我們用它來渲染紋理罗捎。
可參考:OpenGL FrameBuffer Object
framebuffer是幀緩沖對(duì)象观谦,它是一個(gè)容器,但是自身不能用于渲染桨菜,需要與一些可渲染的緩沖區(qū)綁定在一起豁状,像紋理或者渲染緩沖區(qū)捉偏。
在OpenGL渲染管線中,幾何數(shù)據(jù)和紋理經(jīng)過一系列的測(cè)試最終以2d像素圖渲染到屏幕上泻红,最終的渲染目的地就叫做幀緩沖區(qū)夭禽。它是由一些OpenGL操作的2d數(shù)組和存儲(chǔ)空間組成。這個(gè)緩沖區(qū)完全的由窗口系統(tǒng)創(chuàng)建和管理谊路,這個(gè)默認(rèn)的緩沖區(qū)就叫做“窗口系統(tǒng)提供的”緩沖區(qū)讹躯。render buffer
可參考:RenderBuffer
RenderBuffer為顏色、深度和模板緩沖值提供了附著點(diǎn)凶异,而RenderBuffer又被附加到幀緩沖蜀撑。這樣幀緩沖才具有了操作深度信息能力。(為了記憶可以理解為為幀緩沖打開了對(duì)應(yīng)的測(cè)試)
上面兩個(gè)緩沖區(qū)及緩沖區(qū)對(duì)象的關(guān)系大致如下圖:
清空緩沖區(qū)
我們需要添加兩個(gè)標(biāo)識(shí)用來獲取RenderBuffer和FrameBuffer:
@property(nonatomic,assign)GLuint colorRenderBuffer;
@property(nonatomic,assign)GLuint colorFrameBuffer;
清空緩沖區(qū)操作:
- (void)clearBuffers
{
glDeleteBuffers(1, &_colorFrameBuffer);
self.colorFrameBuffer = 0;
glDeleteBuffers(1, &_colorRenderBuffer);
self.colorRenderBuffer = 0;
}
設(shè)置RenderBuffer
生成渲染緩沖區(qū)剩彬,并保存在全局變量中:
GLuint buffer;
glGenRenderbuffers(1, &buffer);
self.colorRenderBuffer = buffer;
將標(biāo)識(shí)符綁定到渲染緩存:
glBindRenderbuffer(GL_RENDERBUFFER, buffer);
將渲染緩存綁定到OpenGLES上:
[self.myContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:self.myEagLayer];
而對(duì)于不使用EAGL的情況酷麦,則需要:
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 800, 600);
上面這段代碼創(chuàng)建了一個(gè)800*600的模板緩沖對(duì)象用于將模板緩沖綁定在RenderBuffer上;
設(shè)置FrameBuffer
同樣喉恋,設(shè)置幀緩存對(duì)象沃饶,并綁定到幀緩存:
GLuint buffer;
glGenFramebuffers(1, &buffer);
self.colorFrameBuffer = buffer;
glBindFramebuffer(GL_FRAMEBUFFER, buffer);
接下來就是建立RenderBuffer與FrameBuffer的聯(lián)系,將RBO附著在FBO的GL_COLOR_ATTACHMENT0
上:
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, buffer);
設(shè)置著色器程序
本篇直接給出著色器程序轻黑,有關(guān)于著色器的用法見04-Shader
糊肤;
頂點(diǎn)著色器shader.vsh
:
attribute vec4 position;
attribute vec2 textCoordinate;
varying lowp vec2 varyTextCoord;
void main()
{
varyTextCoord = textCoordinate;
gl_Position = position;
}
片元著色器shader.fsh
:
precision highp float;
varying lowp vec2 varyTextCoord;
uniform sampler2D colorMap;
void main()
{
//lowp vec4 temp = texture2D(colorMap, varyTextCoord);
//gl_FragColor = temp;
gl_FragColor = texture2D(colorMap, varyTextCoord);
}
將生成寫成一個(gè)函數(shù):
- (GLuint)genShader:(NSString *)file :(GLenum)type
{
NSString* content = [NSString stringWithContentsOfFile:file encoding:NSUTF8StringEncoding error:nil];
const GLchar* source = (GLchar *)[content UTF8String];
GLuint shader = glCreateShader(type);
glShaderSource(shader, 1, &source, NULL);
glCompileShader(shader);
return shader;
}
生成并編譯著色器:
//頂點(diǎn)著色器
NSString *vertFile = [[NSBundle mainBundle]pathForResource:@"shaderv" ofType:@"vsh"];
GLuint vShader = [self genShader:vertFile :GL_VERTEX_SHADER];
//片元著色器
NSString *fragFile = [[NSBundle mainBundle]pathForResource:@"shaderf" ofType:@"fsh"];
GLuint fShader = [self genShader:fragFile :GL_FRAGMENT_SHADER];
生成著色器程序,并把編譯好的著色器附著在其上:
GLuint program = glCreateProgram();
glAttachShader(program, vShader);
glAttachShader(program, fShader);
//鏈接到程序后就可以刪除著色器了氓鄙,這是個(gè)好習(xí)慣
glDeleteShader(vShader);
glDeleteShader(fShader);
繪制
前面的準(zhǔn)備工作已經(jīng)完成馆揉,接下來開始正式開始繪制紋理;我們將在一個(gè)drawTexture
完成
- (void)drawTexture
{
//
}
渲染背景
//設(shè)置清屏顏色
glClearColor(0.3f, 0.45f, 0.5f, 1.0f);
//清除屏幕
glClear(GL_COLOR_BUFFER_BIT);
鏈接并檢查著色器程序
GLuint program = [self setupProgram];
glLinkProgram(program);
鏈接后需要對(duì)程序做檢查抖拦,如果有錯(cuò)誤升酣,則我們?nèi)菀锥ㄎ恢鞒鲥e(cuò)的細(xì)節(jié):
GLint linkStatus;
//獲取鏈接狀態(tài)
glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
if (linkStatus == GL_FALSE) {
GLchar message[512];
glGetProgramInfoLog(program, sizeof(message), 0, &message[0]);
NSString *messageString = [NSString stringWithUTF8String:message];
NSLog(@"Program Link Error:%@",messageString);
return;
}