OpenGL ES-12-案例07-灰度&馬賽克濾鏡

?賽克效果就是把圖?的?個(gè)相當(dāng)??的區(qū)域?同?個(gè)點(diǎn)的顏?來表示.可以認(rèn)為是?規(guī)模的降低圖像的分辨率,?讓圖像的?些細(xì)節(jié)隱藏起來明肮。

其實(shí)整份代碼與前一篇《分屏濾鏡》相比耳标,也就片元著色器部分發(fā)生了改變厉亏。
說白了伪嫁,濾鏡就是片元著色器像素值的處理哮奇。

一饭耳、效果圖

image

二串述、流程圖

image.png

三、著色器中的主要區(qū)別

頂點(diǎn)著色器中不需要改變哥攘,只在片元著色器改變像素繪制的點(diǎn)就ok了

1剖煌、頂點(diǎn)著色器

attribute vec4 Position;
attribute vec2 TextureCoords;
varying vec2 TextureCoordsVarying;

void main (void) {
    
    TextureCoordsVarying = TextureCoords;
    gl_Position = Position;
    
}

以下是不同濾鏡的片元著色器代碼

2、正常

precision highp float;
uniform sampler2D Texture;
varying vec2 TextureCoordsVarying;

void main (void) {
    vec4 mask = texture2D(Texture, TextureCoordsVarying);
    gl_FragColor = vec4(mask.rgb, 1.0);
}


3逝淹、灰度

灰度濾鏡耕姊,說白了就是調(diào)整了每個(gè)像素顯示顏色的亮度,使得整張圖片顯示一個(gè)顏色栅葡。

灰度濾鏡的實(shí)現(xiàn)原理是讓RGB值保持一個(gè)平衡并填充茉兰,或者只保留一個(gè)亮度值,即綠色欣簇,在人眼中规脸,綠色的亮度是最顯眼的坯约,綠色值越深,在肉眼觀察中圖片越暗淡莫鸭,這是眼睛的一種生理現(xiàn)象闹丐。

有5種方法可以通過調(diào)整RGB值來實(shí)現(xiàn):

  • 前三種屬于權(quán)值法:處理后的圖片比較逼真
  1. 浮點(diǎn)算法:Gray = R * 0.3 + G * 0.59 + B * 0.11 【RGB的權(quán)重總和為1】
  2. 整數(shù)?法:Gray = ( R * 30 + G * 59 + B * 11) / 100 【RGB的權(quán)重總和為100】
  3. 移位?法:Gray = ( R * 76 + G * 151 + B* 28) >> 8
  4. 平均值法:Gray = ( R + G + B ) / 3 【處理后的圖片比較柔和】
  5. 僅取綠?:Gray = G 【這種方式最方便簡(jiǎn)單,且易用】
precision highp float;
uniform sampler2D Texture;
varying vec2 TextureCoordsVarying;

//變換因子:RGB的權(quán)重值被因,綠色占比最高
const highp vec3 W = vec3(0.2125, 0.7154, 0.0721);

void main(){
    //獲取每個(gè)像素在對(duì)應(yīng)紋理坐標(biāo)下的顏色值
    vec4 mask = texture2D(Texture, TextureCoordsVarying);
    
    //將 像素顏色 與 變換因子 相乘 得到灰度值(向量和向量 點(diǎn)乘 得到標(biāo)量)
    float luminance = dot(mask.rgb, W);
    
    //將原本的r,g,b,a 最終轉(zhuǎn)換成 灰度值 填充到像素里卿拴。即(luminance,luminance,luminance,1.0)
    gl_FragColor = vec4(vec3(luminance), 1.0);
}



4、顛倒

通過上一個(gè)案例梨与,我們可以簡(jiǎn)單地來實(shí)現(xiàn)了堕花,直接Y值取反就實(shí)現(xiàn)了~

precision highp float;
uniform sampler2D Texture;
varying vec2 TextureCoordsVarying;

void main (void) {
    
    vec2 uv = TextureCoordsVarying.xy;
    vec4 mask = texture2D(Texture, vec2(uv.x,1.0-uv.y));
    gl_FragColor = vec4(mask.rgb, 1.0);
}



5、正方形馬賽克

馬賽克的原理:把圖片的一個(gè)相當(dāng)大小的區(qū)域用同一個(gè)顏色值來表示粥鞋,可以認(rèn)為是大規(guī)模的降低圖像的分辨率缘挽,從而讓圖像的一些細(xì)節(jié)隱藏起來。

舉例說明:100x100的圖片呻粹,每10x10的區(qū)域本里可能有100個(gè)不同顏色壕曼,現(xiàn)在只用一個(gè)顏色填充,整張圖片看下來就模糊了尚猿。

