簡單的OpenGL繪圖

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

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