OpenGL ES

第十三节—关于GLSL中纹理反转的策略

2020-09-23  本文已影响0人  L_Ares

本文为L_Ares个人写作,包括图片皆为个人亲自操作,如需转载请表明原文出处。

第十节—GLSL初探中,已经完成了一个最简单的使用GLSL将图片当作纹理,渲染到屏幕上的流程,但是渲染的结果是倒转的,当时也说明了原因,因为纹理坐标是以左下角为(0,0)点,而iOS中,UIKit是以左上角为原点,这使得纹理坐标遵从了GLKit的坐标布局,把左上角变成了原点,那么如果想要正常的渲染图片,就需要把图片翻转,本节就来探讨一些可以翻转图片的策略。

一、旋转矩阵

在OpenGL中,我已经写过,关于旋转,平移,缩放等位移技能,都是由顶点着色器(VertexShader)来完成的,那么如果要设置旋转矩阵,则必然是在顶点着色器(VertexShader)中定义旋转矩阵变量,并在Client(客户端)也即是我们的业务代码中将旋转矩阵传输到顶点着色器中(VertexShader)。直接View中要实现的逻辑,这个方法要在Program的link完成之后,在glDrawArrays之前实现。

- (void)rotateTextreImage
{
 
    //角度要转换成弧度,旋转180度是正的
    double radius = 180 * (M_PI / 180.f);
    
    //根据苹果官方文档给出的旋转矩阵的设置,我们还需要sin和cos值来完成图片围绕Z轴的旋转
    double mySin = sin(radius);
    double myCos = cos(radius);
    
    //设置旋转矩阵 4 * 4,但是这里要注意,苹果官方文档提供的是数学逻辑上的矩阵设置,
    //而这里,是OpenGL ES,OpenGL ES和数学逻辑上的矩阵是转置的关系,所以这里是转置后的旋转矩阵
    GLfloat rotateMatrix[16] = {
        
        myCos,-mySin,0,0,
        mySin,myCos,0,0,
        0,0,1,0,
        0,0,0,1
        
    };
    
    //设置缩放矩阵,将x轴的正反向调换
    GLfloat scaleMatrix[16] = {
        
        -1,0,0,0,
        0,1,0,0,
        0,0,1,0,
        0,0,0,1
        
    };

    //获取旋转矩阵在vetexShader中的位置信息
    GLuint rotateMatrixLocation = glGetUniformLocation(self.nProgram, "rotateMatrix");
    
    //获取缩放矩阵在vertexShader中的位置信息
    GLuint scaleMatrixLocation = glGetUniformLocation(self.nProgram, "scaleMatrix");
    
    //设置好了旋转矩阵,就可以传输到顶点着色器中
    //利用glUniformMatrix4fv (GLint location, GLsizei count,
    //                       GLboolean transpose,const GLfloat* value)
    //参数:
    //(1). location: 着色器中参数或者说变量的位置信息
    //(2). 要传几个4*4矩阵
    //(3). 是否需要转置,因为我们定义的矩阵已经是按照列定义的了,就写GL_FALSE,
    //     如果矩阵是数组实现且按行定义的,那就GL_TRUE
    //(4). 要传输的旋转矩阵变量
    glUniformMatrix4fv(rotateMatrixLocation, 1, GL_FALSE, rotateMatrix);
    
    //把缩放矩阵传入顶点着色器
    glUniformMatrix4fv(scaleMatrixLocation, 1, GL_FALSE, scaleMatrix);
    
}

VertexShader中代码修改如下:

shaderv.vsh中的代码,这里定义nPosvec4的时候,并没有设置精度,就是因为顶点着色器的浮点型有预设的默认highp

attribute vec4 position;
attribute vec2 textCoordinate;
uniform mat4 rotateMatrix;
uniform mat4 scaleMatrix;
varying lowp vec2 varyTextCoord;

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

二、修改纹理坐标和顶点坐标的映射关系

说的明白点,就是把顶点数组里面的纹理坐标,重新和顶点一一对应。直接贴修改完成后的顶点坐标数组:

GLfloat vertexArr[] = {

        0.5f,-0.5f,0.f,   1.f,1.f,
        -0.5f,0.5f,0.f,   0.f,0.f,
        -0.5f,-0.5f,0.f,  0.f,1.f,

        0.5f,0.5f,0.f,    1.f,0.f,
        -0.5f,0.5f,0.f,   0.f,0.f,
        0.5f,-0.5f,0.f,   1.f,1.f

    };

其他的地方都不用修改。也算是一劳永逸了,毕竟顶点和纹理坐标的映射都被改了。方法也很简单,就是需要自己找清楚顶点和纹理坐标的对应关系,一旦写错了,那就会显示出现问题。

三、图片解压缩的时候,完成图片翻转。

利用CoreGraphic,在设置纹理信息的时候,利用上下文解压完成。
代码要放在CGContextDrawImage之前。

//先向Y轴正方向移动一个图形高度的距离,这时候,图形的底边已经变成原来图形位置顶边了
    CGContextTranslateCTM(spriteContext, 0.f, rect.size.height);
//这时候把Y轴正方向向下,则图片也随之向下,那么远来是反过来的,这次又反过来一次,负负得正。
    CGContextScaleCTM(spriteContext, 1.f, -1.f);

不要修改最开始的vertexShader的代码和fragmentShader的代码。

四、修改片元着色器

思路就是把纹理坐标的Y值翻转,因为纹理坐标的取值范围是[0,1],所以用1减去原来的纹理坐标的y值,就相当于把y轴翻转了。

修改片元着色器的话,其他的就都不要修改了,这就可以完成翻转了。

gl_FragColor = texture2D(colorMap, vec2(varyTextCoord.x,1.0 - varyTextCoord.y));

五、修改顶点着色器

思路和片元着色器一模一样,毕竟片元着色器中的顶点坐标还是我们利用varying通道从顶点着色器传过来的。修改的就是传输过去之前,就把值改了。

varyTextCoord = vec2(textCoordinate.x, 1.0 - textCoordinate.y);

五种方法对比来说,各有优劣,就第四和第五来说,个人更建议使用修改顶点着色器,毕竟正常情况下,顶点的数量一定远远小于像素的数量,所以执行次数也一定小于片元着色器。虽然代码都是着色器代码,执行是在GPU上的,但是能节省一点是一点。

上一篇 下一篇

猜你喜欢

热点阅读