步驟:

  1. 先把原圖按我們?cè)O(shè)置的比例放大
  2. 根據(jù)比例計(jì)算每一個(gè)馬賽克的大小窝稿,獲得一個(gè)區(qū)域
  3. 把馬賽克區(qū)域根據(jù)比例縮小,就拿到了原紋理圖片中的位置
  4. 給這一個(gè)區(qū)域填充顏色
image
precision highp float;
uniform sampler2D Texture;
varying vec2 TextureCoordsVarying;
//TextureCoordsVarying原本傳進(jìn)來
//假定紋理圖片有這么大
const vec2 TexSize = vec2(400.0, 400.0);
//然后一個(gè)馬賽克占得位置這么大凿掂,其實(shí)是算的一個(gè)比例
const vec2 mosaicSize = vec2(16.0, 16.0);

void main (void) {
    //1、計(jì)算實(shí)際紋理的像素點(diǎn)位置纹蝴。
    //其實(shí)也是個(gè)相對(duì)的值庄萎,因?yàn)門extureCoordsVarying原本的值并不能確定每一個(gè)像素點(diǎn)的位置,放大后更好找
    vec2 intXY = vec2(TextureCoordsVarying.x*TexSize.x, TextureCoordsVarying.y*TexSize.y);
    //2塘安、計(jì)算一個(gè)小馬賽克的大小糠涛,即一個(gè)色塊的位置
    /*
    floor(x)函數(shù):返回一個(gè) 小于或者等于x的最大的整數(shù),也就是向下取整
    floor(intXY.x/mosaicSize.x)*mosaicSize.x 拿到小馬賽克的X
    floor(intXY.y/mosaicSize.y)*mosaicSize.y 拿到小馬賽克的Y
     */
    vec2 XYMosaic = vec2(floor(intXY.x/mosaicSize.x)*mosaicSize.x, floor(intXY.y/mosaicSize.y)*mosaicSize.y);
    //3兼犯、換算成在紋理坐標(biāo)中真正的位置
    vec2 UVMosaic = vec2(XYMosaic.x/TexSize.x, XYMosaic.y/TexSize. y);
    //4忍捡、拿到?賽克后的紋理坐標(biāo)中的顏?值,然后進(jìn)行填充
    vec4 mask = texture2D(Texture, UVMosaic);
    gl_FragColor = vec4(mask.rgb, 1.0);
} 

6切黔、六邊形馬賽克

六邊形馬賽克原理:將一張圖片分割成由六邊形組成砸脊,再取每個(gè)六邊形的中心點(diǎn)畫出一個(gè)個(gè)的矩形,根據(jù)矩形的奇偶排列情況求出對(duì)應(yīng)的2個(gè)中心點(diǎn)纬霞,并計(jì)算紋理坐標(biāo)與兩個(gè)中心點(diǎn)的距離凌埂,根據(jù)距離判斷,采取就近原則诗芜,當(dāng)前的六邊形就采用近的中心點(diǎn)的顏色值瞳抓。

說直白點(diǎn)埃疫,一個(gè)圖片分成N個(gè)六邊形,然后找出所有六邊形中心點(diǎn)連線能組個(gè)一個(gè)個(gè)矩陣孩哑,這樣的矩形根據(jù)中心點(diǎn)排布栓霜,可分為4種,然后看每一個(gè)像素點(diǎn)的位置横蜒, 距離哪個(gè)中心點(diǎn)更近叙淌,這樣像素就取那個(gè)中心點(diǎn)的顏色,最終愁铺,一個(gè)六邊形同色塊的馬賽克就形成了鹰霍。

image

看幾行幾列,是為了計(jì)算對(duì)應(yīng)的兩個(gè)中心點(diǎn)茵乱,方便后面取值茂洒。

image

如何計(jì)算某一個(gè)像素點(diǎn)在這四種矩形中的具體坐標(biāo)呢?

image

然后瓶竭,計(jì)算這個(gè)點(diǎn)到兩個(gè)中心點(diǎn)v1和v2的距離督勺,距離誰比較近,取誰的顏色值就完成了斤贰。

  • s1 = √((v1.x-x)2 + (v1.y-y)2)
  • s2 = √((v2.x-x)2 + (v2.y-y)2)
