音视频OpenGL

解决GLSL渲染图片倒置问题

2020-08-04  本文已影响0人  iOSer_jia

在之前使用GLSL渲染图片时,发现会发生图片倒置的现象,本文将使用5种方案分别解决这一问题。

产生原因

我们知道,OpenGL ES纹理坐标是从左下角为原点,而iOS设备屏幕是从左上角开始的,这就导致了本来应该在左下角的像素点跑到了左上角,如下图所示。

翻转原因.png

显然,我们需要将图片翻转过来,本文提供了5种方案供参考。

1. 顶点着色器接收一个旋转矩阵翻转图片。

显然,我们在使用顶点着色器时,让顶点绕x轴旋转180度就可以达到我们要的结果。
首先,需要改写顶点着色器,接收一个旋转矩阵,并将顶点与矩阵叉乘。

shaderv.vsh中

attribute vec4 position;
attribute vec2 textCoordinate;
uniform mat4 rotateMatrix;

varying lowp vec2 varyTextCoord;

void main()
{
    varyTextCoord = textCoordinate;
  
    vec4 vPos = position;
    vPos = vPos * rotateMatrix;
    gl_Position = vPos;
}

在绘制时传递矩阵:

- (void)rotateTextureImage {
    // 获取矩阵uniform
    GLuint rotateMatrix = glGetUniformLocation(self.myPrograme, "rotateMatrix");
    
    // 获得旋转矩阵
    // 旋转角度
    float radian = M_PI;
    
    float sin = sinf(radian);
    float cos = cosf(radian);
    
    // 围绕x轴旋转
    GLfloat xRotation[16] = {
        1, 0, 0, 0,
        0, cos, -sin, 0,
        0, sin, cos, 0,
        0, 0, 0, 1,
    };
    
    glUniformMatrix4fv(rotateMatrix, 1, GL_FALSE, xRotation);
}

2.在重绘图片获取纹理数据时,将纹理翻转

这种方法更为常见,具体代码如下

- (GLuint)setupTexture:(NSString *)fileName {
    
    // 将uiimage转化为cgimagref
    CGImageRef spriteImage = [UIImage imageNamed:fileName].CGImage;
    if(!spriteImage) {
        NSLog(@"failed to load image %@", fileName);
        exit(1);
    }
    
    // 使用CoreGraphics重绘获取位图数据
    // 读取图片的大小
    size_t width = CGImageGetWidth(spriteImage);
    size_t heigth = CGImageGetHeight(spriteImage);
    
    // 创建空间存放数据
    GLubyte *spriteData = calloc(width*heigth*4, sizeof(GLubyte));
    
    // 创建上下文
    CGContextRef spriteContext = CGBitmapContextCreate(spriteData, width, heigth, 8, width*4, CGImageGetColorSpace(spriteImage), kCGImageAlphaPremultipliedLast);
    
    // 翻转图片
    CGContextTranslateCTM(spriteContext, 0, heigth);
    CGContextScaleCTM(spriteContext, 1.0, -1.0);
    
    // 绘制图片
    CGRect rect = CGRectMake(0, 0, width, heigth);
    CGContextDrawImage(spriteContext, rect, spriteImage);
    
    
    // 释放上下文
    CGContextRelease(spriteContext);
    
    // 绑定纹理到默认id
    glBindBuffer(GL_TEXTURE_2D, 0);
    
    // 设置纹理属性
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    
    // 载入纹理
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (float)width, (float)heigth, 0, GL_RGBA, GL_UNSIGNED_BYTE, spriteData);
    
    // 释放纹理
    free(spriteData);
    
    return 0;
    
}

3.修改片元着色器

我们也可以从片元着色器入手,在执行片元着色器时将纹理对应的t坐标翻转。
shaderf.fsh中

precision highp float;
varying vec2 varyTextCoord;
uniform sampler2D colorMap;

void main() {
    lowp vec4 temp = texture2D(colorMap, vec2(varyTextCoord.x, 1.0-varyTextCoord.y));
    gl_FragColor = temp;
}

纹理坐标是从0~1,1-varyTextCoord.y正好等于翻转对应的值

4.修改顶点着色器

除了修改片元着色器,我们也可以在顶点着色器传递纹理坐标给片元着色器时,将顶点坐标做一次上下翻转,让顶点坐标与纹理坐标的对应关系改变。

shderv.vsh中

attribute vec4 position;
attribute vec2 textCoordinate;

varying vec2 varyTextCoord;

void main() {
//    varyTextCoord = textCoordinate;
    
    varyTextCoord = vec2(textCoordinate.x, 1.0-textCoordinate.y);
    gl_Position = position;
}

5.直接修改顶点坐标与纹理坐标对应关系

当然我们也可以直接改变几何图形的顶点和纹理的对应关系,实现纹理的翻转。

对应关系如下:

GLfloat attArr[] = {
        -1.0, -0.3, 0.0,  0.0, 1.0,
        1.0, -0.3, 0.0,  1.0, 1.0,
        1.0, 0.3, 0.0,  1.0, 0.0,
        -1.0, 0.3, 0.0,  0.0, 0.0,
    };

效果

最终实现效果为:

最终效果
上一篇 下一篇

猜你喜欢

热点阅读