利用GLSL自定義的著色去加載一張圖片窿侈,效果圖如下
整體流程圖如下
流程中主要分為4個模塊
- 準備工作:項目的創(chuàng)建及自定義視圖瓶蚂、屬性等
- 自定義著色器:利用GLSL編寫自定義的頂點、片元著色器
- 初始化:創(chuàng)建layer御雕、context顿乒,清空緩存區(qū),以及設置Render和Frame緩存區(qū)
- 繪制:主要是GLSL加載冤留、頂點數(shù)據(jù)處理以及加載紋理,最后繪制到屏幕上
準備工作
項目的創(chuàng)建及自定義視圖創(chuàng)建等树灶,這里不作過多說明纤怒,主要說說著色器文件是如何創(chuàng)建的
command + N
,開始新建文件-
選擇
ios --> Other --> Empty
天通,點擊next
Empty文件 -
輸入文件名稱泊窘,例如
shaderv.vsh
,點擊create
,即創(chuàng)建成功
shaderv.vsh
自定義著色器
自定義的著色器本質上其實是一個字符串,且在Xcode中編寫時烘豹,是沒有任何提示的瓜贾,所以需要格外仔細!
頂點著色器
- 定義兩個
attribute
修飾的變量携悯,分別表示頂點坐標position和紋理坐標textCoordinate
- 定義一個與片元橋接的變量
varyTextCoord
阐虚,用來將紋理坐標從頂點著色器傳遞到片元著色器 -
main
函數(shù):如果頂點沒有任何變換操作,則直接將頂點坐標賦值給內建變量gl_Position
蚌卤,如果頂點有變換实束,將變換后的結果 即最終的頂點坐標數(shù)據(jù),賦值給內建變量
//頂點坐標
attribute vec4 position;
紋理坐標
attribute vec2 textCoordinate;
//紋理坐標
varying lowp vec2 varyTextCoord;
void main(){
//通過varying 修飾的varyTextCoord,將紋理坐標傳遞到片元著色器
varyTextCoord = textCoordinate;
//給內江變量gl_Position賦值
gl_Position = position;
}
片與著色器
- 片元著色器中
float
類型的精度逊彭,如果不寫咸灿,可能會報一些異常的錯誤 - 定義一個與頂點著色器的橋接變量
varyTextCoord
,即紋理坐標侮叮,必須與頂點著色器中一模一樣避矢,如果不一致,紋理坐標數(shù)據(jù)將無法傳遞 - 定義一個
unifom
修飾的紋理采樣器colorMap
囊榜,用于獲取紋理坐標每個像素點的紋素 -
main
函數(shù):主要是紋理顏色的填充审胸,通過texture2D
內建函數(shù)獲取最終的顏色值,有兩個參數(shù)卸勺,參數(shù)1是紋理圖片砂沛,參數(shù)2是紋理坐標,且返回值是一個vec4
類型的顏色值曙求,并將該值結果賦值給內建變量
//指定float的默認精度
precision highp float;
//紋理坐標
varying lowp vec2 varyTextCoord;
//紋理采樣器(獲取對應的紋理ID)
uniform sampler2D colorMap;
void main(){
//texture2D(紋理采樣器碍庵,紋理坐標),獲取對應坐標紋素
//紋理坐標添加到對應像素點上悟狱,即將讀取的紋素賦值給內建變量 gl_FragColor
gl_FragColor = texture2D(colorMap, varyTextCoord);
}
初始化
初始化主要分為4部分
-
setupLayer
:創(chuàng)建圖層 -
setupContext
:創(chuàng)建上下文 -
deleteRenderAndFrameBuffer
:清理緩存區(qū) -
setupRenderBuffer静浴、setupFrameBuffer
:設置RenderBuffer & FrameBuffer
setupLayer函數(shù):創(chuàng)建圖層
layer主要是用于顯示OpenGL ES繪制內容的載體
- 創(chuàng)建特殊圖層,有兩種方式
- 1)直接使用
view
自帶的layer
由于UIView
中自帶的layer
是繼承自CALayer
的挤渐,而需要創(chuàng)建的layer
是繼承自CAEAGLLayer
的苹享,所以需要重寫類方法layerClass
,返回[CAEAGLLayer class]
- 2)使用
init
創(chuàng)建圖層
可以直接使用[[CAEAGLLayer alloc] init]
創(chuàng)建一個新的layer
浴麻,將其add
到layer
上 - 在本案例中得问,使用的是
view
自帶的layer
- 1)直接使用
- 設置
scale
,將layer
的大小設置為跟屏幕大小一致 - 設置描述屬性
-
kEAGLDrawablePropertyRetainedBacking
表示繪圖表面顯示后白胀,是否保留其內容椭赋。 只有true
或者false
兩種, 默認false
-
kEAGLDrawablePropertyColorFormat
表示可繪制表面的內部顏色緩存區(qū)格式或杠。 有以下三種值,默認kEAGLColorFormatRGBA8
-
kEAGLColorFormatRGBA8
32位的RGBA顏色值(每個表示8位宣蔚,所以4*8=32位)
kEAGLColorFormatRGB565
16位的RGB顏色值
kEAGLColorFormatSRGBA8
表示標準的紅向抢、綠认境、藍,sRGB的色彩空間基于獨立的色彩坐標挟鸠,可以使色彩在不同的設備使用傳輸中對應于同一個色彩坐標體系叉信,而不受這些設備各自具有的不同色彩坐標的影響。
//1艘希、創(chuàng)建圖層
- (void)setupLayer{
// 1硼身、創(chuàng)建特殊圖層
self.myEagLayer = (CAEAGLLayer*)self.layer;
// 2、設置scale
[self setContentScaleFactor:[[UIScreen mainScreen] scale]];
// 3覆享、設置描述屬性
self.myEagLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:@false, kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil];
}
+ (Class)layerClass{
return [CAEAGLLayer class];
}
setupContext函數(shù):創(chuàng)建上下文
上下文主要是用于保存OpenGL ES
中的狀態(tài)佳遂,是一個狀態(tài)機,不論是GLKit
還是GLSL
撒顿,都是需要context
的丑罪,主要創(chuàng)建流程如下
- 創(chuàng)建
Context
,并指定OpenGL ES
渲染API的版本號(2凤壁、3均可)吩屹,且判斷是否創(chuàng)建成功 - 設置當前的
context
為創(chuàng)建的context
,并判斷是都設置成功 - 將其賦值給全局的
context
//2拧抖、創(chuàng)建上下文
- (void)setupContext{
// 1煤搜、指定OpenGL ES 渲染API版本
EAGLRenderingAPI api = kEAGLRenderingAPIOpenGLES2;
// 2、創(chuàng)建圖形上下文
EAGLContext *context = [[EAGLContext alloc] initWithAPI:api];
// 3唧席、判斷是否創(chuàng)建成功
if (!context) {
NSLog(@"Create context failed!");
return;
}
// 4宅楞、設置圖形上下文
if (![EAGLContext setCurrentContext:context]) {
NSLog(@"setCurrentContext failed");
return;
}
// 5、將局部變量賦值給全局變量
self.myContext = context;
}
deleteRenderAndFrameBuffer函數(shù):清理緩存區(qū)
清理緩沖區(qū)的目的在于清除殘留數(shù)據(jù)袱吆,防止殘留數(shù)據(jù)對本次操作造成影響
需要清空兩個緩存區(qū):RenderBuffer和FrameBuffer
//3厌衙、清空緩存區(qū)
- (void)deleteRenderAndFrameBuffer{
// 清空渲染緩存區(qū)
glDeleteBuffers(1, &_myColorRenderBuffer);
self.myColorRenderBuffer = 0;
// 清空幀緩存區(qū)
glDeleteBuffers(1, &_myColorFrameBuffer);
self.myColorFrameBuffer = 0;
}
設置RenderBUffe & FrameBuffer
首先了解一下RenderBuffer和FrameBuffer
-
RenderBuffer
:是一個通過應用分配的2D
圖像緩沖區(qū),需要附著在FrameBuffer
上 -
FrameBuffer
:是一個收集顏色绞绒、深度和模板緩存區(qū)的附著點婶希,簡稱FBO
,即是一個管理者蓬衡,用來管理RenderBuffer
喻杈,且FrameBuffer
沒有實際的存儲功能,真正實現(xiàn)存儲的是RenderBuffer
兩者間的關系如下圖:
FrameBuffer
有3個附著點顏色附著點(
Color Attachment
):管理紋理狰晚、顏色緩沖區(qū)
深度附著點(depth Attachment
):會影響顏色緩沖區(qū)筒饰,管理深度緩沖區(qū)(Depth Buffer
)
模板附著點(Stencil Attachment
):管理模板緩沖區(qū)(Stencil Buffer
)
RenderBuffe
r有3種緩存區(qū)深度緩存區(qū)(
Depth Buffer
):存儲深度值等
紋理緩存區(qū):存儲紋理坐標中對應的紋素、顏色值等
模板緩存區(qū)(Stencil Buffer
):存儲模板
setupRenderBuffer函數(shù)
主要是創(chuàng)建RenderBufferID
并申請標識符壁晒,將標識符綁定至GL_RENDERBUFFER
瓷们,并且將layer
的相關存儲綁定到RenderBuffer
對象
//4、設置RenderBuffer
- (void)setupRenderBuffer{
// 1、定義一個緩存區(qū)ID
GLuint buffer;
// 2谬晕、申請一個緩存區(qū)標識符
glGenRenderbuffers(1, &buffer);
// 3碘裕、賦值給全局變量
self.myColorRenderBuffer = buffer;
// 4、將標識符綁定到GL_RENDERBUFFER
glBindRenderbuffer(GL_RENDERBUFFER, self.myColorRenderBuffer);
// 5攒钳、將可繪制對象drawable object的CAEAGLLayer的存儲綁定到OpenGL ES renderBuffer對象
[self.myContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:self.myEagLayer];
}
setupFrameBuffer函數(shù)
主要是創(chuàng)建FrameBuffer
的ID并申請標識符帮孔,將標識符綁定至GL_FRAMEBUFFER
,然后將RenderBuffer
通過glFramebufferRenderbuffer
函數(shù)綁定到FrameBuffer
中的GL_COLOR_ATTACHMENT0
附著點上不撑,通過FrameBuffer
來管理RenderBuffer
文兢,RenderBuffer
存儲相關數(shù)據(jù)到相應緩存區(qū)
//5、設置FrameBuffer
- (void)setupFrameBuffer{
// 1焕檬、定義一個ID
GLuint buffer;
// 2姆坚、申請一個緩存區(qū)標識符
glGenBuffers(1, &buffer);
// 3、賦值給全局變量
self.myColorFrameBuffer = buffer;
// 4揩页、將標識符綁定到GL_FRAMEBUFFER
glBindFramebuffer(GL_FRAMEBUFFER, self.myColorFrameBuffer);
// 5旷偿、將渲染緩存區(qū)的myColorRenderBuffer 通過 glFramebufferRenderbuffer函數(shù)綁定到GL_COLOR_ATTACHMENT0上
/*
glFramebufferRenderbuffer (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer)
參數(shù)1:綁定到的目標
參數(shù)2:FrameBuffer的附著點
參數(shù)3:需要綁定的渲染緩沖區(qū)目標
參數(shù)4:渲染緩沖區(qū)
*/
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, self.myColorRenderBuffer);
}
注:綁定
renderBuffer
和FrameBuffer
是有順序的,先有RenderBuffer
爆侣,才有FrameBuffer
繪制
繪制的整體流程圖所示:
主要包含5部分
- 初始化:初始化背景顏色萍程,清理緩存,并設置視口大小
-
GLSL
自定義著色器加載:對自定義著色器進行加載兔仰,大致步驟為讀取-->加載-->編譯-->program鏈接-->使用
- 頂點數(shù)據(jù)設置及處理:將頂點坐標和紋理坐標讀取到自定義的頂點著色器中
- 加載紋理:將
png/jpg
圖片解壓成位圖茫负,并讀取紋理每個像素點的紋素 - 繪制:開始繪制,存儲到
RenderBuffer
,從RenderBuffer
將圖片顯示到屏幕上
初始化
需要注意的是乎赴,需要將視口的大小設置為與屏幕大小一致
// 設置清屏顏色 & 清除屏幕
glClearColor(0.3f, 0.45f, 0.5f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// 1忍法、設置視口大小
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);
GLSL自定義著色器加載
自定義著色器的加載主要分為以下幾步
- 讀取自定義著色器
-
compileShader & loadShaders
函數(shù):編譯&加載著色器 - 鏈接
program
& 判斷鏈接是否成功 - 使用
program
讀取自定義著色器
讀取自定義著色器文件的前提是需要獲得文件的路徑,將其傳入loadShaders
函數(shù)進行加載
NSString *vertFile = [[NSBundle mainBundle] pathForResource:@"shaderv" ofType:@"vsh"];
NSString *fragFile = [[NSBundle mainBundle] pathForResource:@"shaderf" ofType:@"fsh"];
loadShaders函數(shù) & compileShader函數(shù)
compileShader
函數(shù):在將著色器加載/附著到program
上時榕吼,需要先進行編譯饿序,分為以下幾步
- 根據(jù)文件路徑讀取著色器文件中的源碼字符串,并將其轉換為
c
中的字符串羹蚣,類型為GLchar
- 根據(jù)傳入的著色器類型
type
原探,調用glCreateShader
函數(shù)創(chuàng)建一個帶有唯一標識ID
的著色器,此時著色器中并沒有附加相對應的源碼- 將讀取的著色器源碼通過
glShaderSource
函數(shù)附加到創(chuàng)建的shader
上顽素,并將shader
的ID
返回給loadShaders
函數(shù)中shader
咽弦,以ID
來獲取并使用對應的著色器- 通過
glCompileShader
函數(shù)將shader
上附加的源碼編譯成目標代碼
//編譯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];
NSLog(@"content %@, source %s", content, source);
//2.創(chuàng)建一個shader(根據(jù)type類型)
*shader = glCreateShader(type);
//3.將著色器源碼附加到著色器對象上胁出。
//參數(shù)1:shader,要編譯的著色器對象 *shader
//參數(shù)2:numOfStrings,傳遞的源碼字符串數(shù)量 1個
//參數(shù)3:strings,著色器程序的源碼(真正的著色器程序源碼)
//參數(shù)4:lenOfStrings,長度型型,具有每個字符串長度的數(shù)組,或NULL全蝶,這意味著字符串是NULL終止的
glShaderSource(*shader, 1, &source,NULL);
NSLog(@"shader %d", *shader);
//4.把著色器源代碼編譯成目標代碼
glCompileShader(*shader);
}
loadShaders
函數(shù):分別將頂點著色器和片元著色器編譯完成后闹蒜,并返回著色器對應的ID
寺枉,然后通過glAttachShader
函數(shù)將頂點和片元的shader
分別附著到program
上,然后釋放不再使用的shader
嫂用,并賦值給全局的program
//加載shader
-(GLuint)loadShaders:(NSString *)vert Withfrag:(NSString *)frag
{
//1.定義2個零時著色器對象
GLuint verShader, fragShader;
//創(chuàng)建program
GLint program = glCreateProgram();
//2.編譯頂點著色程序型凳、片元著色器程序
//參數(shù)1:編譯完存儲的底層地址
//參數(shù)2:編譯的類型丈冬,GL_VERTEX_SHADER(頂點)嘱函、GL_FRAGMENT_SHADER(片元)
//參數(shù)3:文件路徑
[self compileShader:&verShader type:GL_VERTEX_SHADER file:vert];
[self compileShader:&fragShader type:GL_FRAGMENT_SHADER file:frag];
NSLog(@"verShader %d, fragShader: %d", verShader, fragShader);
//3.創(chuàng)建最終的程序
glAttachShader(program, verShader);
glAttachShader(program, fragShader);
//4.釋放不需要的shader
glDeleteShader(verShader);
glDeleteShader(fragShader);
return program;
}
鏈接program
- 通過
glLinkProgram
函數(shù)鏈接program
- 可以通過
glGetProgramiv
函數(shù)通過制定值GL_LINK_STATUS
獲取鏈接的狀態(tài),判斷鏈接是成功還是失敗 - 如果鏈接失敗埂蕊,可以通過
glGetProgramIngoLog
函數(shù)獲取錯誤信息日志往弓,根據(jù)錯誤信息一致去排查問題
使用program
通過glUseProgram
函數(shù)來使用鏈接成功的program
glUseProgram(self.myPrograme);
頂點數(shù)據(jù)設置及處理
通過數(shù)組存儲頂點數(shù)據(jù),并將頂點坐標和紋理坐標讀取到自定義的頂點著色器中
分為以下三步
- 設置頂點數(shù)據(jù):主要是初始化頂點坐標和紋理坐標
- 開辟頂點緩存區(qū):用于將頂點數(shù)據(jù)從
CPU
拷貝至GPU
- 打開頂點/片元的通道
- 設置頂點數(shù)據(jù)沒什么好說的蓄氧,就是以一個一維數(shù)組函似,下面說說后面的兩步
開辟頂點緩存區(qū)
開啟頂點緩存區(qū),這部分其實跟之前使用GLKit
框架開啟緩存區(qū)步驟是一致的喉童,沒什么變化撇寞,有以下四步
- 通過
GLuint
定義一個頂點緩存區(qū)ID
- 通過
glGenBuffers
函數(shù),申請一個頂點緩存區(qū)標識符 - 通過
glBindBuffers
函數(shù)堂氯,將緩存區(qū)的標識符綁定到GL_ARRAY_BUFFER
- 通過
glBufferData
函數(shù)蔑担,將頂點數(shù)據(jù)copy
到GPU
中
GLuint attrBuffer;
glGenBuffers(1, &attrBuffer);
glBindBuffer(GL_ARRAY_BUFFER, attrBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(attrArr), attrArr, GL_DYNAMIC_DRAW);
打開頂點/片元的通道
在iOS
中,attribute
通道默認是關閉的咽白,需要手動開啟啤握,而數(shù)據(jù)有頂點坐標和紋理坐標兩種,需要分別開啟兩次晶框,這里的開啟與GLKit
框架中是有所區(qū)別的排抬,
- 由于本案例使用的是自定義著色器,所以需要自己獲取
vertex attribut
的入口授段, - 在
GLKit
中使用的是封裝好的固定著色器蹲蒲,直接指定入口即可
使用自定義著色器打開通道,一般有以下三步(相對于GLKit
而言侵贵,只多了一個獲取入口的步驟届搁,后面兩步是沒有多大變化的)
- 通過
glGetAttribLocation
函數(shù),獲取vertex attribute
的入口模燥,需要傳入兩個參數(shù)咖祭,一個是program
,一個是自定義著色器文件中變量名字符串蔫骂,這里著重強調下C春病!辽旋!第二個參數(shù)的字符串必須與著色器文件中對應的變量名保持一致浩嫌! - 通過
glEnableVertexAttribArray
函數(shù)檐迟,設置合適的格式從buffer
里讀取數(shù)據(jù),即設置讀取入口 - 通過
glVertexAttribPointer
函數(shù)码耐,設置讀取方式
// (1)注意:第二參數(shù)字符串必須和shaderv.vsh中的輸入變量:position保持一致
GLuint position = glGetAttribLocation(self.myPrograme, "position");
// (2).設置合適的格式從buffer里面讀取數(shù)據(jù)
glEnableVertexAttribArray(position);
// (3).設置讀取方式
// 參數(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ù)值是否應該歸一化廓块,或者直接轉換為固定值。(GL_FALSE)
// 參數(shù)5:stride,連續(xù)頂點屬性之間的偏移量契沫,默認為0带猴;
// 參數(shù)6:指定一個指針,指向數(shù)組中的第一個頂點屬性的第一個組件懈万。默認為0
glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*5, NULL);
// 9拴清、處理紋理數(shù)據(jù)
// (1).glGetAttribLocation,用來獲取vertex attribute的入口的.
// 注意:第二參數(shù)字符串必須和shaderv.vsh中的輸入變量:textCoordinate保持一致
// (2).設置合適的格式從buffer里面讀取數(shù)據(jù)
// (3).設置讀取方式
GLuint textColor = glGetAttribLocation(self.myPrograme, "textCoordinate");
glEnableVertexAttribArray(textColor);
glVertexAttribPointer(textColor, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*5, (float *)NULL+3);
加載紋理
這部分的內容主要是將png/jpg
圖片解壓成位圖,并通過自定義著色器讀取紋理每個像素點的紋素会通,包含兩部分
setupTexture函數(shù)
將png/jpg
解壓成位圖口予,加載成紋理數(shù)據(jù),其中紋理的解壓縮使用的都是CoreGraphic
渴语,加載紋理的流程如下圖
- 紋理解壓縮:將
UIImage
轉換為CGImageRef
- 圖片重繪:使用
CGContextRef
常見的上下文苹威,調用CGContextDrawImage
函數(shù)使用默認方式進行繪制,再繪制之前驾凶,需要獲取圖片的大小牙甫、寬、高等數(shù)據(jù)调违,因為繪制時需要使用這些數(shù)據(jù) - 綁定紋理:通過
glBindTexture
函數(shù)綁定窟哺,當只有一個紋理的時候,默認的紋理ID
是0
技肩,且0
一直是激活狀態(tài)且轨,因此是可以省略glGenTexture
的 - 設置紋理屬性:通過
glTexParameteri
函數(shù)分別設置 放大/縮小的過濾方式 和S/T
的環(huán)繞模式 - 載入紋理:通過
glTexImage2D
函數(shù)載入紋理,載入完成后虚婿,釋放指向紋理數(shù)據(jù)的指針
// 從圖片中加載紋理
- (GLuint)setupTexture: (NSString *)fileName {
// 1旋奢、將UIImage轉換為CGImageRef & 判斷圖片是否獲取成功
CGImageRef spriteImage = [UIImage imageNamed:fileName].CGImage;
if (!spriteImage) {
NSLog(@"Failed to lead image %@", fileName);
exit(1);
}
// 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,指向要渲染的繪制圖像的內存地址
參數(shù)2:width,bitmap的寬度剧浸,單位為像素
參數(shù)3:height,bitmap的高度锹引,單位為像素
參數(shù)4:bitPerComponent,內存中像素的每個組件的位數(shù)矗钟,比如32位RGBA,就設置為8
參數(shù)5:bytesPerRow,bitmap的沒一行的內存所占的比特數(shù)
參數(shù)6:colorSpace,bitmap上使用的顏色空間 kCGImageAlphaPremultipliedLast:RGBA
*/
CGContextRef spriteContext = CGBitmapContextCreate(spriteData, width, height, 8, width*4, CGImageGetColorSpace(spriteImage), kCGImageAlphaPremultipliedLast);
NSLog(@"kCGImageAlphaPremultipliedLast %d", 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、使用默認方式繪制
CGContextDrawImage(spriteContext, rect, spriteImage);
// 7碑宴、畫圖完畢就釋放上下文
CGContextRelease(spriteContext);
// 8软啼、綁定紋理到默認的紋理ID
glBindTexture(GL_TEXTURE_2D, 0);
// 9桑谍、設置紋理屬性
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ù)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fw, fh, 0, GL_RGBA, GL_UNSIGNED_BYTE, spriteData);
// 11、釋放spriteData
free(spriteData);
return 0;
}
總結:圖片解壓縮的一般步驟
將
UIImage
轉換為CGImageRef
& 判斷圖片是否獲取成功
設置圖的大小锣披,寬和高
獲取圖片字節(jié)數(shù) = 寬高4(rgba)
創(chuàng)建CGContextRef
上下文
使用CGContextDrawImage
繪制圖片
設置紋理采樣器
主要是獲取紋理中對應像素點的的顏色值贞间,即紋素
- 通過
glGetUniformLocation
函數(shù),獲取fragment uniform
的入口雹仿,需要傳入兩個參數(shù)增热,一個是program
,一個是自定義片元著色器文件中變量名字符串colorMap
胧辽,這里著重強調下>稹!邑商!第二個參數(shù)的字符串必須與著色器文件中對應的變量名保持一致摄咆! - 通過
glUniform1i
函數(shù)獲取紋素,有兩個參數(shù)人断,第一個參數(shù)是fragment uniform
的入口吭从,本質也是一個ID
,第二個參數(shù)是紋理的ID
恶迈,使用的是默認的ID 0
glUniform1i(glGetUniformLocation(self.myPrograme, "colorMap"), 0);
繪制
開始繪制涩金,存儲到RenderBuffer
,從RenderBuffer
將圖片顯示到屏幕上
*調用glDrawArrays
函數(shù)指定圖元連接方式進行繪制
-
context
調用presentRenderbuffer
函數(shù)將繪制好的圖片渲染到屏幕上進行顯示
// 12、繪制
glDrawArrays(GL_TRIANGLES, 0, 6);
// 13暇仲、從渲染緩存區(qū)顯示到屏幕上
[self.myContext presentRenderbuffer:GL_RENDERBUFFER];