precision highp float;
uniform sampler2D Texture;
varying vec2 TextureCoordsVarying;
//定義一個(gè)馬賽克邊長(zhǎng)
const float mosaicSize = 0.03;
void main (void) {
    
    float length = mosaicSize;
    //根據(jù)3:√3設(shè)定寬高
    //定義矩形的寬
    float TB = 1.5;
    //定義矩形的高
    float TR = 0.866025;
    
    //拿到當(dāng)前紋理坐標(biāo)
    float x = TextureCoordsVarying.x;
    float y = TextureCoordsVarying.y;
    
    //換算成在矩形中的坐標(biāo)
    int wx = int(x / TB / length);
    int wy = int(y / TR / length);
    
    //定義中心點(diǎn)v1  v2 和最終選的的點(diǎn)
    vec2 v1, v2, vn;
    
    //分4種情況判斷取這個(gè)點(diǎn)的顏色值智哀。就要先拿到對(duì)應(yīng)2個(gè)中心點(diǎn)坐標(biāo)
    //wx/2 * 2 == wx 就是偶數(shù)行   wy/2 * 2 == wy就是偶數(shù)列
    if (wx/2 * 2 == wx) {
       
        if (wy/2 * 2 == wy) {
            //(0,0),(1,1)
            v1 = vec2(length * 1.5 * float(wx), length * TR * float(wy));
            v2 = vec2(length * 1.5 * float(wx + 1), length * TR * float(wy + 1));
        } else {
            //(0,1),(1,0)
            v1 = vec2(length * 1.5 * float(wx), length * TR * float(wy + 1));
            v2 = vec2(length * 1.5 * float(wx + 1), length * TR * float(wy));
        }
    }else {
        if (wy/2 * 2 == wy) {
            //(0,1),(1,0)
            v1 = vec2(length * 1.5 * float(wx), length * TR * float(wy + 1));
            v2 = vec2(length * 1.5 * float(wx + 1), length * TR * float(wy));
        } else {
            //(0,0),(1,1)
            v1 = vec2(length * 1.5 * float(wx), length * TR * float(wy));
            v2 = vec2(length * 1.5 * float(wx + 1), length * TR * float(wy + 1));
        }
    }
    
    
    //計(jì)算現(xiàn)在這個(gè)像素點(diǎn)到兩個(gè)中心點(diǎn)之間的距離
    float s1 = sqrt(pow(v1.x - x, 2.0) + pow(v1.y - y, 2.0));
    float s2 = sqrt(pow(v2.x - x, 2.0) + pow(v2.y - y, 2.0));
    
    //比較,哪個(gè)距離小荧恍,這個(gè)像素點(diǎn)的顏色就取哪個(gè)中心點(diǎn)顏色
    if (s1 < s2) {
        vn = v1;
    } else {
        vn = v2;
    }
    vec4 color = texture2D(Texture, vn);
    
    gl_FragColor = color;
}



7瓷叫、三角形馬賽克

三角形馬賽克是在六邊形馬賽克的基礎(chǔ)上得到的,是把正六邊形馬賽克進(jìn)行6等分送巡,得到6個(gè)正三角形摹菠。

  1. 計(jì)算某一個(gè)像素點(diǎn)和中心點(diǎn)直接的夾角
  2. 求出6個(gè)正三角形的中心點(diǎn)
  3. 判斷這個(gè)夾角屬于哪個(gè)正三角形,就取哪個(gè)正三角形的顏色值
