?賽克效果就是把圖?的?個(gè)相當(dāng)??的區(qū)域?同?個(gè)點(diǎn)的顏?來表示.可以認(rèn)為是?規(guī)模的降低圖像的分辨率,?讓圖像的?些細(xì)節(jié)隱藏起來明肮。
其實(shí)整份代碼與前一篇《分屏濾鏡》相比耳标,也就片元著色器部分發(fā)生了改變厉亏。
說白了伪嫁,濾鏡就是片元著色器像素值的處理哮奇。
一饭耳、效果圖
二串述、流程圖
三、著色器中的主要區(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)值法:處理后的圖片比較逼真
- 浮點(diǎn)算法:
Gray = R * 0.3 + G * 0.59 + B * 0.11
【RGB的權(quán)重總和為1】 - 整數(shù)?法:
Gray = ( R * 30 + G * 59 + B * 11) / 100
【RGB的權(quán)重總和為100】 - 移位?法:
Gray = ( R * 76 + G * 151 + B* 28) >> 8
- 平均值法:
Gray = ( R + G + B ) / 3
【處理后的圖片比較柔和】 - 僅取綠?:
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è)顏色填充,整張圖片看下來就模糊了尚猿。
步驟:
- 先把原圖按我們?cè)O(shè)置的比例放大
- 根據(jù)比例計(jì)算每一個(gè)馬賽克的大小窝稿,獲得一個(gè)區(qū)域
- 把馬賽克區(qū)域根據(jù)比例縮小,就拿到了原紋理圖片中的位置
- 給這一個(gè)區(qū)域填充顏色
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è)六邊形同色塊的馬賽克就形成了鹰霍。
看幾行幾列,是為了計(jì)算對(duì)應(yīng)的兩個(gè)中心點(diǎn)茵乱,方便后面取值茂洒。
如何計(jì)算某一個(gè)像素點(diǎn)在這四種矩形中的具體坐標(biāo)呢?
然后瓶竭,計(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è)正三角形摹菠。
- 計(jì)算某一個(gè)像素點(diǎn)和中心點(diǎn)直接的夾角
- 求出6個(gè)正三角形的中心點(diǎn)
- 判斷這個(gè)夾角屬于哪個(gè)正三角形,就取哪個(gè)正三角形的顏色值
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