解决GLSL渲染图片倒置问题
2020-08-04 本文已影响0人
iOSer_jia
在之前使用GLSL渲染图片时,发现会发生图片倒置的现象,本文将使用5种方案分别解决这一问题。
产生原因
我们知道,OpenGL ES纹理坐标是从左下角为原点,而iOS设备屏幕是从左上角开始的,这就导致了本来应该在左下角的像素点跑到了左上角,如下图所示。
![](https://img.haomeiwen.com/i1818006/c4855b3b11630ed1.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,
};
效果
最终实现效果为:
![](https://img.haomeiwen.com/i1818006/59c04eeef98e8c0d.png)