在上篇文章GLSL初始著色器語言中已經(jīng)介紹過如何編寫一個著色器文件,以及如何連接程序?qū)ο蠛椭鞯膶ο蟮姆椒ê瘮?shù)余掖,那么接下來通過一個實際的案例來看一下這些方法的具體使用寸爆。
在學(xué)習案例之前,先來看一下什么是FrameBuffer和RenderBuffer盐欺?
幀緩沖區(qū)對象FrameBuffer(FBO)
在OpenGL渲染管線中赁豆,幾何數(shù)據(jù)和紋理經(jīng)過多次轉(zhuǎn)化和多次測試,最后以二維像素的形式顯示在屏幕上冗美。OpenGL管線的最終渲染目的地被稱作幀緩沖(framebuffer)魔种。幀緩沖是一些二維數(shù)組和OpenG所使用的存儲區(qū)的集合:顏色緩沖、深度緩沖粉洼、模板緩沖和累計緩沖节预。默認情況下叶摄,OpenGL將幀緩沖區(qū)作為渲染最終目的地。此幀緩沖區(qū)完全由系統(tǒng)生成和管理安拟。
我們知道蛤吓,在應(yīng)用程序調(diào)用任何的OpenGL ES命令之前,需要首先創(chuàng)建一個渲染上下文和繪圖表面糠赦,并使之成為現(xiàn)行上下文和表面会傲,之前在渲染的時候,其實一直使用的是原生窗口系統(tǒng)(比如EAGL拙泽,GLFW)提供的渲染上下文和繪圖表面(即幀緩沖區(qū))淌山。
一般情況下,我們只需要系統(tǒng)提供的幀緩沖區(qū)作為繪圖表面顾瞻,但是又有些特殊情況泼疑,比如陰影貼圖、動態(tài)反射朋其、處理后特效等需要渲染到紋理操作的王浴,如果使用系統(tǒng)提供的幀緩沖區(qū),效率會比較低低下梅猿,因此需要自定義自己的幀緩沖區(qū)。
幀緩沖區(qū)對象API支持如下操作:
·僅使用OpenGL ES 命令創(chuàng)建幀緩沖區(qū)對象
·在單一EGL上下文中創(chuàng)建和使用多個緩沖區(qū)對象秒裕,也就是說袱蚓,不需要每一個幀緩沖區(qū)都有一個渲染上下文。
·創(chuàng)建屏幕外顏色几蜻,深度或者模板渲染緩沖區(qū)和紋理喇潘,并將它們鏈接到幀緩沖區(qū)對象
·在多個幀緩沖區(qū)之間共享顏色,深度或者模板緩沖區(qū)
·將紋理直接鏈接到幀緩沖區(qū)作為顏色或者深度梭稚,從而避免了進行復(fù)制操作的必要
·在幀緩沖區(qū)之間復(fù)制并使幀緩沖區(qū)內(nèi)容失效颖低。
創(chuàng)建幀緩沖區(qū)對象
//定義一個緩存區(qū)ID
GLuint buffer;
//申請一個緩存區(qū)標志
glGenFramebuffers(1, &buffer);
// 然后綁定
glBindFramebuffer(GL_FRAMEBUFFER, buffer);
glGenFramebuffers (GLsizei n, GLuint* framebuffers) :
第一個參數(shù)是要創(chuàng)建的幀緩存的數(shù)目,
第二個參數(shù)是指向存儲一個或者多個ID的變量或數(shù)組的指針弧烤。
它返回未使用的幀緩沖區(qū)對象的ID忱屑。ID為0表示默認幀緩存,即系統(tǒng)提供的幀緩存暇昂。
一旦一個FBO被創(chuàng)建莺戒,在使用它之前必須綁定
glBindFramebuffer (GLenum target, GLuint framebuffer):
第一個參數(shù)target是GL_FRAMEBUFFER,
第二個參數(shù)是幀緩沖區(qū)對象的ID急波。
一旦幀緩沖區(qū)對象被綁定从铲,之后的所有的OpenGL操作都會對當前所綁定的幀緩沖區(qū)對象造成影響。ID為0表示缺省幀緩存澄暮,即默認的系統(tǒng)提供的幀緩存名段。因此阱扬,在glBindFramebuffer()中將ID設(shè)置為0可以解綁定當前幀緩沖區(qū)對象。
在綁定到GL_FRAMEBUFFER目標之后伸辟,所有的讀取和寫入幀緩沖的操作將會影響當前綁定的幀緩沖麻惶。我們也可以使用GL_READ_FRAMEBUFFER或GL_DRAW_FRAMEBUFFER,將一個幀緩沖分別綁定到讀取目標或?qū)懭肽繕俗悦洹=壎ǖ紾L_READ_FRAMEBUFFER的幀緩沖將會使用在所有像是glReadPixels的讀取操作中用踩,而綁定到GL_DRAW_FRAMEBUFFER的幀緩沖將會被用作渲染、清除等寫入操作的目標忙迁。大部分情況都不需要區(qū)分它們脐彩,通常都會使用GL_FRAMEBUFFER。
刪除緩沖區(qū)對象
在緩沖區(qū)對象不再被使用時姊扔,緩沖區(qū)對象可以通過調(diào)用glDeleteFramebuffers (GLsizei n, const GLuint* framebuffers)來刪除惠奸。
glDeleteFramebuffers(1, &buffer);
和系統(tǒng)的幀緩沖區(qū)一樣,幀緩沖區(qū)對象也包括顏色緩沖區(qū)恰梢、深度和模版緩沖區(qū)佛南,這些邏輯上的緩沖區(qū)在幀緩沖區(qū)對象中稱之為可附加的圖像,它們是可以附加到幀緩沖區(qū)對象的二維像素數(shù)組嵌言。
FBO包含兩種類型的附加圖像:紋理圖像(texture images)和渲染緩存圖像(renderbuffer images)嗅回。如果紋理對象的圖像數(shù)據(jù)關(guān)聯(lián)到幀緩存,OpenGL執(zhí)行的是“渲染到紋理”(render to texture)操作摧茴。如果渲染緩存的圖像數(shù)據(jù)關(guān)聯(lián)到幀緩存绵载,OpenGL執(zhí)行的是離屏渲染(offscreen rendering)。
渲染緩存區(qū)對象RenderBuffer(RBO)
渲染緩存是為離線渲染而新引進的苛白。它允許將一個場景直接渲染到一個渲染緩存對象中娃豹,而不是渲染到紋理對象中。渲染緩存對象是用于存儲單幅圖像的數(shù)據(jù)存儲區(qū)域购裙。該圖像按照一種可渲染的內(nèi)部格式存儲懂版。它用于存儲沒有相關(guān)紋理格式的OpenGL邏輯緩存,比如模板緩存或者深度緩存躏率。
一個renderbuffer對象是通過應(yīng)用程序分配的一個2D圖像緩沖區(qū)躯畴,renderbuffer可以用于分配和存儲顏色,深度或者模板值禾锤,也可以作為一個framebuffer的顏色私股,深度,模板的附著恩掷,一個renderbuffer是一個類似于屏幕外的窗口系統(tǒng)提供的可繪制表面倡鲸。但是renderbuffer不能直接用作GL紋理。
創(chuàng)建渲染緩沖區(qū)
//1.定義一個緩存區(qū)ID
GLuint buffer;
//2.申請一個緩存區(qū)標志
glGenRenderbuffers(1, &buffer);
//3.將標識符綁定到GL_RENDERBUFFER
glBindRenderbuffer(GL_RENDERBUFFER, buffer);
和幀緩沖區(qū)對象一樣黄娘,在引用渲染緩沖區(qū)對象之前必須綁定當前渲染緩沖對象峭状,調(diào)用函數(shù)glBindRenderbuffer (GLenum target, GLuint renderbuffer)進行綁定克滴,
第一個參數(shù)target是GL_RENDERBUFFER,
第二個參數(shù)是渲染緩沖區(qū)對象的ID优床。
刪除緩沖區(qū)對象
在緩沖區(qū)對象不再被使用時劝赔,緩沖區(qū)對象可以通過調(diào)用glDeleteRenderbuffers (GLsizei n, const GLuint* renderbuffers)來刪除。
glDeleteRenderbuffers (1, &buffer);
當一個渲染緩存被創(chuàng)建胆敞,它沒有任何數(shù)據(jù)存儲區(qū)域着帽,所以還要為它分配空間,這可以通過用glRenderbufferStorage (GLenum target, GLenum internalformat, GLsizei width, GLsizei height)實現(xiàn)移层。
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 600, 800);
第一個參數(shù)必須是GL_RENDERBUFFER,
第二個參數(shù)是可用于顏色仍翰,深度,模板的格式观话,
width和height是渲染緩存圖像的像素維度
附加渲染緩沖對象
最后予借,生成幀緩沖區(qū)之后,則需要將renderbuffer跟framebuffer進行綁定频蛔,調(diào)用glFramebufferRenderbuffer函數(shù)進行綁定到對應(yīng)的附著點上灵迫,后面的繪制才能起作用
//將渲染緩存區(qū)myColorRenderBuffer 通過glFramebufferRenderbuffer函數(shù)綁定到 GL_COLOR_ATTACHMENT0上。
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, self.myColorRenderBuffer);
下圖展示了幀緩沖區(qū)對象晦溪,渲染緩沖區(qū)對象和紋理之間的關(guān)系瀑粥,一個幀緩沖區(qū)對象中只能有一個顏色,深度三圆,模板附著利凑。簡單來說就是framebuffer只是一個管理者,其本身并沒有任何存儲區(qū)(沒有存儲紋理嫌术,頂點,顏色等數(shù)據(jù))牌借,只是有顏色度气,深度,模板附著點膨报,而真正存儲這些數(shù)據(jù)的是renderbuffer磷籍。
GLSL渲染圖片
這個案例大概實現(xiàn)以下這些內(nèi)容:
·用EAGL 創(chuàng)建屏幕上的渲染表面
·加載頂點/片元著?器
·創(chuàng)建一個程序?qū)ο?并鏈接頂點/?元著?器,并鏈接程序?qū)ο?br>
·設(shè)置視口
·清除顏色緩存區(qū)
·渲染簡單圖元
·使顏?緩存區(qū)的內(nèi)容在EAGL 窗?表現(xiàn)呈現(xiàn)
1.創(chuàng)建頂點/片元著色器文件
著色器文件一般以.vsh/.fsh/.gsl為文件后綴名
頂點著色器shaderv.vsh
// 頂點坐標
attribute highp vec4 position;
// 紋理坐標
attribute highp vec2 textCoordinate;
// 紋理坐標
varying lowp vec2 varyTextCoord;
void main() {
varyTextCoord = textCoordinate;
gl_Position = position;
}
在著色器文件中最好不要加中文注釋,以防編譯無法通過现柠,此處的中文注釋只作為理解注釋院领。
片元著色器shaderf.fsh
// 紋理坐標
varying lowp vec2 varyTextCoord;
// 紋理采樣器(獲取對應(yīng)的紋理ID)
uniform sampler2D colorMap;
void main() {
gl_FragColor = texture2D(colorMap, varyTextCoord);
}
創(chuàng)建一個UIView,并導(dǎo)入頭文件#import <OpenGLES/ES2/gl.h>够吩,此次用GLSL渲染圖片的代碼全部書寫在這個UIView中比然。
//在iOS和tvOS上繪制OpenGL ES內(nèi)容的圖層,繼承與CALayer
@property(nonatomic,strong)CAEAGLLayer *zhEagLayer;
@property(nonatomic,strong)EAGLContext *zhContext;
@property(nonatomic,assign)GLuint zhColorRenderBuffer;
@property(nonatomic,assign)GLuint zhColorFrameBuffer;
@property(nonatomic,assign)GLuint zhPrograme;
2.設(shè)置圖層setupLayer
//1.創(chuàng)建特殊圖層
//這里需要重寫layerClass周循,將ZHView返回的圖層從CALayer替換成CAEAGLLayer
self.zhEagLayer = (CAEAGLLayer *)self.layer;
//2.設(shè)置scale
[self setContentScaleFactor:[[UIScreen mainScreen]scale]];
//3.設(shè)置描述屬性强法,這里設(shè)置不維持渲染內(nèi)容以及顏色格式為RGBA8
self.zhEagLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:@false,kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8,kEAGLDrawablePropertyColorFormat,nil];
對于drawableProperties設(shè)置描述屬性万俗,
kEAGLDrawablePropertyRetainedBacking:表示繪圖表面顯示后,是否保留其內(nèi)容
kEAGLDrawablePropertyColorFormat:表示可繪制表面的內(nèi)部顏色緩存區(qū)格式饮怯,這個key對應(yīng)的值是一個NSString指定特定顏色緩存區(qū)對象闰歪。默認是kEAGLColorFormatRGBA8;
· kEAGLColorFormatRGBA8:32位RGBA的顏色
· kEAGLColorFormatRGB565:16位RGB的顏色
· kEAGLColorFormatSRGBA8:sRGB代表了標準的紅蓖墅、綠库倘、藍,即CRT顯示器论矾、LCD顯示器教翩、投影機、打印機以及其他設(shè)備中色彩再現(xiàn)所使用的三個基本色素拇囊。sRGB的色彩空間基于獨立的色彩坐標迂曲,可以使色彩在不同的設(shè)備使用傳輸中對應(yīng)于同一個色彩坐標體系,而不受這些設(shè)備各自具有的不同色彩坐標的影響寥袭。
重寫layerClass
+(Class)layerClass
{
return [CAEAGLLayer class];
}
3.設(shè)置渲染上下文setupContext
//1.指定OpenGL ES 渲染API版本
EAGLRenderingAPI api = kEAGLRenderingAPIOpenGLES2;
//2.創(chuàng)建圖形上下文
EAGLContext *context = [[EAGLContext alloc]initWithAPI:api];
//3.判斷是否創(chuàng)建成功
if (!context) {
NSLog(@"Create failed!");
return;
}
//4.設(shè)置圖形上下文
if (![EAGLContext setCurrentContext:context]) {
NSLog(@"Set failed!");
return;
}
//5.將局部context賦值成全局的context
self.zhContext = context;
4.清空緩沖區(qū)deleteRenderAndFrameBuffer
//清空幀緩沖區(qū)
glDeleteBuffers(1, &_zhColorFrameBuffer);
self.zhColorFrameBuffer = 0;
//清空渲染緩沖區(qū)
glDeleteBuffers(1, &_zhColorRenderBuffer);
self.zhColorRenderBuffer = 0;
清空緩沖區(qū)的代碼也可以寫成:
glDeleteFramebuffers(1, &_zhColorFrameBuffer);
self.zhColorFrameBuffer = 0;
glDeleteRenderbuffers(1, &_zhColorRenderBuffer);
self.zhColorRenderBuffer = 0;
5.設(shè)置RenderBuffer
//1.定義一個緩存區(qū)ID
GLuint buffer;
//2.申請一個緩存區(qū)標志
glGenRenderbuffers(1, &buffer);
//3.將當前申請的buffer變成全局的
self.zhColorRenderBuffer = buffer;
//4.將標識符綁定到GL_RENDERBUFFER
glBindRenderbuffer(GL_RENDERBUFFER, self.zhColorRenderBuffer);
//5.將可繪制對象drawable object's CAEAGLLayer的存儲綁定到OpenGL ES renderBuffer對象
[self.zhContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:self.zhEagLayer];
6.設(shè)置FrameBuffer
//1.定義一個緩存區(qū)ID
GLuint buffer;
//2.申請一個緩存區(qū)標志
glGenRenderbuffers(1, &buffer);
//3.將buffer變成全局的
self.zhColorFrameBuffer = buffer;
//4.綁定標識符到GL_FRAMEBUFFER
glBindFramebuffer(GL_FRAMEBUFFER, self.zhColorFrameBuffer);
/*生成幀緩存區(qū)之后路捧,則需要將renderbuffer跟framebuffer進行綁定,
調(diào)用glFramebufferRenderbuffer函數(shù)進行綁定到對應(yīng)的附著點上传黄,后面的繪制才能起作用*/
//5.將渲染緩存區(qū)myColorRenderBuffer 通過glFramebufferRenderbuffer函數(shù)綁定到 GL_COLOR_ATTACHMENT0上杰扫。
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, self.zhColorRenderBuffer);
7.開始繪制renderLayer
1.設(shè)置清屏顏色
glClearColor(0.3f, 0.45f, 0.5f, 1.0f);
2.清除屏幕
glClear(GL_COLOR_BUFFER_BIT);
3.設(shè)置視口大小
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);
4.讀取頂點著色程序、片元著色程序
NSString *vertFile = [[NSBundle mainBundle]pathForResource:@"shaderv" ofType:@"vsh"];
NSString *fragFile = [[NSBundle mainBundle]pathForResource:@"shaderf" ofType:@"fsh"];
5.加載著色器shader
self.zhPrograme = [self loadShaders:vertFile Withfrag:fragFile];
//加載shader
-(GLuint)loadShaders:(NSString *)vert Withfrag:(NSString *)frag
{
//1.定義2個臨時的著色器對象
GLuint verShader, fragShader;
//創(chuàng)建program
GLint program = glCreateProgram();
//2.編譯頂點著色程序和片元著色器程序
[self compileShader:&verShader type:GL_VERTEX_SHADER file:vert];
[self compileShader:&fragShader type:GL_FRAGMENT_SHADER file:frag];
/*
關(guān)于這個compileShader:type:file:方法傳入的三個參數(shù):
參數(shù)1:編譯完存儲的底層地址
參數(shù)2:編譯的著色器的類型膘掰,GL_VERTEX_SHADER(頂點)章姓、GL_FRAGMENT_SHADER(片元)
參數(shù)3:文件路徑
*/
//3.鏈接著色器對象和程序?qū)ο? glAttachShader(program, verShader);
glAttachShader(program, fragShader);
//4.釋放不需要的shader
glDeleteShader(verShader);
glDeleteShader(fragShader);
return program;
}
//編譯shader
- (void)compileShader:(GLuint *)shader type:(GLenum)type file:(NSString *)file{
//1.讀取文件路徑字符串
NSString* content = [NSString stringWithContentsOfFile:file encoding:NSUTF8StringEncoding error:nil];
const GLchar* source = (GLchar *)[content UTF8String];
//2.根據(jù)type類型創(chuàng)建一個shader
*shader = glCreateShader(type);
//3.將著色器源碼附加到著色器對象上。
glShaderSource(*shader, 1, &source,NULL);
/*
參數(shù)1:shader,要編譯的著色器對象 *shader
參數(shù)2:numOfStrings,傳遞的源碼字符串數(shù)量 1個
參數(shù)3:strings,著色器程序的源碼(真正的著色器程序源碼)
參數(shù)4:lenOfStrings,長度识埋,具有每個字符串長度的數(shù)組凡伊,或NULL,這意味著字符串是NULL終止的
*/
//4.把著色器源代碼編譯成目標代碼
glCompileShader(*shader);
}
6.鏈接程序?qū)ο?/strong>
glLinkProgram(self.zhPrograme);
//檢查鏈接是否成功
GLint linkStatus;
//獲取鏈接狀態(tài)
glGetProgramiv(self.myPrograme, GL_LINK_STATUS, &linkStatus);
if (linkStatus == GL_FALSE) {
GLchar message[512];
glGetProgramInfoLog(self.zhPrograme, sizeof(message), 0, &message[0]);
NSString *messageString = [NSString stringWithUTF8String:message];
NSLog(@"program link error:%@",messageString);
return;
}
NSLog(@"program link success!");
7.使用程序?qū)ο?/strong>
glUseProgram(self.zhPrograme);
8.設(shè)置頂點坐標和紋理坐標
//坐標數(shù)組
GLfloat attrArr[] =
{
1.0f, -1.0f, -1.0f, 1.0f, 0.0f,
-1.0f, 1.0f, -1.0f, 0.0f, 1.0f,
-1.0f, -1.0f, -1.0f, 0.0f, 0.0f,
1.0f, 1.0f, -1.0f, 1.0f, 1.0f,
-1.0f, 1.0f, -1.0f, 0.0f, 1.0f,
1.0f, -1.0f, -1.0f, 1.0f, 0.0f,
};
//每一行的前3位為頂點坐標窒舟,后兩位為紋理坐標
9.處理頂點數(shù)據(jù)
//(1)頂點緩存區(qū)
GLuint attrBuffer;
//(2)申請一個緩存區(qū)標識符
glGenBuffers(1, &attrBuffer);
//(3)將attrBuffer綁定到GL_ARRAY_BUFFER標識符上
glBindBuffer(GL_ARRAY_BUFFER, attrBuffer);
//(4)把頂點數(shù)據(jù)從CPU內(nèi)存復(fù)制到GPU上
glBufferData(GL_ARRAY_BUFFER, sizeof(attrArr), attrArr, GL_DYNAMIC_DRAW);
10.將頂點數(shù)據(jù)傳入到頂點著色器對象
將頂點數(shù)據(jù)通過self.zhPrograme中的傳遞到頂點著色器程序的position系忙,通過以下三個函數(shù)處理頂點數(shù)據(jù)
·glGetAttribLocation,用來獲取vertex attribute的入口。
·告訴OpenGL ES,通過glEnableVertexAttribArray從buffer讀取數(shù)據(jù)(打開通道)
·glVertexAttribPointer設(shè)置讀取數(shù)據(jù)的方式
//第二參數(shù)字符串必須和shaderv.vsh中的輸入變量:position保持一致
GLuint position = glGetAttribLocation(self.zhPrograme, "position");
//設(shè)置合適的格式從buffer里面讀取數(shù)據(jù)
glEnableVertexAttribArray(position);
//設(shè)置讀取方式
glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, NULL);
glVertexAttribPointer (GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* ptr)
參數(shù)1:index,頂點數(shù)據(jù)的索引
參數(shù)2:size,每個頂點屬性的組件數(shù)量惠豺,1银还,2,3洁墙,或者4.默認初始值是4.
參數(shù)3:type,數(shù)據(jù)中的每個組件的類型蛹疯,常用的有GL_FLOAT,GL_BYTE,GL_SHORT。默認初始值為GL_FLOAT
參數(shù)4:normalized,固定點數(shù)據(jù)值是否應(yīng)該歸一化热监,或者直接轉(zhuǎn)換為固定值捺弦。(GL_FALSE)
參數(shù)5:stride,連續(xù)頂點屬性之間的偏移量,默認為0;
參數(shù)6:指定一個指針羹呵,指向數(shù)組中的第一個頂點屬性的第一個組件骂际。默認為0
11.處理紋理數(shù)據(jù)
//第二參數(shù)字符串必須和shaderv.vsh中的輸入變量:textCoordinate保持一致
GLuint textCoor = glGetAttribLocation(self.zhPrograme, "textCoordinate");
//設(shè)置合適的格式從buffer里面讀取數(shù)據(jù)
glEnableVertexAttribArray(textCoor);
//設(shè)置讀取方式
glVertexAttribPointer(textCoor, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*5, (float *)NULL + 3);
12.加載紋理
//1、將 UIImage 轉(zhuǎn)換為 CGImageRef
CGImageRef spriteImage = [UIImage imageNamed:fileName].CGImage;
//判斷圖片是否獲取成功
if (!spriteImage) {
NSLog(@"load image failed: %@", fileName);
return;
}
//2冈欢、讀取圖片的大小歉铝,寬和高
size_t width = CGImageGetWidth(spriteImage);
size_t height = CGImageGetHeight(spriteImage);
//3.獲取圖片字節(jié)數(shù) 寬*高*4(RGBA)
GLubyte * spriteData = (GLubyte *) calloc(width * height * 4, sizeof(GLubyte));
//4.創(chuàng)建上下文
/*
參數(shù)1:data,指向要渲染的繪制圖像的內(nèi)存地址
參數(shù)2:width,bitmap的寬度,單位為像素
參數(shù)3:height,bitmap的高度凑耻,單位為像素
參數(shù)4:bitPerComponent,內(nèi)存中像素的每個組件的位數(shù)太示,比如32位RGBA,就設(shè)置為8
參數(shù)5:bytesPerRow,bitmap的沒一行的內(nèi)存所占的比特數(shù)
參數(shù)6:colorSpace,bitmap上使用的顏色空間 kCGImageAlphaPremultipliedLast:RGBA
*/
CGContextRef spriteContext = CGBitmapContextCreate(spriteData, width, height, 8, width*4,CGImageGetColorSpace(spriteImage), kCGImageAlphaPremultipliedLast);
//5香浩、在CGContextRef上--> 將圖片繪制出來
/*
CGContextDrawImage 使用的是Core Graphics框架类缤,坐標系與UIKit 不一樣。UIKit框架的原點在屏幕的左上角邻吭,Core Graphics框架的原點在屏幕的左下角餐弱。
CGContextDrawImage
參數(shù)1:繪圖上下文
參數(shù)2:rect坐標
參數(shù)3:繪制的圖片
*/
CGRect rect = CGRectMake(0, 0, width, height);
//6.使用默認方式繪制
CGContextTranslateCTM(spriteContext, 0, rect.size.height);
CGContextScaleCTM(spriteContext, 1.0, -1.0);
CGContextDrawImage(spriteContext, rect, spriteImage);
//7、畫圖完畢就釋放上下文
CGContextRelease(spriteContext);
//8囱晴、綁定紋理到默認的紋理ID(
glBindTexture(GL_TEXTURE_2D, 0);
//9.設(shè)置紋理屬性
/*
參數(shù)1:紋理維度
參數(shù)2:線性過濾膏蚓、為s,t坐標設(shè)置模式
參數(shù)3:wrapMode,環(huán)繞模式
*/
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;
//10.載入紋理2D數(shù)據(jù)
/*
參數(shù)1:紋理模式(綁定紋理對象的種類),GL_TEXTURE_1D畸写、GL_TEXTURE_2D驮瞧、GL_TEXTURE_3D
參數(shù)2:加載的層次,一般設(shè)置為0, 0表示沒有進行縮小的原始圖片等級枯芬。
參數(shù)3:紋理的顏色值GL_RGBA, 表示了紋理所采用的內(nèi)部格式论笔,內(nèi)部格式是我們的像素數(shù)據(jù)在顯卡中存儲的格式,這里的GL_RGB顯然就表示紋理中像素的顏色值是以RGB的格式存儲的千所。
參數(shù)4:紋理的寬
參數(shù)5:紋理的高
參數(shù)6:border狂魔,邊界寬度,通常為0.
參數(shù)7:format(描述了像素在內(nèi)存中的存儲格式)
參數(shù)8:type(描述了像素在內(nèi)存中的數(shù)據(jù)類型)
參數(shù)9:紋理數(shù)據(jù)
*/
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fw, fh, 0, GL_RGBA, GL_UNSIGNED_BYTE, spriteData);
//11.釋放spriteData
free(spriteData);
13.設(shè)置紋理采樣器 sampler2D
glUniform1i(glGetUniformLocation(self.zhPrograme, "colorMap"), 0);
14.繪圖
glDrawArrays(GL_TRIANGLES, 0, 6);
15.從渲染緩沖區(qū)顯示到屏幕上
[self.zhContext presentRenderbuffer:GL_RENDERBUFFER];
至此用GLSL渲染圖片的代碼已經(jīng)基本完成淫痰,如果沒有處理圖片的翻轉(zhuǎn)問題的話毅臊,運行的效果顯示出來圖片是翻轉(zhuǎn)的,至于圖片翻轉(zhuǎn)的原因和解決圖片翻轉(zhuǎn)的方法已在上一篇文章中做了詳細的介紹,這里就不再一一贅述。
下面將上述的代碼步驟做一個梳理袁翁,如下圖所示:文中部分內(nèi)容參考:《OpenGL ES 3.0 編程指南》