簡單的OpenGL繪圖

最近研究了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ī)定成灰色然后繪制出來。挺簡單的襟雷,稍微搜索下就能寫了刃滓。就不特別貼出來了哈。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末耸弄,一起剝皮案震驚了整個(gè)濱河市咧虎,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌计呈,老刑警劉巖砰诵,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件征唬,死亡現(xiàn)場離奇詭異,居然都是意外死亡茁彭,警方通過查閱死者的電腦和手機(jī)总寒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來理肺,“玉大人摄闸,你說我怎么就攤上這事∶萌” “怎么了贪薪?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長眠副。 經(jīng)常有香客問我画切,道長,這世上最難降的妖魔是什么囱怕? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任霍弹,我火速辦了婚禮,結(jié)果婚禮上娃弓,老公的妹妹穿的比我還像新娘典格。我一直安慰自己,他們只是感情好台丛,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布耍缴。 她就那樣靜靜地躺著,像睡著了一般挽霉。 火紅的嫁衣襯著肌膚如雪防嗡。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天侠坎,我揣著相機(jī)與錄音蚁趁,去河邊找鬼。 笑死实胸,一個(gè)胖子當(dāng)著我的面吹牛他嫡,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播庐完,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼钢属,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了门躯?” 一聲冷哼從身側(cè)響起淆党,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后宁否,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體窒升,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡缀遍,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年慕匠,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片域醇。...
    茶點(diǎn)故事閱讀 38,018評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡台谊,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出譬挚,到底是詐尸還是另有隱情锅铅,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布减宣,位于F島的核電站盐须,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏漆腌。R本人自食惡果不足惜贼邓,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望闷尿。 院中可真熱鬧塑径,春花似錦、人聲如沸填具。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽劳景。三九已至誉简,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間盟广,已是汗流浹背描融。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留衡蚂,地道東北人窿克。 一個(gè)月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像毛甲,于是被迫代替她去往敵國和親年叮。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,502評論 25 707
  • 找個(gè)理由吧 在空虛的工作和腐壞的感情里得以安生
    逃離孤島閱讀 368評論 0 0
  • openssl的配置文件: /etc/pki/tls/openssl.cnf玻募,這個(gè)文件包含了很多關(guān)于CA的配置只损。 ...
    姜餅人_9e7b閱讀 365評論 0 0