image
image
precision highp float;
uniform sampler2D Texture;
varying vec2 TextureCoordsVarying;
//定義一個(gè)馬賽克邊長(zhǎng)
const float mosaicSize = 0.03;
void main (void) {
    
    float length = mosaicSize;
    //根據(jù)3:√3設(shè)定寬高
    //定義矩形的寬
    float TB = 1.5;
    //定義矩形的高
    float TR = 0.866025;
    //π/6的值
    const float PI6 = 0.523599;
    //拿到當(dāng)前紋理坐標(biāo)
    float x = TextureCoordsVarying.x;
    float y = TextureCoordsVarying.y;
    
    //換算成在矩形中的坐標(biāo)
    int wx = int(x / TB / length);
    int wy = int(y / TR / length);
    
    //定義中心點(diǎn)v1  v2 和最終選的的點(diǎn)
    vec2 v1, v2, vn;
    
    //分4種情況判斷取這個(gè)點(diǎn)的顏色值骗爆。就要先拿到對(duì)應(yīng)2個(gè)中心點(diǎn)坐標(biāo)
    //wx/2 * 2 == wx 就是偶數(shù)行   wy/2 * 2 == wy就是偶數(shù)列
    if (wx/2 * 2 == wx) {
       
        if (wy/2 * 2 == wy) {
            //(0,0),(1,1)
            v1 = vec2(length * 1.5 * float(wx), length * TR * float(wy));
            v2 = vec2(length * 1.5 * float(wx + 1), length * TR * float(wy + 1));
        } else {
            //(0,1),(1,0)
            v1 = vec2(length * 1.5 * float(wx), length * TR * float(wy + 1));
            v2 = vec2(length * 1.5 * float(wx + 1), length * TR * float(wy));
        }
    }else {
        if (wy/2 * 2 == wy) {
            //(0,1),(1,0)
            v1 = vec2(length * 1.5 * float(wx), length * TR * float(wy + 1));
            v2 = vec2(length * 1.5 * float(wx + 1), length * TR * float(wy));
        } else {
            //(0,0),(1,1)
            v1 = vec2(length * 1.5 * float(wx), length * TR * float(wy));
            v2 = vec2(length * 1.5 * float(wx + 1), length * TR * float(wy + 1));
        }
    }
    
    
    //計(jì)算現(xiàn)在這個(gè)像素點(diǎn)到兩個(gè)中心點(diǎn)之間的距離
    float s1 = sqrt(pow(v1.x - x, 2.0) + pow(v1.y - y, 2.0));
    float s2 = sqrt(pow(v2.x - x, 2.0) + pow(v2.y - y, 2.0));
    
    //比較次氨,哪個(gè)距離小,這個(gè)像素點(diǎn)的顏色就取哪個(gè)中心點(diǎn)顏色
    if (s1 < s2) {
        vn = v1;
    } else {
        vn = v2;
    }
    
    
    //計(jì)算像素和中心點(diǎn)vn的夾角
    float a = atan((x - vn.x)/(y - vn.y));
    
    //計(jì)算6個(gè)正三角形的中心點(diǎn)
    vec2 area1 = vec2(vn.x, vn.y - mosaicSize * TR / 2.0);
    vec2 area2 = vec2(vn.x + mosaicSize / 2.0, vn.y - mosaicSize * TR / 2.0);
    vec2 area3 = vec2(vn.x + mosaicSize / 2.0, vn.y + mosaicSize * TR / 2.0);
    vec2 area4 = vec2(vn.x, vn.y + mosaicSize * TR / 2.0);
    vec2 area5 = vec2(vn.x - mosaicSize / 2.0, vn.y + mosaicSize * TR / 2.0);
    vec2 area6 = vec2(vn.x - mosaicSize / 2.0, vn.y - mosaicSize * TR / 2.0);
    
    //判斷在哪個(gè)區(qū)域
    if (a >= PI6 && a < PI6 * 3.0) {
        vn = area1;
    } else if (a >= PI6 * 3.0 && a < PI6 * 5.0) {
        vn = area2;
    } else if ((a >= PI6 * 5.0 && a <= PI6 * 6.0)|| (a<-PI6 * 5.0 && a>-PI6*6.0)) {
        vn = area3;
    } else if (a < -PI6 * 3.0 && a >= -PI6 * 5.0) {
        vn = area4;
    } else if(a <= -PI6 && a> -PI6 * 3.0) {
        vn = area5;
    } else if (a > -PI6 && a < PI6)
    {
        vn = area6;
    }
    
    //拿的最終所在區(qū)域的三角形中心點(diǎn)顏色
    vec4 color = texture2D(Texture, vn);
    gl_FragColor = color;
     
}



