最近研究了OpenGL的一些繪圖方面的東西,這里給大家分享一下做的一個(gè)小demo拷获,順帶自己復(fù)習(xí)下律想。
功能主要是在繪制好的圖片上進(jìn)行點(diǎn)擊,將規(guī)定的范圍內(nèi)的色值改為灰色流济。就是一個(gè)畫筆的功能實(shí)現(xiàn)
這一篇锐锣,默認(rèn)大家對OpenGL還是有一定的了解的,一些基礎(chǔ)的東西相對有所了解
#import "OpenGLView.h"
//系統(tǒng)目錄
#import <QuartzCore/QuartzCore.h>
#import <OpenGLES/ES2/gl.h>
#import <OpenGLES/ES2/glext.h>
@interface OpenGLView () {
CAEAGLLayer *_eaglLayer;
EAGLContext *_context;
//參數(shù)索引
GLuint _colorRenderBuffer;
GLuint _positionSlot;
GLuint _texCoordSlot;
GLuint _imageTexture;
GLuint _vertexBuffer;
GLuint _indexBuffer;
GLuint _textureUniform;
//灰色塊參數(shù)索引
GLuint _grayVertexBuffer;
GLuint _grayIndexBuffer;
GLuint _grayPositionSlot;
GLuint _grayTexCoordSlot;
GLuint _grayTransTexCoordSlot;
//混合圖片
GLuint _transparentImageTexture;
GLuint _transparentTextureUniform;
}
@end
typedef struct {
float Position[3];
float TexCoord[2];
} Vertex;
typedef struct {
float grayPosition[3];
float grayTexCoord[2];
float transTexCoord[2];
} GrayVertex;
const Vertex Vertices[] = {
{{1, -1, 0}, {-0, -0}},
{{1, 1, 0}, {-0, -1}},
{{-1, 1, 0}, {-1, -1}},
{{-1, -1, 0},{-1, -0}}
};
const GLubyte Indices[] = {
0, 1, 2,
2, 3, 0
};
const GLubyte GrayIndices[] = {
0, 1, 2,
2, 3, 0
};
@implementation OpenGLView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.isRendGray = NO;
[self setupLayer];
[self setupContext];
[self setupRenderBuffer];
[self setupFrameBuffer];
_imageTexture = [self setupTexture:@"image.jpg"];
_transparentImageTexture = [self setupTexture2:@"magic.png"];
[self compileShaders];
[self setupVBOs];
[self render];
}
return self;
}
/**
* 想要顯示OpenGL內(nèi)容袭灯,你需要把它缺省的layer設(shè)置為一個(gè)特殊的Layer
*/
+ (Class)layerClass {
return [CAEAGLLayer class];
}
/**
* 設(shè)置layer為不透明狀態(tài),因?yàn)槿笔〉脑挻滔拢珻ALayer是透明的绑嘹。而透明層對性能的負(fù)荷很大稽荧,特別是OpengGL的層。
*/
- (void)setupLayer {
_eaglLayer = (CAEAGLLayer *)self.layer;
_eaglLayer.opaque = YES;
// 設(shè)置描繪屬性工腋,在這里設(shè)置維持渲染內(nèi)容以及顏色格式
// 為了保存層中用到的OpenGL ES的幀緩存類型的信息
//這段代碼是告訴Core Animation要試圖保留任何以前繪制的圖像留作以后重用
//雙緩存機(jī)制:單緩存機(jī)制是在渲染前必須要清空畫面然后再進(jìn)行渲染姨丈,這樣在重繪的過程中會不斷的閃爍。雙緩存機(jī)制擅腰,是在后臺先將需要繪制的畫面繪制好然后再顯示繪制出來
_eaglLayer.drawableProperties = @{
kEAGLDrawablePropertyRetainedBacking: [NSNumber numberWithBool:YES],
kEAGLDrawablePropertyColorFormat: kEAGLColorFormatRGBA8
};
}
/**
* EAGLContext管理所有通過OpenGL進(jìn)行draw的信息蟋恬,創(chuàng)建一個(gè)context并聲明用的哪個(gè)版本
*/
- (void)setupContext {
EAGLRenderingAPI api = kEAGLRenderingAPIOpenGLES2;
_context = [[EAGLContext alloc] initWithAPI:api];
if (!_context) {
NSLog(@"Failed to initialize OpenGLES 2.0 context");
return;
}
if (![EAGLContext setCurrentContext:_context]) {
NSLog(@"Failed to set current OpenGL context");
return;
}
}
/**
* 創(chuàng)建渲染緩沖區(qū)render buffer
* render buffer 是OpenGL的一個(gè)對象,用于存放渲染過的圖像
*/
- (void)setupRenderBuffer {
//調(diào)用函數(shù)來創(chuàng)建一個(gè)新的render buffer索引.這里返回一個(gè)唯一的integer來標(biāo)記render buffer
glGenRenderbuffers(1, &_colorRenderBuffer);
//調(diào)用函數(shù)趁冈,告訴OpenGL歼争,我們定義的buffer對象屬于哪一種OpenGL對象
glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderBuffer);
//為render buffer分配空間
[_context renderbufferStorage:GL_RENDERBUFFER fromDrawable:_eaglLayer];
}
/**
* 創(chuàng)建一個(gè)幀緩沖區(qū)frame buffer
*/
- (void)setupFrameBuffer {
GLuint framebuffer;
glGenFramebuffers(1, &framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
//把前面創(chuàng)建的buffer render 依附在frame buffer的GL_COLOR_ATTACHMENT0的位置上
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER, _colorRenderBuffer);
}
- (void)compileShaders {
// 編譯vertex shader 和 fragment shader
GLuint vertexShader = [self compileShader:@"SimpleVertex"
withType:GL_VERTEX_SHADER];
GLuint fragmentShader = [self compileShader:@"SimpleFragment"
withType:GL_FRAGMENT_SHADER];
// 鏈接vertex 和 fragment成一個(gè)完整的Program
GLuint programHandle = glCreateProgram();
glAttachShader(programHandle, vertexShader);
glAttachShader(programHandle, fragmentShader);
glLinkProgram(programHandle);
// 輸出錯(cuò)誤信息
GLint linkSuccess;
glGetProgramiv(programHandle, GL_LINK_STATUS, &linkSuccess);
if (linkSuccess == GL_FALSE) {
GLchar messages[256];
glGetProgramInfoLog(programHandle, sizeof(messages), 0, &messages[0]);
NSString *messageString = [NSString stringWithUTF8String:messages];
NSLog(@"%@", messageString);
return;
}
// 執(zhí)行program
glUseProgram(programHandle);
// 獲取指向vertex shader傳入變量的指針拜马。以后可以通過這個(gè)寫指針來使用。調(diào)用glEnableVertexAttribArray來啟用數(shù)據(jù)
_positionSlot = glGetAttribLocation(programHandle, "Position");
glEnableVertexAttribArray(_positionSlot);
_texCoordSlot = glGetAttribLocation(programHandle, "TexCoordIn");
glEnableVertexAttribArray(_texCoordSlot);
_textureUniform = glGetUniformLocation(programHandle, "Texture");
}
- (GLuint)compileShader:(NSString*)shaderName withType:(GLenum)shaderType {
//在NSBundle中查找某個(gè)文件
NSString *shaderPath = [[NSBundle mainBundle] pathForResource:shaderName
ofType:@"glsl"];
NSError *error;
NSString *shaderString = [NSString stringWithContentsOfFile:shaderPath
encoding:NSUTF8StringEncoding error:&error];
if (!shaderString) {
NSLog(@"Error loading shader: %@", error.localizedDescription);
}
//創(chuàng)建一個(gè)代表shader的OpenGL對象沐绒。這時(shí)你必須告訴OpenGL,你想創(chuàng)建的是frament shader 還是 vertex shader.所以便有了這個(gè)參數(shù):shaderTypeType
GLuint shaderHandle = glCreateShader(shaderType);
//讓OpenGL獲取到這個(gè)shader的源代碼俩莽,把NSString轉(zhuǎn)換成C-string
const char *shaderStringUTF8 = [shaderString UTF8String];
int shaderStringLength = [shaderString length];
glShaderSource(shaderHandle, 1, &shaderStringUTF8, &shaderStringLength);
//運(yùn)行時(shí)編譯shader
glCompileShader(shaderHandle);
// 輸出失敗信息
GLint compileSuccess;
glGetShaderiv(shaderHandle, GL_COMPILE_STATUS, &compileSuccess);
if (compileSuccess == GL_FALSE) {
GLchar messages[256];
glGetShaderInfoLog(shaderHandle, sizeof(messages), 0, &messages[0]);
NSString *messageString = [NSString stringWithUTF8String:messages];
NSLog(@"%@", messageString);
}
return shaderHandle;
}
- (void)setupVBOs {
//創(chuàng)建索引
glGenBuffers(1, &_vertexBuffer);
//在glBufferData之前需要將要使用的緩沖區(qū)綁定
glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
/**
* 把數(shù)據(jù)傳到緩沖區(qū)
*
* @param target 與綁定緩沖區(qū)時(shí)使用的目標(biāo)相同
* @param size 我們將要上傳的數(shù)據(jù)大小,以字節(jié)為單位
* @param data 將要上傳的數(shù)據(jù)本身
* @param usage 告訴OpenGL我們打算如何使用緩沖區(qū)
*/
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertices), Vertices, GL_STATIC_DRAW);
glGenBuffers(1, &_indexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Indices), Indices, GL_STATIC_DRAW);
}
/**
* 獲取圖片里面的像素?cái)?shù)據(jù)
*/
- (GLuint)setupTexture:(NSString *)imageName{
//初始化一個(gè)UIImage對象乔遮,然后獲得它的CGImage屬性
//圖片規(guī)格有限制 只能用2次方的大小的圖
CGImageRef spriteImage = [UIImage imageNamed:imageName].CGImage;
if (!spriteImage) {
NSLog(@"load image failed");
exit(1);
}
//獲取image的寬度和高度然后手動分配空間 width*height*4個(gè)字節(jié)的數(shù)據(jù)空間
//空間*4的原因是扮超,我們在調(diào)用方法來繪制圖片數(shù)據(jù)時(shí),我們要為red,green,blue和alpha通道蹋肮,每個(gè)通道要準(zhǔn)備一個(gè)字節(jié)
//每個(gè)通道準(zhǔn)備一個(gè)字節(jié)的原因出刷,因?yàn)橐肅oreGraphics來建立繪圖上下文。而CGBitmapContextCreate函數(shù)里面的第4個(gè)參數(shù)指定的就是每個(gè)通道要采用幾位來表現(xiàn)坯辩,我們只用8位馁龟,所以是一個(gè)字節(jié)
NSInteger width = 512;
NSInteger height = 512;
GLubyte *spriteData = (GLubyte *)calloc(width * height * 4, sizeof(GLubyte));
CGContextRef spriteContext = CGBitmapContextCreate(spriteData, width, height, 8, width * 4, CGColorSpaceCreateDeviceRGB(), kCGImageAlphaPremultipliedLast);
//告訴Core Graphics在一個(gè)指定的矩形區(qū)域內(nèi)來繪制這些圖像
CGContextDrawImage(spriteContext, CGRectMake(0, 0, width, height), spriteImage);
//完成繪制要釋放
CGContextRelease(spriteContext);
//把像素信息發(fā)送給OpenGL,首先調(diào)用glGenTextures來創(chuàng)建一個(gè)紋理對象,并且得到一個(gè)唯一的ID漆魔,由"name"保存著屁柏。然后,我們調(diào)用glBindTexture來把我們新建的紋理名字加載到當(dāng)前的紋理單元中有送。
GLuint texName;
glGenTextures(1, &texName);
glBindTexture(GL_TEXTURE_2D, texName);
glUniform1i(_textureUniform, 0);
//接下來的步驟是淌喻,為我們的紋理設(shè)置紋理參數(shù),使用glTexParameterf函數(shù)雀摘,裸删。這里我們設(shè)置函數(shù)參數(shù)為GL_TEXTURE_MIN_FILTER(這個(gè)參數(shù)的意思是,當(dāng)我們繪制遠(yuǎn)距離的對象的時(shí)候阵赠,我們會把紋理縮醒乃)和GL_NEAREST(這個(gè)函數(shù)的意思是,當(dāng)繪制頂點(diǎn)的時(shí)候清蚀,選擇最臨近的紋理像素)匕荸。
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
//最后一步,把像素中的數(shù)據(jù)發(fā)送給OpenGL枷邪,通過調(diào)用glTexImage2D.當(dāng)你調(diào)用這個(gè)函數(shù)的時(shí)候榛搔,你需要指定像素格式。這里我們指定的是GL_RGBA和GL_UNSIGNED_BYTE.它的意思是东揣,紅綠藍(lán)alpha道具都有践惑,并且他們占用的空間是1個(gè)字節(jié),也就是每個(gè)通道8位嘶卧。
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (int)width, (int)height, 0, GL_RGBA, GL_UNSIGNED_BYTE, spriteData);
//已經(jīng)把圖片數(shù)據(jù)傳送給了OpenGL,所以可以把這個(gè)釋放掉
free(spriteData);
return texName;
}
- (void)render {
glClearColor(150.0/255.0, 100.0/255.0, 55.0/255.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
// 設(shè)置UIView中渲染的部分
glViewport(0, 0, self.frame.size.width, self.frame.size.height);
glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBuffer);
/**
* 為vertex shader的兩個(gè)輸入?yún)?shù)配置兩個(gè)合適的值尔觉。
*
* @param _positionSlot 聲明這個(gè)屬性的名稱
* @param 3 定義這個(gè)屬性由多少個(gè)值組成,定點(diǎn)是3個(gè)芥吟,顏色是4個(gè)
* @param GL_FLOAT 聲明每一個(gè)值是什么類型
* @param GL_FALSE
* @param Vertex 描述每個(gè)vertex數(shù)據(jù)大小的方式侦铜,所以可以簡單的傳入
* sizeof(Vertex)
* 最后一個(gè)是數(shù)據(jù)結(jié)構(gòu)的偏移量
* @return
*/
glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE,
sizeof(Vertex), 0);
glVertexAttribPointer(_texCoordSlot, 2, GL_FLOAT, GL_FALSE,
sizeof(Vertex), (GLvoid *) (sizeof(float) * 3));
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, _imageTexture);
glUniform1i(_textureUniform, 0);
//開啟混合因子
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_DST_ALPHA);
// 在每個(gè)vertex上調(diào)用我們的vertex shader,以及每個(gè)像素調(diào)用fragment shader专甩,最終畫出我們的矩形
glDrawElements(GL_TRIANGLES, sizeof(Indices)/sizeof(Indices[0]),
GL_UNSIGNED_BYTE, 0);
[_context presentRenderbuffer:GL_RENDERBUFFER];//繪制到渲染緩沖區(qū)
}
#pragma mark - 渲染灰色塊
- (void)renderGray:(NSArray *)pointArr{
@synchronized(self){
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self compileGrayShaders];
// 啟動混合并設(shè)置混合因子
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
});
//色塊邊長
NSInteger graySideLength = 40;
for (int i = 0; i < pointArr.count; i++) {
CGPoint point = CGPointFromString([pointArr objectAtIndex:i]);
CGFloat x = point.x;
CGFloat y = point.y;
//點(diǎn)擊位置
float clickX = 1 - (x - graySideLength / 2) / [UIScreen mainScreen].bounds.size.width;
float clickY = 1 - (y + graySideLength / 2) / [UIScreen mainScreen].bounds.size.height;
//邊長在屏幕中橫豎占比例
float lengthX = graySideLength / [UIScreen mainScreen].bounds.size.width;
float lengthY = graySideLength / [UIScreen mainScreen].bounds.size.height;
const GrayVertex GrayVertexs[] = {
{{1,-1,0} , {-clickX + lengthX,-clickY} , {-0,-0}},
{{1,1,0} , {-clickX + lengthX,-clickY - lengthY} , {-0,-1}},
{{-1,1,0} , {-clickX ,-clickY - lengthY} , {-1,-1}},
{{-1,-1,0}, {-clickX ,-clickY} , {-1,-0}}
};
glBindBuffer(GL_ARRAY_BUFFER, _grayVertexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _grayIndexBuffer);
//構(gòu)建定點(diǎn)、元素?cái)?shù)組VBO
glGenBuffers(1, &_grayVertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, _grayVertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(GrayVertexs), GrayVertexs, GL_STATIC_DRAW);
glGenBuffers(1, &_grayIndexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _grayIndexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GrayIndices), GrayIndices, GL_STATIC_DRAW);
// 設(shè)置UIView中渲染的部分
// glViewport(0,
// [[UIScreen mainScreen]bounds].size.height - 200,
// (int)100,
// (int)100);
glViewport(x - graySideLength / 2,
[[UIScreen mainScreen]bounds].size.height - y - graySideLength / 2,
(int)graySideLength,
(int)graySideLength);
// NSLog(@"%f %f",x - graySideLength / 2,[[UIScreen mainScreen]bounds].size.height - y - graySideLength / 2);
glVertexAttribPointer(_grayPositionSlot, 3, GL_FLOAT, GL_FALSE,
sizeof(GrayVertex), 0);
glVertexAttribPointer(_grayTexCoordSlot, 2, GL_FLOAT, GL_FALSE,
sizeof(GrayVertex), (GLvoid *) (sizeof(float) * 3));
glVertexAttribPointer(_grayTransTexCoordSlot, 2, GL_FLOAT, GL_FALSE, sizeof(GrayVertex), (GLvoid *) (sizeof(float) * 5));
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, _imageTexture);
glUniform1i(_textureUniform, 0);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, _transparentImageTexture);
glUniform1i(_transparentTextureUniform, 1);
glDrawElements(GL_TRIANGLES, sizeof(GrayIndices)/sizeof(GrayIndices[0]),GL_UNSIGNED_BYTE, 0);
}
[_context presentRenderbuffer:GL_RENDERBUFFER];//繪制到渲染緩沖區(qū)
}
}
//重新加載灰色塊著色器
- (void)compileGrayShaders {
// 編譯vertex shader 和 fragment shader
GLuint vertexShader = [self compileShader:@"GrayVertex"
withType:GL_VERTEX_SHADER];
GLuint fragmentShader = [self compileShader:@"GrayFragment"
withType:GL_FRAGMENT_SHADER];
// 鏈接vertex 和 fragment成一個(gè)完整的Program
GLuint programHandle = glCreateProgram();
glAttachShader(programHandle, vertexShader);
glAttachShader(programHandle, fragmentShader);
glLinkProgram(programHandle);
// 輸出錯(cuò)誤信息
GLint linkSuccess;
glGetProgramiv(programHandle, GL_LINK_STATUS, &linkSuccess);
if (linkSuccess == GL_FALSE) {
GLchar messages[256];
glGetProgramInfoLog(programHandle, sizeof(messages), 0, &messages[0]);
NSString *messageString = [NSString stringWithUTF8String:messages];
NSLog(@"%@", messageString);
return;
}
// 執(zhí)行program
glUseProgram(programHandle);
// 獲取指向vertex shader傳入變量的指針钉稍。以后可以通過這個(gè)寫指針來使用配深。調(diào)用glEnableVertexAttribArray來啟用數(shù)據(jù)
_grayPositionSlot = glGetAttribLocation(programHandle, "grayPosition");
glEnableVertexAttribArray(_grayPositionSlot);
_grayTexCoordSlot = glGetAttribLocation(programHandle, "grayTexCoordIn");
glEnableVertexAttribArray(_grayTexCoordSlot);
_grayTransTexCoordSlot = glGetAttribLocation(programHandle, "grayTransTexCoordIn");
glEnableVertexAttribArray(_grayTransTexCoordSlot);
_textureUniform = glGetUniformLocation(programHandle, "grayTexture");
_transparentTextureUniform = glGetUniformLocation(programHandle, "grayTransparentTexture");
}
/**
* 獲取圖片里面的像素?cái)?shù)據(jù)
*/
- (GLuint)setupTexture2:(NSString *)imageName{
//初始化一個(gè)UIImage對象,然后獲得它的CGImage屬性
//圖片規(guī)格有限制 只能用2次方的大小的圖
CGImageRef spriteImage = [UIImage imageNamed:imageName].CGImage;
if (!spriteImage) {
NSLog(@"load image failed");
exit(1);
}
//獲取image的寬度和高度然后手動分配空間 width*height*4個(gè)字節(jié)的數(shù)據(jù)空間
//空間*4的原因是嫁盲,我們在調(diào)用方法來繪制圖片數(shù)據(jù)時(shí)篓叶,我們要為red,green,blue和alpha通道,每個(gè)通道要準(zhǔn)備一個(gè)字節(jié)
//每個(gè)通道準(zhǔn)備一個(gè)字節(jié)的原因羞秤,因?yàn)橐肅oreGraphics來建立繪圖上下文缸托。而CGBitmapContextCreate函數(shù)里面的第4個(gè)參數(shù)指定的就是每個(gè)通道要采用幾位來表現(xiàn),我們只用8位瘾蛋,所以是一個(gè)字節(jié)
// NSInteger width = CGImageGetWidth(spriteImage);
// NSInteger height = CGImageGetHeight(spriteImage);
NSInteger width = 64;
NSInteger height = 64;
GLubyte *spriteData = (GLubyte *)calloc(width * height * 4, sizeof(GLubyte));
CGContextRef spriteContext = CGBitmapContextCreate(spriteData, width, height, 8, width * 4, CGColorSpaceCreateDeviceRGB(), kCGImageAlphaPremultipliedLast);
//告訴Core Graphics在一個(gè)指定的矩形區(qū)域內(nèi)來繪制這些圖像
CGContextDrawImage(spriteContext, CGRectMake(0, 0, width, height), spriteImage);
//完成繪制要釋放
CGContextRelease(spriteContext);
//把像素信息發(fā)送給OpenGL,首先調(diào)用glGenTextures來創(chuàng)建一個(gè)紋理對象俐镐,并且得到一個(gè)唯一的ID,由"name"保存著哺哼。然后佩抹,我們調(diào)用glBindTexture來把我們新建的紋理名字加載到當(dāng)前的紋理單元中。
GLuint texName2;
glGenTextures(2, &texName2);
glBindTexture(GL_TEXTURE_2D, texName2);
glUniform1i(_transparentTextureUniform, 1);
//接下來的步驟是取董,為我們的紋理設(shè)置紋理參數(shù)棍苹,使用glTexParameterf函數(shù),茵汰。這里我們設(shè)置函數(shù)參數(shù)為GL_TEXTURE_MIN_FILTER(這個(gè)參數(shù)的意思是枢里,當(dāng)我們繪制遠(yuǎn)距離的對象的時(shí)候,我們會把紋理縮絮逦纭)和GL_NEAREST(這個(gè)函數(shù)的意思是栏豺,當(dāng)繪制頂點(diǎn)的時(shí)候,選擇最臨近的紋理像素)豆胸。
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
//最后一步奥洼,把像素中的數(shù)據(jù)發(fā)送給OpenGL,通過調(diào)用glTexImage2D.當(dāng)你調(diào)用這個(gè)函數(shù)的時(shí)候晚胡,你需要指定像素格式灵奖。這里我們指定的是GL_RGBA和GL_UNSIGNED_BYTE.它的意思是,紅綠藍(lán)alpha道具都有搬泥,并且他們占用的空間是1個(gè)字節(jié)桑寨,也就是每個(gè)通道8位伏尼。
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (int)width, (int)height, 0, GL_RGBA, GL_UNSIGNED_BYTE, spriteData);
//已經(jīng)把圖片數(shù)據(jù)傳送給了OpenGL,所以可以把這個(gè)釋放掉
free(spriteData);
return texName2;
}
代碼里面注釋都還挺詳細(xì)的忿檩,這里主要講幾個(gè)點(diǎn)
1、一般來說在第二次繪制爆阶,將制定區(qū)域繪制為灰色的時(shí)候燥透,繪制出來的是一個(gè)矩形沙咏,這樣的話連續(xù)的繪制之后會出現(xiàn)非常明顯的鋸齒效果。如果要去掉這個(gè)鋸齒效果的話只好是采用混合模式班套,使用一個(gè)內(nèi)部是白色圓形肢藐,外部是黑色的圖片進(jìn)行混合,更好的情況是圓形的邊緣有個(gè)透明度的漸變吱韭。這樣繪制出來的效果就很自然了吆豹。
2、在渲染灰色塊的時(shí)候理盆,是從外部創(chuàng)建一個(gè)貝塞爾曲線痘煤,然后將點(diǎn)傳進(jìn)來進(jìn)行繪制。如果是每個(gè)點(diǎn)都調(diào)用一次
- (void)renderGray:(NSArray *)pointArr
會產(chǎn)生嚴(yán)重的閃爍猿规,所以處理的時(shí)候需要將每一條曲線的點(diǎn)進(jìn)行集中繪制到屏幕上
for (int i = 0; i < pointArr.count; i++)
所以在上面的函數(shù)里面有個(gè)這樣的循環(huán)
3衷快、在這里面其實(shí)也實(shí)現(xiàn)了一個(gè)簡單的放大鏡的功能
// 設(shè)置UIView中渲染的部分
// glViewport(0,
// [[UIScreen mainScreen]bounds].size.height - 200,
// (int)100,
// (int)100);
glViewport(x - graySideLength / 2,
[[UIScreen mainScreen]bounds].size.height - y - graySideLength / 2,
(int)graySideLength,
(int)graySideLength);
在這里面把上一段注釋打開,下一段給注釋掉就是一個(gè)圖片局部位置的放大鏡了姨俩,其實(shí)原理很簡單蘸拔,就是獲取手指點(diǎn)擊位置范圍的圖片色值,然后在指定繪制位置進(jìn)行繪制环葵,然后把繪制范圍放大调窍。自然就成了放大鏡啦 0.o
4、最后张遭,如果代碼里面有些概念基礎(chǔ)不太懂的陨晶,大家可以先試著查查資料,這樣以后也記得清楚些帝璧,也更有成就感一些啦先誉。然后,如果還有上面疑惑的地方的烁,還請?zhí)岢鰜泶蠹蚁嗷ビ懻撓隆?br>
5褐耳、關(guān)于本文著色器腳本的編寫,還挺簡單的渴庆,一個(gè)是將原圖繪制出來铃芦,一個(gè)是把色值規(guī)定成灰色然后繪制出來。挺簡單的襟雷,稍微搜索下就能寫了刃滓。就不特別貼出來了哈。