zoukankan      html  css  js  c++  java
  • OpenGL 13

    此案例用来处理纹理的拉伸,并对拉伸后图片进行保存。

    拉伸效果:

    一、拉伸案例 - 主流程

    1、加载原图

    2、拉伸区域的滑块处理 -- sliderView

     

    3、图片拉伸绘制

    4、保存图片到本地相册

    二、拉伸,顶点/纹理坐标处理过程

    1、手动指定拉伸区域、选取合适的图元装配方式

    8个顶点,通过方式 GL_LINE_STRIP 连接绘制。--> V2 ~ V5,拉伸区域 --> 拉伸区域高度 = V5.y - V3.y

     

    2、设置纹理宽高比 得到拉伸量

    根据图片实际size计算出纹理高,宽一直不变。

    设置初识纹理高度占控件 LongLegView 高度 的 0.8

    radio = 当前纹理图片的高宽比 * 控件的宽高比 = img.height/img.width * (view.width/view.height);

    纹理高度 textureHeight = textureWidth * radio;

    拉伸量 delta = (newHeight - (endY -  startY)) * textureHeight; --> newHeight: 拉伸后的纹理高度; startY和endY: 当前的拉伸区域上下的纹理值

    3、根据传入的开始结束纹理坐标,计算拉伸后的顶点坐标 [-1 ~ 1]

    textureWidth = 0.8455;

     

    4、换算拉伸后对应的纹理坐标 [0 ~ 1]

    5、记录拉伸后的当前值,绘制

    GLKitView 的 display 方法执行,触发delegate:glkView:drawInRect

    1、准备绘制

    2、清空缓冲区

    3、顶点数据、纹理数据的绑定传递 bind()、glVertexAttribPointer()

    4、开始绘制 draw

    三、主要代码

    1、拉伸后纹理、顶点坐标的计算

    // 获取图片的中间拉伸区域高度: (currentBottom - currentTop)*sliderValue + 0.5;
        CGFloat newHeight = (self.currentBottom - self.currentTop) * ((sender.value) + 0.5);
     1 /**
     2  根据当前控件的尺寸和纹理的尺寸,计算初始纹理、顶点坐标
     3  
     4  @param size 原始纹理尺寸
     5  @param startY 中间区域的开始纵坐标位置 0~1
     6  @param endY 中间区域的结束纵坐标位置 0~1
     7  @param newHeight 新的中间区域的高度
     8  */
     9 - (void)calculateOriginTextureCoordWithTextureSize:(CGSize)size
    10                                             startY:(CGFloat)startY
    11                                               endY:(CGFloat)endY
    12                                          newHeight:(CGFloat)newHeight {
    13     NSLog(@"%f,%f",size.height,size.width);
    14     
    15     // 1. 计算拉伸后的宽高比;
    16     CGFloat ratio = (size.height / size.width) *
    17     (self.bounds.size.width / self.bounds.size.height);
    18     CGFloat rr = self.bounds.size.width / self.bounds.size.height;
    19     // 2. 宽度=纹理本身宽度;
    20     CGFloat textureWidth = self.currentTextureWidth;
    21     // 3. 高度=纹理高度*radio(宽高比)
    22     CGFloat textureHeight = textureWidth * ratio;
    23     
    24     NSLog(@"%f,%f,%f,%f",newHeight,endY,startY,textureHeight);
    25     // 4. 拉伸量 (newHeight - (endY-startY)) * 纹理高度;
    26     CGFloat delta = (newHeight - (endY -  startY)) * textureHeight;
    27     
    28     // 5. 判断纹理高度+拉伸量是否超出最大值1
    29     if (textureHeight + delta >= 1) {
    30         delta = 1 - textureHeight;
    31         newHeight = delta / textureHeight + (endY -  startY);
    32     }
    33     
    34     // 6. 纹理4个角的顶点
    35     // 左上角
    36     GLKVector3 pointLT = {-textureWidth, textureHeight + delta, 0};
    37     // 右上角
    38     GLKVector3 pointRT = {textureWidth, textureHeight + delta, 0};
    39     // 左下角
    40     GLKVector3 pointLB = {-textureWidth, -textureHeight - delta, 0};
    41     // 右下角
    42     GLKVector3 pointRB = {textureWidth, -textureHeight - delta, 0};
    43     
    44     // 中间矩形区域的顶点
    45     CGFloat tempStartYCoord = textureHeight - 2 * textureHeight * startY;
    46     CGFloat tempEndYCoord = textureHeight - 2 * textureHeight * endY;
    47     
    48     CGFloat startYCoord = MIN(tempStartYCoord, textureHeight);
    49     CGFloat endYCoord = MAX(tempEndYCoord, -textureHeight);
    50    
    51     // 中间部分左上角
    52     GLKVector3 centerPointLT = {-textureWidth, startYCoord + delta, 0};
    53     // 中间部分右上角
    54     GLKVector3 centerPointRT = {textureWidth, startYCoord + delta, 0};
    55     // 中间部分左下角
    56     GLKVector3 centerPointLB = {-textureWidth, endYCoord - delta, 0};
    57     // 中间部分右下角
    58     GLKVector3 centerPointRB = {textureWidth, endYCoord - delta, 0};
    59     
    60     // --纹理的上面两个顶点
    61     // 顶点V0的顶点坐标以及纹理坐标;
    62     self.vertices[0].positionCoord = pointRT;
    63     self.vertices[0].textureCoord = GLKVector2Make(1, 1);
    64     
    65     // 顶点V1的顶点坐标以及纹理坐标;
    66     self.vertices[1].positionCoord = pointLT;
    67     self.vertices[1].textureCoord = GLKVector2Make(0, 1);
    68     
    69     // -- 中间区域的4个顶点
    70     //顶点V2的顶点坐标以及纹理坐标;
    71     self.vertices[2].positionCoord = centerPointRT;
    72     self.vertices[2].textureCoord = GLKVector2Make(1, 1 - startY);
    73     
    74     // 顶点V3的顶点坐标以及纹理坐标;
    75     self.vertices[3].positionCoord = centerPointLT;
    76     self.vertices[3].textureCoord = GLKVector2Make(0, 1 - startY);
    77     
    78     // 顶点V4的顶点坐标以及纹理坐标;
    79     self.vertices[4].positionCoord = centerPointRB;
    80     self.vertices[4].textureCoord = GLKVector2Make(1, 1 - endY);
    81     
    82     // 顶点V5的顶点坐标以及纹理坐标;
    83     self.vertices[5].positionCoord = centerPointLB;
    84     self.vertices[5].textureCoord = GLKVector2Make(0, 1 - endY);
    85     
    86     // 纹理的下面两个顶点
    87     // 顶点V6的顶点坐标以及纹理坐标;
    88     self.vertices[6].positionCoord = pointRB;
    89     self.vertices[6].textureCoord = GLKVector2Make(1, 0);
    90     
    91     // 顶点V7的顶点坐标以及纹理坐标;
    92     self.vertices[7].positionCoord = pointLB;
    93     self.vertices[7].textureCoord = GLKVector2Make(0, 0);
    94     
    95     // 保存临时值
    96     self.currentTextureStartY = startY;
    97     self.currentTextureEndY = endY;
    98     self.currentNewHeight = newHeight;
    99 }

    2、图片保存至相册

     1 // 从帧缓存区中获取纹理图片文件,获取当前的渲染结果
     2 - (UIImage *)createResult {
     3 
     4     // 1. 根据屏幕上显示结果, 重新获取顶点/纹理坐标
     5     [self resetTextureWithOriginWidth:self.currentImageSize.width
     6                          originHeight:self.currentImageSize.height
     7                                  topY:self.currentTextureStartY
     8                               bottomY:self.currentTextureEndY
     9                             newHeight:self.currentNewHeight];
    10     
    11     // 2.绑定帧缓存区;
    12     glBindFramebuffer(GL_FRAMEBUFFER, self.tmpFrameBuffer);
    13     // 3.获取新的图片Size
    14     CGSize imageSize = [self newImageSize];
    15     // 4.从帧缓存中获取拉伸后的图片;
    16     UIImage *image = [self imageFromTextureWithWidth:imageSize.width height:imageSize.height];
    17     // 5. 将帧缓存绑定0,清空;
    18     glBindFramebuffer(GL_FRAMEBUFFER, 0);
    19     
    20     // 6. 返回拉伸后的图片
    21     return image;
    22 }
      1 /**
      2  根据当前屏幕上的显示,重新创建纹理
      3  
      4  @param originWidth 纹理的原始实际宽度
      5  @param originHeight 纹理的原始实际高度
      6  @param topY 0 ~ 1,拉伸区域的顶边的纵坐标
      7  @param bottomY 0 ~ 1,拉伸区域的底边的纵坐标
      8  @param newHeight 0 ~ 1,拉伸区域的新高度
      9  */
     10 - (void)resetTextureWithOriginWidth:(CGFloat)originWidth
     11                        originHeight:(CGFloat)originHeight
     12                                topY:(CGFloat)topY
     13                             bottomY:(CGFloat)bottomY
     14                           newHeight:(CGFloat)newHeight {
     15 
     16     // 1.新的纹理尺寸(新纹理图片的宽高)
     17     GLsizei newTextureWidth = originWidth;
     18     GLsizei newTextureHeight = originHeight * (newHeight - (bottomY - topY)) + originHeight;
     19     
     20     // 2.高度变化百分比
     21     CGFloat heightScale = newTextureHeight / originHeight;
     22     
     23     // 3.在新的纹理坐标下,重新计算 topY、bottomY
     24     CGFloat newTopY = topY / heightScale;
     25     CGFloat newBottomY = (topY + newHeight) / heightScale;
     26     
     27     // 4.创建顶点数组与纹理数组(逻辑与calculateOriginTextureCoordWithTextureSize 中关于纹理坐标以及顶点坐标逻辑是一样的)
     28     SenceVertex *tmpVertices = malloc(sizeof(SenceVertex) * kVerticesCount);
     29     tmpVertices[0] = (SenceVertex){{-1, 1, 0}, {0, 1}};
     30     tmpVertices[1] = (SenceVertex){{1, 1, 0}, {1, 1}};
     31     tmpVertices[2] = (SenceVertex){{-1, -2 * newTopY + 1, 0}, {0, 1 - topY}};
     32     tmpVertices[3] = (SenceVertex){{1, -2 * newTopY + 1, 0}, {1, 1 - topY}};
     33     tmpVertices[4] = (SenceVertex){{-1, -2 * newBottomY + 1, 0}, {0, 1 - bottomY}};
     34     tmpVertices[5] = (SenceVertex){{1, -2 * newBottomY + 1, 0}, {1, 1 - bottomY}};
     35     tmpVertices[6] = (SenceVertex){{-1, -1, 0}, {0, 0}};
     36     tmpVertices[7] = (SenceVertex){{1, -1, 0}, {1, 0}};
     37     
     38     
     39     /// 下面开始渲染到纹理的流程
     40     
     41     // 1. 生成帧缓存区;
     42     GLuint frameBuffer;
     43     GLuint texture;
     44     // glGenFramebuffers 生成帧缓存区对象名称;
     45     glGenFramebuffers(1, &frameBuffer);
     46     // glBindFramebuffer 绑定一个帧缓存区对象;
     47     glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
     48     
     49     // 2. 生成纹理ID,绑定纹理;
     50     // glGenTextures 生成纹理ID
     51     glGenTextures(1, &texture);
     52     // glBindTexture 将一个纹理绑定到纹理目标上;
     53     glBindTexture(GL_TEXTURE_2D, texture);
     54     // glTexImage2D 指定一个二维纹理图像;
     55     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, newTextureWidth, newTextureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
     56     
     57     // 3. 设置纹理相关参数
     58     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
     59     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
     60     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
     61     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
     62     
     63     // 4. 将纹理图像加载到帧缓存区对象上;
     64     /*
     65      glFramebufferTexture2D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level)
     66      target: 指定帧缓冲目标,符合常量必须是GL_FRAMEBUFFER;
     67      attachment: 指定附着纹理对象的附着点GL_COLOR_ATTACHMENT0
     68      textarget: 指定纹理目标, 符合常量:GL_TEXTURE_2D
     69      teture: 指定要附加图像的纹理对象;
     70      level: 指定要附加的纹理图像的mipmap级别,该级别必须为0。
     71      */
     72     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
     73     
     74     // 5. 设置视口尺寸
     75     glViewport(0, 0, newTextureWidth, newTextureHeight);
     76     
     77     // 6. 获取着色器程序
     78     GLuint program = [LongLegHelper programWithShaderName:@"spring"];
     79     glUseProgram(program);
     80     
     81     // 7. 获取参数ID
     82     GLuint positionSlot = glGetAttribLocation(program, "Position");
     83     GLuint textureSlot = glGetUniformLocation(program, "Texture");
     84     GLuint textureCoordsSlot = glGetAttribLocation(program, "TextureCoords");
     85     
     86     // 8. 传值
     87     glActiveTexture(GL_TEXTURE0);
     88     glBindTexture(GL_TEXTURE_2D, self.baseEffect.texture2d0.name);
     89     glUniform1i(textureSlot, 0);
     90     
     91     // 9.初始化缓存区
     92     LongLegVertexAttribArrayBuffer *vbo = [[LongLegVertexAttribArrayBuffer alloc] initWithAttribStride:sizeof(SenceVertex) numberOfVertices:kVerticesCount data:tmpVertices usage:GL_STATIC_DRAW];
     93     
     94     // 10.准备绘制,将纹理/顶点坐标传递进去;
     95     [vbo prepareToDrawWithAttrib:positionSlot numberOfCoordinates:3 attribOffset:offsetof(SenceVertex, positionCoord) shouldEnable:YES];
     96     [vbo prepareToDrawWithAttrib:textureCoordsSlot numberOfCoordinates:2 attribOffset:offsetof(SenceVertex, textureCoord) shouldEnable:YES];
     97     
     98     // 11. 绘制
     99     [vbo drawArrayWithMode:GL_TRIANGLE_STRIP startVertexIndex:0 numberOfVertices:kVerticesCount];
    100     
    101     // 12.解绑缓存
    102     glBindFramebuffer(GL_FRAMEBUFFER, 0);
    103     // 13.释放顶点数组
    104     free(tmpVertices);
    105     
    106     // 14.保存临时的纹理对象/帧缓存区对象;
    107     self.tmpTexture = texture;
    108     self.tmpFrameBuffer = frameBuffer;
    109 }
     1 // 返回某个纹理对应的 UIImage,调用前先绑定对应的帧缓存
     2 - (UIImage *)imageFromTextureWithWidth:(int)width height:(int)height {
     3     
     4     // 1.绑定帧缓存区;
     5     glBindFramebuffer(GL_FRAMEBUFFER, self.tmpFrameBuffer);
     6     
     7     // 2.将帧缓存区内的图片纹理绘制到图片上;
     8     int size = width * height * 4;
     9     GLubyte *buffer = malloc(size);
    10     
    11     /*
    12      
    13      glReadPixels (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid* pixels);
    14      @功能: 读取像素(理解为将已经绘制好的像素,从显存中读取到内存中;)
    15      @参数解读:
    16      参数x,y,width,height: xy坐标以及读取的宽高;
    17      参数format: 颜色格式; GL_RGBA;
    18      参数type: 读取到的内容保存到内存所用的格式;GL_UNSIGNED_BYTE 会把数据保存为GLubyte类型;
    19      参数pixels: 指针,像素数据读取后, 将会保存到该指针指向的地址内存中;
    20      
    21      注意: pixels指针,必须保证该地址有足够的可以使用的空间, 以容纳读取的像素数据; 例如一副256 * 256的图像,如果读取RGBA 数据, 且每个数据保存在GLUbyte. 总大小就是 256 * 256 * 4 = 262144字节, 即256M;
    22      int size = width * height * 4;
    23      GLubyte *buffer = malloc(size);
    24      */
    25     glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
    26     
    27     // 使用data和size 数组来访问buffer数据;
    28     /*
    29      CGDataProviderRef CGDataProviderCreateWithData(void *info, const void *data, size_t size, CGDataProviderReleaseDataCallback releaseData);
    30      @功能: 新的数据类型, 方便访问二进制数据;
    31      @参数:
    32      参数info: 指向任何类型数据的指针, 或者为Null;
    33      参数data: 数据存储的地址,buffer
    34      参数size: buffer的数据大小;
    35      参数releaseData: 释放的回调,默认为空;
    36      
    37      */
    38     CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, buffer, size, NULL);
    39     // 每个组件的位数;
    40     int bitsPerComponent = 8;
    41     // 像素占用的比特数4 * 8 = 32;
    42     int bitsPerPixel = 32;
    43     // 每一行的字节数
    44     int bytesPerRow = 4 * width;
    45     // 颜色空间格式;
    46     CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
    47     // 位图图形的组件信息 - 默认的
    48     CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;
    49     // 颜色映射
    50     CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;
    51     
    52     // 3.将帧缓存区里像素点绘制到一张图片上;
    53     /*
    54      CGImageCreate(size_t width, size_t height,size_t bitsPerComponent, size_t bitsPerPixel, size_t bytesPerRow,CGColorSpaceRef space, CGBitmapInfo bitmapInfo, CGDataProviderRef provider,const CGFloat decode[], bool shouldInterpolate,CGColorRenderingIntent intent);
    55      @功能:根据你提供的数据创建一张位图;
    56      注意:size_t 定义的是一个可移植的单位,在64位机器上为8字节,在32位机器上是4字节;
    57      参数 图片的宽度像素;
    58      参数height: 图片的高度像素;
    59      参数bitsPerComponent: 每个颜色组件所占用的位数, 比如R占用8位;
    60      参数bitsPerPixel: 每个颜色的比特数, 如果是RGBA则是32位, 4 * 8 = 32位;
    61      参数bytesPerRow :每一行占用的字节数;
    62      参数space:颜色空间模式,CGColorSpaceCreateDeviceRGB
    63      参数bitmapInfo:kCGBitmapByteOrderDefault 位图像素布局;
    64      参数provider: 图片数据源提供者, 在CGDataProviderCreateWithData ,将buffer 转为 provider 对象;
    65      参数decode: 解码渲染数组, 默认NULL
    66      参数shouldInterpolate: 是否抗锯齿;
    67      参数intent: 图片相关参数;kCGRenderingIntentDefault
    68      
    69      */
    70     CGImageRef imageRef = CGImageCreate(width, height, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpaceRef, bitmapInfo, provider, NULL, NO, renderingIntent);
    71     
    72     // 4. 此时的 imageRef 是上下颠倒的,调用 CG 的方法重新绘制一遍,翻转过来
    73     // 创建一个图片context
    74     UIGraphicsBeginImageContext(CGSizeMake(width, height));
    75     CGContextRef context = UIGraphicsGetCurrentContext();
    76     // 将图片绘制上去
    77     CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);
    78     // 从context中获取图片
    79     UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    80     // 结束图片context处理
    81     UIGraphicsEndImageContext();
    82     
    83     // 释放buffer
    84     free(buffer);
    85     // 返回图片
    86     return image;
    87 }

    Demo 地址 

  • 相关阅读:
    多重背包POJ1276不要求恰好装满 poj1014多重背包恰好装满
    哈理工1053完全背包
    求最小公倍数与最大公约数的函数
    Bus Pass ZOJ 2913 BFS 最大中取最小的
    POJ 3624 charm bracelet 01背包 不要求装满
    HavelHakimi定理(判断一个序列是否可图)
    z0j1008Gnome Tetravex
    ZOJ 1136 Multiple BFS 取模 POJ 1465
    01背包 擎天柱 恰好装满 zjut(浙江工业大学OJ) 1355
    zoj 2412 水田灌溉,求连通分支个数
  • 原文地址:https://www.cnblogs.com/zhangzhang-y/p/13549875.html
Copyright © 2011-2022 走看看