NO.15 - OpenGL ES 使用GLSL加載圖片

利用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浴麻,將其addlayer
    • 在本案例中得问,使用的是view自帶的layer
  • 設置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

兩者間的關系如下圖:


RenderBuffer和FrameBuffer兩者關系

FrameBuffer有3個附著點

顏色附著點(Color Attachment):管理紋理狰晚、顏色緩沖區(qū)
深度附著點(depth Attachment):會影響顏色緩沖區(qū)筒饰,管理深度緩沖區(qū)(Depth Buffer
模板附著點(Stencil Attachment):管理模板緩沖區(qū)(Stencil Buffer

RenderBuffer有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);
}

注:綁定renderBufferFrameBuffer是有順序的,先有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上顽素,并將shaderID返回給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ù)copyGPU
    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渴语,加載紋理的流程如下圖

setupTexture函數(shù)流程
  • 紋理解壓縮:將UIImage轉換為CGImageRef
  • 圖片重繪:使用CGContextRef常見的上下文苹威,調用CGContextDrawImage函數(shù)使用默認方式進行繪制,再繪制之前驾凶,需要獲取圖片的大小牙甫、寬、高等數(shù)據(jù)调违,因為繪制時需要使用這些數(shù)據(jù)
  • 綁定紋理:通過glBindTexture函數(shù)綁定窟哺,當只有一個紋理的時候,默認的紋理ID0技肩,且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];
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末步做,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子奈附,更是在濱河造成了極大的恐慌全度,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件桅狠,死亡現(xiàn)場離奇詭異讼载,居然都是意外死亡轿秧,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進店門咨堤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來菇篡,“玉大人,你說我怎么就攤上這事一喘「恍睿” “怎么了躯砰?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我机断,道長,這世上最難降的妖魔是什么媳否? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任揉阎,我火速辦了婚禮,結果婚禮上蚂维,老公的妹妹穿的比我還像新娘戳粒。我一直安慰自己,他們只是感情好虫啥,可當我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布蔚约。 她就那樣靜靜地躺著,像睡著了一般涂籽。 火紅的嫁衣襯著肌膚如雪苹祟。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天评雌,我揣著相機與錄音树枫,去河邊找鬼。 笑死柳骄,一個胖子當著我的面吹牛团赏,可吹牛的內容都是我干的。 我是一名探鬼主播耐薯,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼舔清,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了曲初?” 一聲冷哼從身側響起体谒,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎臼婆,沒想到半個月后抒痒,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡颁褂,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年故响,在試婚紗的時候發(fā)現(xiàn)自己被綠了傀广。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡彩届,死狀恐怖伪冰,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情樟蠕,我是刑警寧澤贮聂,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站寨辩,受9級特大地震影響吓懈,放射性物質發(fā)生泄漏。R本人自食惡果不足惜靡狞,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一耻警、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧耍攘,春花似錦榕栏、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽庆揪。三九已至式曲,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間缸榛,已是汗流浹背吝羞。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留内颗,地道東北人钧排。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像均澳,于是被迫代替她去往敵國和親恨溜。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,722評論 2 345