四摘投、代碼部分


 #import "ViewController.h"
 #import <GLKit/GLKit.h>
 #import "FilterBar.h"

 //之前也提到過煮寡,c語言結(jié)構(gòu)體,存放頂點(diǎn)數(shù)據(jù)
 typedef struct {
     GLKVector3 positionCoord; // (X, Y, Z)
     GLKVector2 textureCoord; // (U, V)
 } SenceVertex;


 @interface ViewController ()<FilterBarDelegate>
 // 頂點(diǎn)數(shù)組
 @property (nonatomic, assign) SenceVertex *vertices;
 // 上下文
 @property (nonatomic, strong) EAGLContext *context;
 // 用于刷新屏幕的專屬定時(shí)器(相比timer犀呼,它可以和屏幕刷新同頻)
 @property (nonatomic, strong) CADisplayLink *displayLink;
 // 著色器程序
 @property (nonatomic, assign) GLuint program;
 // 頂點(diǎn)緩沖區(qū)id
 @property (nonatomic, assign) GLuint vertexBuffer;
 // 紋理的id
 @property (nonatomic, assign) GLuint textureID;

 @end

 @implementation ViewController

 //釋放部分
 - (void)dealloc {
     //上下文釋放
     if ([EAGLContext currentContext] == self.context) {
         [EAGLContext setCurrentContext:nil];
     }
     //頂點(diǎn)緩存區(qū)釋放
     if (_vertexBuffer) {
         glDeleteBuffers(1, &_vertexBuffer);
         _vertexBuffer = 0;
     }
     //頂點(diǎn)數(shù)組釋放
     if (_vertices) {
         free(_vertices);
         _vertices = nil;
     }
 }

 - (void)viewWillDisappear:(BOOL)animated {
     [super viewWillDisappear:animated];
     
     // 移除 displayLink
     if (self.displayLink) {
         [self.displayLink invalidate];
         self.displayLink = nil;
     }
 }

 - (void)viewDidLoad {
     [super viewDidLoad];
     
     self.view.backgroundColor = [UIColor blackColor];
     
     /*
      整體思路:
      和GLSL加載圖片的流程一樣
      不同分屏濾鏡效果幸撕,主要是在著色器里去計(jì)算的
      這里加一個(gè)計(jì)時(shí)器的關(guān)鍵點(diǎn)在于,讓屏幕保持一直刷新渲染圆凰,方便切換濾鏡及時(shí)刷新杈帐。更多是用于有動(dòng)效的濾鏡
      */
     //1、創(chuàng)建底部切換bar
     [self setupFilterBar];
     
     //2、GLSL加載圖片流程
     [self loaderImage];

     //3挑童、啟動(dòng)定時(shí)器累铅,刷新屏幕
     [self startRender];
 }

 #pragma mark - 1
 - (void)setupFilterBar {
     
     CGFloat filterBarWidth = [UIScreen mainScreen].bounds.size.width;
     CGFloat filterBarHeight = 100;
     CGFloat filterBarY = [UIScreen mainScreen].bounds.size.height - filterBarHeight;
     NSArray *dataSource = @[@"無",@"灰度",@"顛倒",@"正方形\n馬賽克",@"六邊形\n馬賽克",@"三角形\n馬賽克"];
     
     FilterBar *filerBar = [[FilterBar alloc] initWithFrame:CGRectMake(0, filterBarY, filterBarWidth, filterBarHeight)];
     filerBar.itemList = dataSource;
     filerBar.delegate = self;
     [self.view addSubview:filerBar];
      
 }


 - (void)filterBar:(FilterBar *)filterBar didScrollToIndex:(NSUInteger)index {
     //1. 選擇默認(rèn)shader
     if (index == 0) {
         [self setUpDrawShaderWith:@"Normal"];
     }else if(index == 1)
     {
         [self setUpDrawShaderWith:@"Gray"];
     }else if(index == 2)
     {
         [self setUpDrawShaderWith:@"Reversal"];
     }else if(index == 3)
     {
         [self setUpDrawShaderWith:@"square"];
     }else if(index == 4)
     {
         [self setUpDrawShaderWith:@"hexagon"];
     }else if(index == 5)
     {
         [self setUpDrawShaderWith:@"triangle"];
     }
     // 重新開始濾鏡動(dòng)畫
     [self startRender];
 }

 #pragma mark - 2
 - (void)loaderImage {

     //1、準(zhǔn)備工作
     /*
      把一些會(huì)重復(fù)用到的地方站叼,封裝起來娃兽,然后剩下的不變的,放在這個(gè)方法里面尽楔。
      上下文&設(shè)置當(dāng)前
      設(shè)置圖層
      設(shè)置緩沖區(qū)
      設(shè)置視口
      設(shè)置頂點(diǎn)數(shù)據(jù)
      設(shè)置頂點(diǎn)緩沖區(qū)
      解壓圖片投储,拿到紋理id(因?yàn)檫@里面只有一個(gè)紋理,如果有多個(gè)也要拆分出去阔馋,方便復(fù)用)
      */
     [self setUpConfig];
     
     //2玛荞、繪制每一個(gè)著色器都需要調(diào)用的方法。第一次加載呕寝,肯定使用默認(rèn)著色器
     /*
      1勋眯、加載、編譯shader
         1)拿到shader路徑下梢,轉(zhuǎn)成c字符串
         2)創(chuàng)建shader對(duì)象
         3)把著色器字符串 附著到shader對(duì)象上
         4)編譯shader對(duì)象&檢驗(yàn)
      2客蹋、附著、連接program
         1)創(chuàng)建一個(gè)program對(duì)象
         2)把頂點(diǎn)孽江、片元shader 附著上
         3)鏈接program&檢驗(yàn)
      3讶坯、use program
      4、傳遞數(shù)據(jù)
         1)頂點(diǎn)坐標(biāo)數(shù)據(jù)
         2)紋理坐標(biāo)數(shù)據(jù)
         3)采樣器傳遞紋理id(紋理id在準(zhǔn)備工作就拿到了岗屏,不放這里是防止重復(fù)操作)
      */
     [self setUpDrawShaderWith:@"Normal"];
     
      
 }
 #pragma mark - 2.1
 - (void)setUpConfig {
     
     //1.上下文
     self.context = [[EAGLContext alloc] initWithAPI: kEAGLRenderingAPIOpenGLES2];
     [EAGLContext setCurrentContext:self.context];
     
     
     //2辆琅、圖層-設(shè)置一個(gè)正方形
     CAEAGLLayer *layer = [[CAEAGLLayer alloc] init];
     layer.frame = CGRectMake(0, 100, self.view.frame.size.width, self.view.frame.size.width);
     layer.contentsScale = [[UIScreen mainScreen] scale];
     [self.view.layer addSublayer:layer];
     
     
     //3、緩沖區(qū)
     //1)渲染緩沖區(qū)
     GLuint rBuffer,fBuffer;
     glGenRenderbuffers(1, &rBuffer);
     glBindRenderbuffer(GL_RENDERBUFFER, rBuffer);
     //把layer的存儲(chǔ)綁定到渲染緩沖區(qū)
     [self.context renderbufferStorage:GL_RENDERBUFFER fromDrawable:layer];
     //2)幀緩沖區(qū)
     glGenFramebuffers(1, &fBuffer);
     glBindFramebuffer(GL_FRAMEBUFFER, fBuffer);
     //把renderBuffer綁定到ATTACHMENT0上
     glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rBuffer);

     
     //4担汤、視口
     glViewport(0, 0, self.drawableWidth, self.drawableHeight);


     //5涎跨、頂點(diǎn)數(shù)據(jù)
     //1)開辟頂點(diǎn)數(shù)組內(nèi)存空間
     self.vertices = malloc(sizeof(SenceVertex) * 4);
     //2)
     self.vertices[0] = (SenceVertex){{-1, 1, 0}, {0, 1}};
     self.vertices[1] = (SenceVertex){{-1, -1, 0}, {0, 0}};
     self.vertices[2] = (SenceVertex){{1, 1, 0}, {1, 1}};
     self.vertices[3] = (SenceVertex){{1, -1, 0}, {1, 0}};


     //6、頂點(diǎn)緩沖區(qū)
     GLuint vBuffer;
     glGenBuffers(1, &vBuffer);
     glBindBuffer(GL_ARRAY_BUFFER, vBuffer);
     glBufferData(GL_ARRAY_BUFFER, sizeof(SenceVertex) * 4, self.vertices, GL_STATIC_DRAW);
     //保存崭歧,退出的時(shí)候才釋放
     self.vertexBuffer = vBuffer;


     //7、解壓圖片撞牢,獲取紋理id
     
     GLuint textureID = [self createTextureWithImageName:@"mark.jpeg"];
     //設(shè)置紋理ID
     self.textureID = textureID;
     
     
     
 }
 - (GLuint)createTextureWithImageName:(NSString *)imageName{
     
     
     //1率碾、拿到圖片路徑
     //這么寫的好處是,圖片不做緩存處理
     NSString *imagePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:imageName];
    
     UIImage *image = [UIImage imageWithContentsOfFile:imagePath];
     
     
     //2屋彪、解壓圖片
     CGImageRef imageRef = [image CGImage];
     
     //3所宰、判斷圖片有沒有拿到
     if (!imageRef) {
         NSLog(@"load image faile");
         exit(1);
     }
     
     //4、創(chuàng)建上下文
     //1)獲取寬高
     GLuint width = (GLuint)CGImageGetWidth(imageRef);
     GLuint height = (GLuint)CGImageGetHeight(imageRef);
     //2)拿到圖片大小
     void *imageData = malloc(width * height * 4);
     //3)拿到圖片的顏色
     CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
     //3)上下文
     /*
     參數(shù)1:data,指向要渲染的繪制圖像的內(nèi)存地址
     參數(shù)2:width,bitmap的寬度畜挥,單位為像素
     參數(shù)3:height,bitmap的高度仔粥,單位為像素
     參數(shù)4:bitPerComponent,內(nèi)存中像素的每個(gè)組件的位數(shù),比如32位RGBA,就設(shè)置為8
     參數(shù)5:bytesPerRow,bitmap的沒一行的內(nèi)存所占的比特?cái)?shù)
     參數(shù)6:colorSpace,bitmap上使用的顏色空間  kCGImageAlphaPremultipliedLast:RGBA
     */
     CGContextRef imageContext = CGBitmapContextCreate(imageData, width, height, 8, width * 4, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);

     //5躯泰、重新繪制
     CGRect rect = CGRectMake(0, 0, width, height);
     //1)翻轉(zhuǎn)策略
     CGContextTranslateCTM(imageContext, 0, height);
     CGContextScaleCTM(imageContext, 1.0f, -1.0f);
     
     //2)對(duì)圖片重新繪制谭羔,得到一張新的解壓后的位圖
     CGContextDrawImage(imageContext, rect, imageRef);
     
     //3)用完之后釋放
     CGColorSpaceRelease(colorSpace);
     CGContextRelease(imageContext);
     
     //6、設(shè)置紋理 (因?yàn)檫@個(gè)方法需要我們返回一個(gè)id麦向,就不穿默認(rèn)0了瘟裸,還是寫一遍代碼吧)
     GLuint textureId;
     glGenTextures(1, &textureId);
     glBindTexture(GL_TEXTURE_2D, textureId);
     
     //7、載入紋理數(shù)據(jù)
     /*
     參數(shù)1:紋理模式诵竭,GL_TEXTURE_1D话告、GL_TEXTURE_2D、GL_TEXTURE_3D
     參數(shù)2:加載的層次卵慰,一般設(shè)置為0
     參數(shù)3:紋理的顏色值GL_RGBA
     參數(shù)4:寬
     參數(shù)5:高
     參數(shù)6:border沙郭,邊界寬度
     參數(shù)7:format
     參數(shù)8:type
     參數(shù)9:紋理數(shù)據(jù)
     */
     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData);

     
     //8、設(shè)置紋理屬性
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
     
     //9裳朋、重新綁定一下(用的時(shí)候就綁定準(zhǔn)沒錯(cuò))
     glBindTexture(GL_TEXTURE_2D, textureId);
     //10病线、釋放
     free(imageData);
         
     return textureId;
 }

 #pragma mark - 2.2
 - (void)setUpDrawShaderWith:(NSString *)shaderName{
     
     //1. 編譯頂點(diǎn)著色器/片元著色器
     GLuint vertexShader = [self compileShaderWithName:shaderName type:GL_VERTEX_SHADER];
     GLuint fragmentShader = [self compileShaderWithName:shaderName type:GL_FRAGMENT_SHADER];
     
     
     //2、
     //1)創(chuàng)建一個(gè)program
     GLuint program = glCreateProgram();
     
     //2)附著
     glAttachShader(program, vertexShader);
     glAttachShader(program, fragmentShader);
     
 //    glDeleteShader(vertexShader);
 //    glDeleteShader(fragmentShader);
     
     //3)link
     glLinkProgram(program);
     
     //4)檢查
     GLint linkStatus;
     glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
     if (linkStatus == GL_FALSE) {
         GLchar messages[256];
         glGetProgramInfoLog(program, sizeof(messages), 0, &messages[0]);
         NSString *messageString = [NSString stringWithUTF8String:messages];
         NSLog(@"program鏈接失斣倥ぁ:%@", messageString);
         exit(1);
     }
  
     
     //3氧苍、use
     glUseProgram(program);
     
     //4、傳遞數(shù)據(jù)
     //1)先拿到通道名
     //頂點(diǎn)坐標(biāo)
     GLuint positionSlot = glGetAttribLocation(program, "Position");
     //紋理坐標(biāo)
     GLuint textureCoordsSlot = glGetAttribLocation(program, "TextureCoords");
     //紋理
     GLuint textureSlot = glGetUniformLocation(program, "Texture");
  
     
     //2)傳頂點(diǎn)坐標(biāo)
     glEnableVertexAttribArray(positionSlot);
     glVertexAttribPointer(positionSlot, 3, GL_FLOAT, GL_FALSE, sizeof(SenceVertex), NULL + offsetof(SenceVertex, positionCoord));
     
     //3)傳紋理坐標(biāo)
     glEnableVertexAttribArray(textureCoordsSlot);
     glVertexAttribPointer(textureCoordsSlot, 2, GL_FLOAT, GL_FALSE, sizeof(SenceVertex), NULL + offsetof(SenceVertex, textureCoord));
     
     //4) 傳紋理
     glActiveTexture(GL_TEXTURE0);
     glBindTexture(GL_TEXTURE_2D, self.textureID);
     
     glUniform1i(textureSlot, 0);
     
     
     //5.保存program,界面銷毀則釋放
     self.program = program;
 }

 //編譯shader代碼
 - (GLuint)compileShaderWithName:(NSString *)name type:(GLenum)shaderType {
     
     //1泛范、獲得shader路徑
     NSString *shaderPath = [[NSBundle mainBundle] pathForResource:name ofType:shaderType == GL_VERTEX_SHADER ? @"vsh" : @"fsh"];
     
     //2让虐、轉(zhuǎn)換成c語言字符串
     NSError *error;
     NSString *shaderString = [NSString stringWithContentsOfFile:shaderPath encoding:NSUTF8StringEncoding error:&error];
     if (!shaderString) {
         NSLog( @"讀取shader失敗");
         exit(1);
     }
     
 //    const GLchar* source = (GLchar*)[pathString UTF8String];
     
     const char *shaderStringUTF8 = [shaderString UTF8String];
     int shaderStringLength = (int)[shaderString length];
     
     
     //3、創(chuàng)建shader對(duì)象
     GLuint shader = glCreateShader(shaderType);
     
     //4罢荡、附著
     glShaderSource(shader, 1, &shaderStringUTF8, &shaderStringLength);
     
     //5赡突、編譯
     glCompileShader(shader);
     
     //6、檢查編譯
     GLint compileStatus;
     glGetShaderiv(shader, GL_COMPILE_STATUS, &compileStatus);
     
     if (compileStatus == GL_FALSE) {
         GLchar messages[256];
         glGetShaderInfoLog(shader, sizeof(messages), 0, &messages[0]);
         NSString *messageString = [NSString stringWithUTF8String:messages];
         NSLog(@"shader編譯失斍浴:%@", messageString);
         exit(1);
     }
     
     return shader;
 }
 #pragma mark - 3
 - (void)startRender {

     //1.因?yàn)闀?huì)重復(fù)調(diào)用惭缰,嚴(yán)謹(jǐn)一點(diǎn),先判斷一下
     if (self.displayLink) {
         
         [self.displayLink invalidate];
         self.displayLink = nil;
     }
     //2. 設(shè)置displayLink 的方法
     self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(timeAction)];
     
     //3.將displayLink 添加到runloop 運(yùn)行循環(huán)
     [self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
 }

  
 - (void)timeAction{
     
    
     //使用program
     glUseProgram(self.program);
     //綁定buffer
     glBindBuffer(GL_ARRAY_BUFFER, self.vertexBuffer);
      
     // 清除畫布
     glClear(GL_COLOR_BUFFER_BIT);
     glClearColor(1, 1, 1, 1);
     
     // 重繪
     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
     //渲染到屏幕上
     [self.context presentRenderbuffer:GL_RENDERBUFFER];
     
 }


 //獲取渲染緩存區(qū)的寬
 - (GLint)drawableWidth {
     GLint backingWidth;
     glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &backingWidth);
     return backingWidth;
 }
 //獲取渲染緩存區(qū)的高
 - (GLint)drawableHeight {
     GLint backingHeight;
     glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &backingHeight);
     return backingHeight;
 }
 @end



