最近研究了OpenGL的一些繪圖方面的東西,這里給大家分享一下做的一個小demo,順帶自己復(fù)習(xí)下。
功能主要是在繪制好的圖片上進(jìn)行點擊,將規(guī)定的范圍內(nèi)的色值改為灰色。就是一個畫筆的功能實現(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è)置為一個特殊的Layer
*/
+ (Class)layerClass {
return [CAEAGLLayer class];
}
/**
* 設(shè)置layer為不透明狀態(tài),因為缺省的話,CALayer是透明的。而透明層對性能的負(fù)荷很大,特別是OpengGL的層。
*/
- (void)setupLayer {
_eaglLayer = (CAEAGLLayer *)self.layer;
_eaglLayer.opaque = YES;
// 設(shè)置描繪屬性,在這里設(shè)置維持渲染內(nèi)容以及顏色格式
// 為了保存層中用到的OpenGL ES的幀緩存類型的信息
//這段代碼是告訴Core Animation要試圖保留任何以前繪制的圖像留作以后重用
//雙緩存機制:單緩存機制是在渲染前必須要清空畫面然后再進(jìn)行渲染,這樣在重繪的過程中會不斷的閃爍。雙緩存機制,是在后臺先將需要繪制的畫面繪制好然后再顯示繪制出來
_eaglLayer.drawableProperties = @{
kEAGLDrawablePropertyRetainedBacking: [NSNumber numberWithBool:YES],
kEAGLDrawablePropertyColorFormat: kEAGLColorFormatRGBA8
};
}
/**
* EAGLContext管理所有通過OpenGL進(jìn)行draw的信息,創(chuàng)建一個context并聲明用的哪個版本
*/
- (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的一個對象,用于存放渲染過的圖像
*/
- (void)setupRenderBuffer {
//調(diào)用函數(shù)來創(chuàng)建一個新的render buffer索引.這里返回一個唯一的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)建一個幀緩沖區(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成一個完整的Program
GLuint programHandle = glCreateProgram();
glAttachShader(programHandle, vertexShader);
glAttachShader(programHandle, fragmentShader);
glLinkProgram(programHandle);
// 輸出錯誤信息
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傳入變量的指針。以后可以通過這個寫指針來使用。調(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中查找某個文件
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)建一個代表shader的OpenGL對象。這時你必須告訴OpenGL,你想創(chuàng)建的是frament shader 還是 vertex shader.所以便有了這個參數(shù):shaderTypeType
GLuint shaderHandle = glCreateShader(shaderType);
//讓OpenGL獲取到這個shader的源代碼,把NSString轉(zhuǎn)換成C-string
const char *shaderStringUTF8 = [shaderString UTF8String];
int shaderStringLength = [shaderString length];
glShaderSource(shaderHandle, 1, &shaderStringUTF8, &shaderStringLength);
//運行時編譯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ū)時使用的目標(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);
}
/**
* 獲取圖片里面的像素數(shù)據(jù)
*/
- (GLuint)setupTexture:(NSString *)imageName{
//初始化一個UIImage對象,然后獲得它的CGImage屬性
//圖片規(guī)格有限制 只能用2次方的大小的圖
CGImageRef spriteImage = [UIImage imageNamed:imageName].CGImage;
if (!spriteImage) {
NSLog(@"load image failed");
exit(1);
}
//獲取image的寬度和高度然后手動分配空間 width*height*4個字節(jié)的數(shù)據(jù)空間
//空間*4的原因是,我們在調(diào)用方法來繪制圖片數(shù)據(jù)時,我們要為red,green,blue和alpha通道,每個通道要準(zhǔn)備一個字節(jié)
//每個通道準(zhǔn)備一個字節(jié)的原因,因為要用CoreGraphics來建立繪圖上下文。而CGBitmapContextCreate函數(shù)里面的第4個參數(shù)指定的就是每個通道要采用幾位來表現(xiàn),我們只用8位,所以是一個字節(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在一個指定的矩形區(qū)域內(nèi)來繪制這些圖像
CGContextDrawImage(spriteContext, CGRectMake(0, 0, width, height), spriteImage);
//完成繪制要釋放
CGContextRelease(spriteContext);
//把像素信息發(fā)送給OpenGL,首先調(diào)用glGenTextures來創(chuàng)建一個紋理對象,并且得到一個唯一的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(這個參數(shù)的意思是,當(dāng)我們繪制遠(yuǎn)距離的對象的時候,我們會把紋理縮?。┖虶L_NEAREST(這個函數(shù)的意思是,當(dāng)繪制頂點的時候,選擇最臨近的紋理像素)。
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
//最后一步,把像素中的數(shù)據(jù)發(fā)送給OpenGL,通過調(diào)用glTexImage2D.當(dāng)你調(diào)用這個函數(shù)的時候,你需要指定像素格式。這里我們指定的是GL_RGBA和GL_UNSIGNED_BYTE.它的意思是,紅綠藍(lán)alpha道具都有,并且他們占用的空間是1個字節(jié),也就是每個通道8位。
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (int)width, (int)height, 0, GL_RGBA, GL_UNSIGNED_BYTE, spriteData);
//已經(jīng)把圖片數(shù)據(jù)傳送給了OpenGL,所以可以把這個釋放掉
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的兩個輸入?yún)?shù)配置兩個合適的值。
*
* @param _positionSlot 聲明這個屬性的名稱
* @param 3 定義這個屬性由多少個值組成,定點是3個,顏色是4個
* @param GL_FLOAT 聲明每一個值是什么類型
* @param GL_FALSE
* @param Vertex 描述每個vertex數(shù)據(jù)大小的方式,所以可以簡單的傳入
* sizeof(Vertex)
* 最后一個是數(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);
// 在每個vertex上調(diào)用我們的vertex shader,以及每個像素調(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;
//點擊位置
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)建定點、元素數(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成一個完整的Program
GLuint programHandle = glCreateProgram();
glAttachShader(programHandle, vertexShader);
glAttachShader(programHandle, fragmentShader);
glLinkProgram(programHandle);
// 輸出錯誤信息
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傳入變量的指針。以后可以通過這個寫指針來使用。調(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");
}
/**
* 獲取圖片里面的像素數(shù)據(jù)
*/
- (GLuint)setupTexture2:(NSString *)imageName{
//初始化一個UIImage對象,然后獲得它的CGImage屬性
//圖片規(guī)格有限制 只能用2次方的大小的圖
CGImageRef spriteImage = [UIImage imageNamed:imageName].CGImage;
if (!spriteImage) {
NSLog(@"load image failed");
exit(1);
}
//獲取image的寬度和高度然后手動分配空間 width*height*4個字節(jié)的數(shù)據(jù)空間
//空間*4的原因是,我們在調(diào)用方法來繪制圖片數(shù)據(jù)時,我們要為red,green,blue和alpha通道,每個通道要準(zhǔn)備一個字節(jié)
//每個通道準(zhǔn)備一個字節(jié)的原因,因為要用CoreGraphics來建立繪圖上下文。而CGBitmapContextCreate函數(shù)里面的第4個參數(shù)指定的就是每個通道要采用幾位來表現(xiàn),我們只用8位,所以是一個字節(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在一個指定的矩形區(qū)域內(nèi)來繪制這些圖像
CGContextDrawImage(spriteContext, CGRectMake(0, 0, width, height), spriteImage);
//完成繪制要釋放
CGContextRelease(spriteContext);
//把像素信息發(fā)送給OpenGL,首先調(diào)用glGenTextures來創(chuàng)建一個紋理對象,并且得到一個唯一的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(這個參數(shù)的意思是,當(dāng)我們繪制遠(yuǎn)距離的對象的時候,我們會把紋理縮小)和GL_NEAREST(這個函數(shù)的意思是,當(dāng)繪制頂點的時候,選擇最臨近的紋理像素)。
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
//最后一步,把像素中的數(shù)據(jù)發(fā)送給OpenGL,通過調(diào)用glTexImage2D.當(dāng)你調(diào)用這個函數(shù)的時候,你需要指定像素格式。這里我們指定的是GL_RGBA和GL_UNSIGNED_BYTE.它的意思是,紅綠藍(lán)alpha道具都有,并且他們占用的空間是1個字節(jié),也就是每個通道8位。
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (int)width, (int)height, 0, GL_RGBA, GL_UNSIGNED_BYTE, spriteData);
//已經(jīng)把圖片數(shù)據(jù)傳送給了OpenGL,所以可以把這個釋放掉
free(spriteData);
return texName2;
}
代碼里面注釋都還挺詳細(xì)的,這里主要講幾個點
1、一般來說在第二次繪制,將制定區(qū)域繪制為灰色的時候,繪制出來的是一個矩形,這樣的話連續(xù)的繪制之后會出現(xiàn)非常明顯的鋸齒效果。如果要去掉這個鋸齒效果的話只好是采用混合模式,使用一個內(nèi)部是白色圓形,外部是黑色的圖片進(jìn)行混合,更好的情況是圓形的邊緣有個透明度的漸變。這樣繪制出來的效果就很自然了。
2、在渲染灰色塊的時候,是從外部創(chuàng)建一個貝塞爾曲線,然后將點傳進(jìn)來進(jìn)行繪制。如果是每個點都調(diào)用一次
- (void)renderGray:(NSArray *)pointArr
會產(chǎn)生嚴(yán)重的閃爍,所以處理的時候需要將每一條曲線的點進(jìn)行集中繪制到屏幕上
for (int i = 0; i < pointArr.count; i++)
所以在上面的函數(shù)里面有個這樣的循環(huán)
3、在這里面其實也實現(xiàn)了一個簡單的放大鏡的功能
// 設(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);
在這里面把上一段注釋打開,下一段給注釋掉就是一個圖片局部位置的放大鏡了,其實原理很簡單,就是獲取手指點擊位置范圍的圖片色值,然后在指定繪制位置進(jìn)行繪制,然后把繪制范圍放大。自然就成了放大鏡啦 0.o
4、最后,如果代碼里面有些概念基礎(chǔ)不太懂的,大家可以先試著查查資料,這樣以后也記得清楚些,也更有成就感一些啦。然后,如果還有上面疑惑的地方,還請?zhí)岢鰜泶蠹蚁嗷ビ懻撓隆?br>
5、關(guān)于本文著色器腳本的編寫,還挺簡單的,一個是將原圖繪制出來,一個是把色值規(guī)定成灰色然后繪制出來。挺簡單的,稍微搜索下就能寫了。就不特別貼出來了哈。