效果

image.png
案例思路

image.png
核心代碼
1.創(chuàng)建圖層
-(id)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super initWithCoder:aDecoder]) {
//初始化CAEAGLLayer
CAEAGLLayer *eagLayer = (CAEAGLLayer *)self.layer;
//設(shè)置透明度
eagLayer.opaque = YES;
//設(shè)置eaglLayer描述屬性
/*
1.kEAGLDrawablePropertyRetainedBacking
表示繪圖表面顯示后,是否保留其內(nèi)容,通過(guò)一個(gè)NSNumber 包裝一個(gè)bool值。如果是NO,表示
顯示內(nèi)容后,不能依賴(lài)于相同的內(nèi)容;如果是YES,表示顯示內(nèi)容后不變,一般只有在需要內(nèi)容保存不變的情況下才使用YES,設(shè)置為YES,會(huì)導(dǎo)致性能降低,內(nèi)存使用量降低。一般設(shè)置為NO。
2.kEAGLDrawablePropertyColorFormat
表示繪制表面的內(nèi)部顏色緩存區(qū)格式
*/
eagLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:NO],kEAGLDrawablePropertyRetainedBacking,kEAGLColorFormatRGBA8,kEAGLDrawablePropertyColorFormat, nil];
//初始化上下文
context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
//判斷是否開(kāi)辟成功以及設(shè)置到當(dāng)前的context
if (!context ||![EAGLContext setCurrentContext:context]) {
return nil;
}
//設(shè)置視圖比例因子
/*
比例因子決定視圖中的內(nèi)容如何從邏輯坐標(biāo)空間(以點(diǎn)測(cè)量)映射到設(shè)備坐標(biāo)空間(以像素為單位)。此值通常為1或2。更高比例的因素表明視圖中的每一個(gè)點(diǎn)由底層的多個(gè)像素表示。例如,如果縮放因子為2,并且視圖框大小為50×50點(diǎn),則用于顯示內(nèi)容的位圖的大小為100×100像素。
*/
self.contentScaleFactor = [UIScreen mainScreen].scale;
//是否需要清屏,默認(rèn)等于YES
needsErase = YES;
}
return self;
}
2.判斷是否要初始化
//CCView layOut
-(void)layoutSubviews
{
[EAGLContext setCurrentContext:context];
//判斷是否初始化
if (!initialized) {
//如果沒(méi)有初始化則對(duì)OpenGL初始化
initialized = [self initGL];
}else
{
//如果已經(jīng)初始化則調(diào)整layer
[self resizeFromLayer:(CAEAGLLayer *)self.layer];
}
//清除第一次分配
if (needsErase) {
[self erase];
needsErase = NO;
}
}
3.初始化并繪制加油字樣
-(BOOL)initGL
{
//生成表示一個(gè)幀緩存對(duì)象和顏色渲染
glGenFramebuffers(1, &viewFrameBuffer);
glGenRenderbuffers(1, &viewRenderBuffer);
//綁定viewFrameBuffer 和viewRenderBuffer
glBindFramebuffer(GL_FRAMEBUFFER, viewFrameBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, viewRenderBuffer);
//綁定一個(gè)Drawable對(duì)象儲(chǔ)存到一個(gè)OpenGL ES渲染緩存對(duì)象
/*
創(chuàng)建一個(gè)渲染,可以呈現(xiàn)到屏幕上,你將渲染然后分配共享存儲(chǔ)通過(guò)調(diào)用此方法。這個(gè)方法的調(diào)用替換通常給glrenderbufferstorage。緩存的存儲(chǔ)分配了這個(gè)方法以后可以顯示一個(gè)回調(diào)presentrenderbuffer:
為繪制緩沖區(qū)分配存儲(chǔ)區(qū),此處將CAEAGLLayer的繪制存儲(chǔ)區(qū)作為繪制緩沖區(qū)的存儲(chǔ)區(qū)
參數(shù)1:OpenGL ES的結(jié)合點(diǎn)為當(dāng)前綁定的渲染。這個(gè)參數(shù)的值必須gl_renderbuffer(或gl_renderbuffer_oes在OpenGL ES 1.1語(yǔ)境)
參數(shù)2:對(duì)象管理數(shù)據(jù)存儲(chǔ)區(qū)中的渲染。在iOS中,這個(gè)參數(shù)的值必須是一個(gè)CAEAGLLayer對(duì)象
*/
[context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(id<EAGLDrawable>)self.layer];
//將viewRenderBuffer 綁定到GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER 附著在GL_RENDERBUFFER的顏色附著點(diǎn)0點(diǎn)上,放在viewRenderBuffer
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, viewRenderBuffer);
//獲取繪制緩存區(qū)的像素寬度 -- 將繪制緩存區(qū)像素寬度存儲(chǔ)在backingWidth
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &backingWidth);
//獲取繪制緩存區(qū)的像素高度--將繪制緩存區(qū)像素高度存儲(chǔ)在backingHeight
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &backingHeight);
//檢查GL_FRAMEBUFFER緩存區(qū)狀態(tài)
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
NSLog(@"Make comlete frameBuffer Object faild %X !", glCheckFramebufferStatus(GL_FRAMEBUFFER));
return NO;
}
//設(shè)置視口
glViewport(0, 0, backingWidth, backingHeight);
//創(chuàng)建頂點(diǎn)緩沖對(duì)象來(lái)保存我們的對(duì)象
glGenBuffers(1, &vboId);
//加載畫(huà)筆紋理
brushTexture = [self textureFromName:@"Particle.png"];
//加載著色器
[self setupShaders];
//點(diǎn)模糊效果通過(guò)開(kāi)啟混合模式,并設(shè)置混合函數(shù)
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); //參考之前的混合函數(shù)表文章
//回放錄制的路徑,這是“加油”
NSString *path = [[NSBundle mainBundle]pathForResource:@"abc" ofType:@"string"];
NSString *str = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
//開(kāi)辟數(shù)據(jù)空間-可變的
CCArr = [NSMutableArray array];
//根據(jù)abc.string文件,將繪制點(diǎn)的數(shù)據(jù)。json解析到數(shù)組
NSArray *jsonArray = [NSJSONSerialization JSONObjectWithData:[str dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingAllowFragments error:nil];
for (NSDictionary *dict in jsonArray) {
CCPoint *point = [CCPoint new];
point.mX = [dict objectForKey:@"mX"];
point.mY = [dict objectForKey:@"mY"];
//將CCPoint 對(duì)象添加到CCArr數(shù)組
[CCArr addObject:point];
}
//調(diào)用繪制方法:繪制abc.string 繪制的加油字樣,延時(shí)5秒繪制
[self performSelector:@selector(paint) withObject:nil afterDelay:0.5];
return YES;
}
//繪制加油字樣
-(void)paint
{
//從0開(kāi)始遍歷頂點(diǎn),步長(zhǎng)為2
//p1,開(kāi)始點(diǎn),p2,結(jié)束點(diǎn)
for (int i = 0; i < CCArr.count - 1; i+=2) {
//從CCArr數(shù)組中讀取頂點(diǎn)cp1,cp2
CCPoint *cp1 = CCArr[i];
CCPoint *cp2 = CCArr[i + 1];
//將CCPoint對(duì)象 -> CGPoint對(duì)象
CGPoint p1,p2;
p1.x = cp1.mX.floatValue;
p2.x = cp2.mX.floatValue;
p1.y = cp1.mY.floatValue;
p2.y = cp2.mY.floatValue;
//在用戶(hù)觸摸的地方繪制上的線條
[self renderLineFromPoint:p1 toPoint:p2];
}
}
4.手寫(xiě)頂點(diǎn)著色器程序point.vsh
//頂點(diǎn)
attribute vec4 inVertex;
//矩陣
uniform mat4 MVP;
//點(diǎn)的大小
uniform float pointSize;
//點(diǎn)的顏色
uniform lowp vec4 vertexColor;
//輸出顏色
varying lowp vec4 color;
void main()
{
//頂點(diǎn)計(jì)算 = 矩陣 * 頂點(diǎn)
gl_Position = MVP * inVertex;
//修改頂點(diǎn)大小
gl_PointSize = pointSize;
// 1 * 3.0;
//將通過(guò)uniform 傳遞進(jìn)來(lái)的顏色,從頂點(diǎn)著色器程序傳遞到片元著色器
color = vertexColor;
}
5.手寫(xiě)片元著色器程序point.fsh
// 獲取紋理
/**
sampler2D中的2D,表示這是一個(gè)2D紋理。我們也可以使用1D/3D或者其他類(lèi)型的采樣器。我們總是
把這個(gè)值設(shè)置為0。來(lái)指示紋理單元0.
*/
uniform sampler2D texture;
//獲取從頂點(diǎn)程序傳遞過(guò)來(lái)的顏色
//lowp.精度
//輸出顏色
varying lowp vec4 color;
void main()
{
//將顏色和紋理組合是相乘
gl_FragColor = color * texture2D(texture, gl_PointCoord);
}
6.加載shader
- (void)setupShaders
{
for (int i = 0; i < NUM_PROGRAMS; i++) {
//讀取頂點(diǎn)著色程序
char *vsrc = readFile(pathForResource(program[i].vert));
char *fsrc = readFile(pathForResource(program[i].frag));
//將char ->NSString 對(duì)象
NSString *vsrcStr = [[NSString alloc] initWithBytes:vsrc length:strlen(vsrc)-1 encoding:NSUTF8StringEncoding];
NSString *fsrcStr = [[NSString alloc] initWithBytes:fsrc length:strlen(fsrc)-1 encoding:NSUTF8StringEncoding];
NSLog(@"vsrc:%@", vsrcStr);
NSLog(@"fsrc:%@", fsrcStr);
//attribute
GLsizei attribCt = 0;
//創(chuàng)建字符串?dāng)?shù)組【1】
GLchar *attribUsed[NUM_ATTRIBS];
GLint attrib[NUM_ATTRIBS];
//attribute變量名稱(chēng)-inVertex(point.vsh)
GLchar *attribName[NUM_ATTRIBS] = {
"inVertex",
};
//uniform 變量名稱(chēng) “mvp”, "pointSize","vertexColor",@"texture"
const GLchar *uniformName[NUM_UNIFORMS] = {
"MVP","pointSize","vertexColor", "texture",
};
//遍歷attribute
for (int j = 0; j < NUM_ATTRIBS; j++) {
//strstr(str1,str2) 函數(shù)用于判斷字符串str2是否是str1的子串。如果是,則該函數(shù)返回str2在str1中首次出現(xiàn)的地址;否則,返回NULL。
//判斷,attribute 變量,是否存在頂點(diǎn)著色器程序中。point.vsh
if (strstr(vsrc, attribName[j])) {
//attribute個(gè)數(shù)
attrib[attribCt] = j;
//使用的attribute的名稱(chēng)
attribUsed[attribCt++] = attribName[j];
}
}
//利用shaderUtil.c封裝好的方法對(duì)programe 進(jìn)行創(chuàng)建、鏈接、生成Programe
/**
GLint glueCreateProgram(const GLchar *vertSource, const GLchar *fragSource,
GLsizei attribNameCt, const GLchar **attribNames,
const GLint *attribLocations,
GLsizei uniformNameCt, const GLchar **uniformNames,
GLint *uniformLocations,
GLuint *program)
*/
/*
參數(shù)1:vsrc,頂點(diǎn)著色器程序
參數(shù)2:fsrc,片元著色器程序
參數(shù)3:attribute變量個(gè)數(shù)
參數(shù)4:attribute變量名稱(chēng)
參數(shù)5:當(dāng)前attribute位置
參數(shù)6: uniform個(gè)數(shù)
參數(shù)7:uniform名字
參數(shù)8:program的uniform地址
參數(shù)9:program程序地址
*/
glueCreateProgram(vsrc, fsrc, attribCt, (const GLchar **)&attribUsed[0], attrib, NUM_UNIFORMS, &uniformName[0], program[i].uniform, &program[i].id);
//釋放vsrc, fsrc 指針
free(vsrc);
free(fsrc);
//設(shè)置常數(shù)
//當(dāng)前i == 0
if (i == PROGRAM_POINT) {
//使用proram program[0].id 等價(jià)以往的例子GLuint program;
glUseProgram(program[PROGRAM_POINT].id);
//為當(dāng)前程序?qū)ο笾付╱niform變量值
/*
為當(dāng)前程序?qū)ο笾付╱niform變量MVP賦值
void glUniform1f(GLint location, GLfloat v0);
參數(shù)1:location,指明要更改的uniform變量的位置 MVP
參數(shù)2:v0,指明在指定的uniform變量中要使用的新值
program[0].uniform[3] = 0
等價(jià)于,vsh頂點(diǎn)著色器程序中的uniform變量,MVP = 0;
其實(shí)簡(jiǎn)單理解就是做了一次初始化,清空這個(gè)mat4矩陣
*/
glUniform1i(program[PROGRAM_POINT].uniform[UNIFORM_TEXTURE], 0);
// 投影矩陣
/*
投影分為正射投影和透視投影,我們可以通過(guò)它來(lái)設(shè)置投影矩陣來(lái)設(shè)置視域,在OpenGL中,默認(rèn)的投影矩陣是一個(gè)立方體,即x y z 分別是-1.0~1.0的距離,如果超出該區(qū)域,將不會(huì)被顯示
正射投影(orthographic projection):GLKMatrix4MakeOrtho(float left, float righ, float bottom, float top, float nearZ, float farZ),該函數(shù)返回一個(gè)正射投影的矩陣,它定義了一個(gè)由 left、right、bottom、top、near、far 所界定的一個(gè)矩形視域。此時(shí),視點(diǎn)與每個(gè)位置之間的距離對(duì)于投影將毫無(wú)影響。
透視投影(perspective projection):GLKMatrix4MakeFrustum(float left, float right,float bottom, float top, float nearZ, float farZ),該函數(shù)返回一個(gè)透視投影的矩陣,它定義了一個(gè)由 left、right、bottom、top、near、far 所界定的一個(gè)平截頭體(椎體切去頂端之后的形狀)視域。此時(shí),視點(diǎn)與每個(gè)位置之間的距離越遠(yuǎn),對(duì)象越小。
在平面上繪制,只需要使正投影就可以了??!
*/
GLKMatrix4 projectionMatrix = GLKMatrix4MakeOrtho(0, backingWidth, 0, backingHeight, -1, 1);
//模型矩陣,比如你要平移、旋轉(zhuǎn)、縮放,就可以設(shè)置在模型矩陣上
//這里不需要這些變換,則使用單元矩陣即可,相當(dāng)于1 * ? = ?
GLKMatrix4 modelViewMatrix = GLKMatrix4Identity;
//矩陣相乘就2個(gè)矩陣的結(jié)果交給MVPMatrix
GLKMatrix4 MVPMatrix = GLKMatrix4Multiply(projectionMatrix, modelViewMatrix);
/*
void glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value);
功能:為當(dāng)前程序?qū)ο笾付╱niform變量值
參數(shù)1:location 指明要更改的uniform變量的位置 MVP
參數(shù)2:count 指定將要被修改的矩陣的數(shù)量
參數(shù)3:transpose 矩陣的值被載入變量時(shí),是否要對(duì)矩陣進(jìn)行變換,比如轉(zhuǎn)置!
參數(shù)4:value ,指向?qū)⒁糜诟聈niform變量MVP的數(shù)組指針
*/
glUniformMatrix4fv(program[PROGRAM_POINT].uniform[UNIFORM_MVP], 1, GL_FALSE, MVPMatrix.m);
//點(diǎn)的大小 pointSize
/*
為當(dāng)前程序?qū)ο笾付╱niform變量pointSize賦值
program[0].uniform[pointSize] = 紋理寬度/畫(huà)筆比例
*/
glUniform1f(program[PROGRAM_POINT].uniform[UNIFORM_POINT_SIZE], brushTexture.width / kBrushScale);
//筆刷顏色
/*
為當(dāng)前程序?qū)ο笾付╱niform變量vertexColor賦值
program[0].uniform[vertexColor] = 畫(huà)筆顏色
void glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value);
功能:為當(dāng)前程序?qū)ο笾付╱niform變量值
參數(shù)1:location 指明要更改的uniform變量的位置 vertexColor
參數(shù)2:count 指定將要被修改的4分量的數(shù)量
參數(shù)3:value ,指向?qū)⒁糜诟聈niform變量vertexColor的值
*/
glUniform4fv(program[PROGRAM_POINT].uniform[UNIFORM_VERTEX_COLOR], 1, brushColor);
}
}
glError();
}
7.通過(guò)兩點(diǎn)繪制線條
//在用戶(hù)觸摸的地方繪制屏幕上的線條
-(void)renderLineFromPoint:(CGPoint)start toPoint:(CGPoint)end
{
//頂點(diǎn)緩存區(qū)
static GLfloat *vertexBuffer = NULL;
//頂點(diǎn)Max(暫時(shí))
static NSUInteger vertextMax = 64;
//頂點(diǎn)個(gè)數(shù)
NSUInteger vertexCount = 0,count;
//從點(diǎn)到像素轉(zhuǎn)換
//視圖的比例因子
CGFloat scale = self.contentScaleFactor;
//將每個(gè)頂點(diǎn)與scale 因子相乘
start.x *= scale;
start.y *= scale;
end.x *= scale;
end.y *= scale;
//開(kāi)辟數(shù)組緩存區(qū)
if (vertexBuffer == NULL) {
//開(kāi)辟頂點(diǎn)地址空間
vertexBuffer = malloc(vertextMax * 2 * sizeof(GLfloat));
}
/*
通過(guò)把起點(diǎn)到終點(diǎn)的軌跡分解成若干個(gè)點(diǎn),分別來(lái)繪制每個(gè)點(diǎn),從而達(dá)到線的效果
ceilf()向上取整。不是四舍五入,而是判斷后面有小數(shù),去掉小數(shù)部分,整數(shù)部分加1.
如:123.456 => 124
123.001 => 124
*/
//向緩沖區(qū)添加點(diǎn),所以每個(gè)像素都有繪圖點(diǎn)
//求得start 和 end 2點(diǎn)間的距離
float seq = sqrtf((end.x - start.x) * (end.x - start.x) + (end.y - start.y) * (end.y - start.y));
/*
向上取整,求得距離要產(chǎn)生多少個(gè)點(diǎn)?
kBrushPixelStep,畫(huà)筆像素步長(zhǎng)
修改kBrushPixelStep 的值,越大,筆觸越細(xì);越小,筆觸越粗!
*/
NSInteger pointCount = ceilf(seq / kBrushPixelStep);
//比較pointCount 是不是大于1,如果小于1,則count = 1,否則count = pointCount;
count = MAX(pointCount, 1);
for (int i = 0; i < count; i++) {
//判斷如果頂點(diǎn)數(shù) > 設(shè)置頂點(diǎn)Max
if (vertexCount == vertextMax) {
//修改vertexMax 2倍增長(zhǎng)
vertextMax = 2 * vertextMax;
//增加空間開(kāi)辟
vertexBuffer = realloc(vertexBuffer, vertextMax * 2 * sizeof(GLfloat));
}
//修改vertexBuffer數(shù)組的值
//將start 和 end 距離之間,計(jì)算出count個(gè)點(diǎn),并存儲(chǔ)在vertexBuffer數(shù)組中
//x = start.x + (end.x - start.x) * (i/count);
//y = start.y + (end.y - start.y) * (i/count);
//vertexBuffer[0]->x
vertexBuffer[2 * vertexCount + 0] = start.x + (end.x - start.x) * ((CGFloat)i/(GLfloat)count);
//vertextBuffer[1]->y
vertexBuffer[2 * vertexCount + 1] = start.y + (end.y - start.y) * ((CGFloat)i/(CGFloat)count);
NSLog(@"X:%f",vertexBuffer[2 * vertexCount]);
NSLog(@"Y:%f",vertexBuffer[2 * vertexCount + 1]);
// vertexCount 自增1
vertexCount += 1;
}
//加載數(shù)據(jù)到vertexBuffer 對(duì)象中
glBindBuffer(GL_ARRAY_BUFFER, vboId);
//將cpu存儲(chǔ)的頂點(diǎn)數(shù)據(jù)-cpu中 復(fù)制頂點(diǎn)數(shù)據(jù)到緩沖中提供給OpenGL使用
glBufferData(GL_ARRAY_BUFFER, vertexCount * 2 * sizeof(GLfloat), vertexBuffer, GL_DYNAMIC_DRAW);
/**
連接頂點(diǎn)屬性
glEnableVertexAttribArray啟用指定屬性,才可在頂點(diǎn)著色器中訪問(wèn)逐頂點(diǎn)的屬性數(shù)據(jù)
*/
glEnableVertexAttribArray(ATTRIB_VERTEX);
glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, GL_FALSE, 2*sizeof(GLfloat), 0);
//繪制
//使用剛剛創(chuàng)建的program[0].id的program
glUseProgram(program[PROGRAM_POINT].id);
/*
根據(jù)頂點(diǎn)繪制圖形,
參數(shù)1:繪制模型 連接線段,參考視覺(jué)班第一節(jié)課的課件
參數(shù)2:起始點(diǎn),0
參數(shù)3:頂點(diǎn)個(gè)數(shù)
*/
glDrawArrays(GL_POINTS, 0, (int)vertexCount);
//顯示buffer
glBindRenderbuffer(GL_RENDERBUFFER, viewRenderBuffer);
[context presentRenderbuffer:GL_RENDERBUFFER];
}
8.用戶(hù)手勢(shì)處理
#pragma mark -- Touch Click
//點(diǎn)擊屏幕開(kāi)始
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
//獲取繪制的bounds
CGRect bounds = [self bounds];
//獲取當(dāng)前的點(diǎn)擊touch
UITouch * touch = [[event touchesForView:self] anyObject];
//設(shè)置為firstTouch -> yes
firstTouch = YES;
////獲取當(dāng)前點(diǎn)擊的位置信息,x,y
_location = [touch locationInView:self];
//y = height - y
_location.y = bounds.size.height - _location.y;
}
-(void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
CGRect bounds = [self bounds];
UITouch *touch = [[event touchesForView:self] anyObject];
//第一次點(diǎn)擊
if (firstTouch) {
//將firstTouch狀態(tài)改為NO
firstTouch = NO;
////_previousLocation = 獲取上一個(gè)頂點(diǎn)
_previousLocation = [touch previousLocationInView:self];
_previousLocation.y = bounds.size.height - _previousLocation.y;
}else {
_location = [touch locationInView:self];
_location.y = bounds.size.height - _location.y;
_previousLocation = [touch previousLocationInView:self];
_previousLocation.y = bounds.size.height - _previousLocation.y;
}
//獲取_previousLocation 和 _location 2個(gè)頂點(diǎn),繪制成線條
[self renderLineFromPoint:_previousLocation toPoint:_location];
}
-(void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
CGRect bounds = [self bounds];
UITouch *touch = [[event touchesForView:self] anyObject];
//判斷是否第一次觸碰
if (firstTouch) {
firstTouch = NO;
_previousLocation = [touch previousLocationInView:self];
_previousLocation.y = bounds.size.height - _previousLocation.y;
[self renderLineFromPoint:_previousLocation toPoint:_location];
}
}
-(void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
NSLog(@"Touch Cancelled");
}
10.加載畫(huà)筆紋理
// 創(chuàng)建一個(gè)紋理圖片
- (textureInfo_t)textureFromName:(NSString *)name
{
CGImageRef brushImage;
CGContextRef brushContext;
GLubyte *brushData;
size_t width,height;
GLuint textId;
textureInfo_t texture;
//首先建立在圖像文件的額數(shù)據(jù)一個(gè)UIImage對(duì)象,然后提取核心圖形圖像
brushImage = [UIImage imageNamed:name].CGImage;
//獲取圖片的寬高
width = CGImageGetWidth(brushImage);
height = CGImageGetHeight(brushImage);
//分配位圖上下文所需的內(nèi)存
/**
參數(shù)一:圖標(biāo)的內(nèi)存大小,寬*高*4,4為rbga
參數(shù)二:存儲(chǔ)類(lèi)型,無(wú)符號(hào)
*/
brushData = (GLubyte *)calloc(width * height * 4 , sizeof(GLubyte));
//使用Core Graphics框架提供的bitmatp創(chuàng)造功能。
/*
CGContextRef CGBitmapContextCreate(
void * data,
size_t width,
size_t height,
size_t bitsPerComponent,
size_t bytesPerRow,
CGColorSpaceRef cg_nullable space,
uint32_t bitmapInfo);
Quartz創(chuàng)建一個(gè)位圖繪制環(huán)境,也就是位圖上下文。
參數(shù)1:data,要渲染的繪制內(nèi)容的地址
參數(shù)2:位圖的寬
參數(shù)3:位圖的高
參數(shù)4:內(nèi)存中像素的每個(gè)組件的位數(shù),比如32位像素格式和RGB顏色空間。一般設(shè)置為8
參數(shù)5:位圖每一行占有比特?cái)?shù)
參數(shù)5:顏色空間,通過(guò)CGImageGetColorSpace(圖片)獲取顏色空間
參數(shù)6:顏色通道,RGBA = kCGImageAlphaPremultipliedLast
*/
brushContext = CGBitmapContextCreate(brushData, width, height, 8, width * 4, CGImageGetColorSpace(brushImage), kCGImageAlphaPremultipliedLast);
//創(chuàng)建完context之后,可以在context上繪制圖片
/*
void CGContextDrawImage(CGContextRef c, CGRect rect,
CGImageRef image);
參數(shù)1:位圖上下文
參數(shù)2:繪制的frame
參數(shù)3:繪制的圖片
*/
CGContextDrawImage(brushContext, CGRectMake(0.0, 0.0f, (CGFloat)width, (CGFloat)height), brushImage);
//接下來(lái)將不需要上下文,因此需要釋放它以避免內(nèi)存泄漏
CGContextRelease(brushContext);
//使用OpenGL ES生成紋理
/*
生成紋理的函數(shù)
glGenTextures (GLsizei n, GLuint* textures)
參數(shù)1:n,生成紋理個(gè)數(shù)
參數(shù)2:存儲(chǔ)紋理索引的第一個(gè)元素指針
*/
glGenTextures(1, &textId);
//綁定紋理名稱(chēng),允許建立一個(gè)綁定到目標(biāo)紋理的有名稱(chēng)的紋理
glBindTexture(GL_TEXTURE_2D, textId);
//設(shè)置紋理參數(shù)使用縮小濾波器和線性濾波器(加權(quán)平均)--設(shè)置紋理屬性
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
//指定2D紋理圖像,為內(nèi)存中的圖像數(shù)據(jù)提供一個(gè)指針。
/*
功能:生成2D紋理
glTexImage2D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid* pixels);
參數(shù)1:target,紋理目標(biāo),因?yàn)槟闶褂玫氖莋lTexImage2D函數(shù),所以必須設(shè)置為GL_TEXTURE_2D
參數(shù)2:level,0,基本圖像級(jí)別
參數(shù)3:internalformat,顏色組件;GL_RGBA,GL_ALPHA,GL_RGBA
參數(shù)4:width,紋理圖像的寬度
參數(shù)5:height,紋理圖像的高度
參數(shù)6:border,紋理邊框的寬度,必須為0
參數(shù)7:format,像素?cái)?shù)據(jù)的顏色格式,可不與internalformat一致,可參考internalformat的值
參數(shù)8:type,像素?cái)?shù)據(jù)類(lèi)型,GL_UNSIGNED_BYTE
參數(shù)9:pixels,內(nèi)存中指向圖像數(shù)據(jù)的指針
*/
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (int)width, (int)height, 0, GL_RGBA, GL_UNSIGNED_BYTE, brushData);
//生成紋理之后,即可釋放brushData數(shù)據(jù)
free(brushData);
//補(bǔ)充自己定義的texture結(jié)構(gòu)體中的內(nèi)容
//紋理
texture.id = textId;
//紋理寬度
texture.width = (int)width;
//紋理高度
texture.height = (int)height;
//返回紋理對(duì)象數(shù)據(jù)
return texture;
}
11.設(shè)置畫(huà)筆顏色
- (void)setBrushColorWithRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue
{
//更新畫(huà)筆顏色 顏色 * 透明度
brushColor[0] = red * kBrushOpacity;
brushColor[1] = green * kBrushOpacity;
brushColor[2] = blue * kBrushOpacity;
brushColor[3] = kBrushOpacity;
NSLog(@"%f,%f,%f,%f",brushColor[0],brushColor[1],brushColor[2],brushColor[3]);
NSLog(@"%f,%f,%f",red,green,blue);
//釋放初始化
if (initialized) {
//使用program[0].id
glUseProgram(program[PROGRAM_POINT].id);
//將顏色值brushColor 傳遞到 vertexColor中
glUniform4fv(program[PROGRAM_POINT].uniform[UNIFORM_VERTEX_COLOR], 1, brushColor);
}
}