源碼鏈接:

鏈接:https://pan.baidu.com/s/13G0wUQKyjf4BqwYhjJ7bbA 密碼:sbfd

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末笼才,一起剝皮案震驚了整個(gè)濱河市漱受,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌骡送,老刑警劉巖昂羡,帶你破解...
    沈念sama閱讀 206,482評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異摔踱,居然都是意外死亡虐先,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門派敷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蛹批,“玉大人撰洗,你說我怎么就攤上這事「郑” “怎么了差导?”我有些...
    開封第一講書人閱讀 152,762評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)甸赃。 經(jīng)常有香客問我柿汛,道長(zhǎng),這世上最難降的妖魔是什么埠对? 我笑而不...
    開封第一講書人閱讀 55,273評(píng)論 1 279
  • 正文 為了忘掉前任络断,我火速辦了婚禮,結(jié)果婚禮上项玛,老公的妹妹穿的比我還像新娘貌笨。我一直安慰自己,他們只是感情好襟沮,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評(píng)論 5 373
  • 文/花漫 我一把揭開白布锥惋。 她就那樣靜靜地躺著,像睡著了一般开伏。 火紅的嫁衣襯著肌膚如雪膀跌。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,046評(píng)論 1 285
  • 那天固灵,我揣著相機(jī)與錄音捅伤,去河邊找鬼。 笑死巫玻,一個(gè)胖子當(dāng)著我的面吹牛丛忆,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播仍秤,決...
    沈念sama閱讀 38,351評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼熄诡,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了诗力?” 一聲冷哼從身側(cè)響起凰浮,我...
    開封第一講書人閱讀 36,988評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎苇本,沒想到半個(gè)月后导坟,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,476評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡圈澈,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評(píng)論 2 324
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了尘惧。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片康栈。...
    茶點(diǎn)故事閱讀 38,064評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出啥么,到底是詐尸還是另有隱情登舞,我是刑警寧澤,帶...
    沈念sama閱讀 33,712評(píng)論 4 323
  • 正文 年R本政府宣布悬荣,位于F島的核電站菠秒,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏氯迂。R本人自食惡果不足惜践叠,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望嚼蚀。 院中可真熱鬧禁灼,春花似錦、人聲如沸轿曙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽导帝。三九已至守谓,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間您单,已是汗流浹背斋荞。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留睹限,地道東北人譬猫。 一個(gè)月前我還...
    沈念sama閱讀 45,511評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像羡疗,于是被迫代替她去往敵國(guó)和親染服。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評(píng)論 2 345