iOS视频渲染gles_02
glsl 渲染
参考:
https://www.cnblogs.com/zhoug2020/p/7842808.html
https://www.jianshu.com/p/ee597b2bd399
https://www.jianshu.com/p/02ebf6084ec7
渲染流程如图:


代码如下:
#import <UIKit/UIKit.h>
@interface showView : UIView
@end
1 // 2 // showView.m 3 // gltest2 4 // 2020/5/19. 7 // 8 9 #import <Foundation/Foundation.h> 10 #import "showView.h" 11 #import <OpenGLES/ES2/gl.h> 12 @interface showView() 13 14 @property(nonatomic ,strong)CAEAGLLayer *myEglayer; 15 @property(nonatomic ,strong)EAGLContext *myContext; 16 @property(nonatomic ,assign)GLuint myColorRenderBuff; 17 @property(nonatomic ,assign)GLuint myColorFrameBuff; 18 @property(nonatomic,assign)GLuint myProgram; 19 20 - (void)setupLayer; 21 @end 22 23 //集成UIview,作为函数入口 24 @implementation showView 25 26 //重写layerClass,将View返回的图层从CALayer替换成CAEAGLLayer 27 //如果我们继承了UIView,那我们就可以重写+layerClass方法使得在创建的时候能返回一个不同的图层子类 28 + (Class)layerClass { 29 return [CAEAGLLayer class]; 30 } 31 32 - (void)layoutSubviews { 33 34 [self setupLayer]; 35 [self setupContext]; 36 [self SETRenderAndFrameBuffer]; 37 [self Render]; 38 } 39 40 41 -(void)setupLayer{ 42 43 self.myEglayer = (CAEAGLLayer*) self.layer; 44 //设置放大倍数 45 [self setContentScaleFactor:[[UIScreen mainScreen] scale]]; 46 47 // CALayer 默认是透明的,必须将它设为不透明才能让其可见 48 self.myEglayer.opaque = YES; 49 50 // 设置描绘属性,在这里设置不维持渲染内容以及颜色格式为 RGBA8 51 self.myEglayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys: 52 [NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil]; 53 } 54 55 -(void)setupContext{ 56 57 EAGLContext *context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; 58 if (!context) { 59 NSLog(@"Create context failed!"); 60 return; 61 } 62 if(![EAGLContext setCurrentContext:context]) 63 { 64 NSLog(@"setCurrentContext failed!"); 65 return; 66 } 67 self.myContext = context; 68 } 69 70 -(void)SETRenderAndFrameBuffer 71 { 72 /* 73 buffer分为frame buffer 和 render buffer2个大类。 74 其中frame buffer 相当于render buffer的管理者。 75 frame buffer object即称FBO。 76 render buffer则又可分为3类。colorBuffer、depthBuffer、stencilBuffer。 77 */ 78 //先清空后设置 79 glDeleteBuffers(1, &_myColorRenderBuff); 80 self.myColorRenderBuff = 0; 81 glDeleteBuffers(1, &_myColorFrameBuff); 82 self.myColorFrameBuff =0; 83 84 //再设置---先设置渲染缓冲,在设置帧缓冲 85 GLuint buffer1; 86 glGenRenderbuffers(1, &buffer1);//申请一个缓冲区标识符 **render 87 self.myColorRenderBuff =buffer1; 88 glBindRenderbuffer(GL_RENDERBUFFER, self.myColorRenderBuff);//将标识符绑定到GL_RENDERBUFFER 89 //将可绘制对象drawable object的 CAEAGLLayer的存储绑定到OpenGL ES renderBuffer对象 90 [self.myContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:self.myEglayer]; 91 92 //设置帧缓冲 93 94 glGenFramebuffers(1, &buffer1);//申请一个缓冲区标识符 ** frame 这里申请标志注意是frame 而不是glgenbuffer 95 self.myColorFrameBuff =buffer1; 96 glBindFramebuffer(GL_FRAMEBUFFER, self.myColorFrameBuff); 97 /*生成帧缓存区之后,则需要将renderbuffer跟framebuffer进行绑定, 98 调用glFramebufferRenderbuffer函数进行绑定到对应的附着点上,后面的绘制才能起作用 99 将渲染缓存区myColorRenderBuffer 通过glFramebufferRenderbuffer函数绑定到 GL_COLOR_ATTACHMENT0上。*/ 100 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, self .myColorRenderBuff); 101 102 103 } 104 105 -(void)Render{ 106 107 glClearColor(0, 1.0, 0, 1.0); 108 glClear(GL_COLOR_BUFFER_BIT); 109 110 //1设置视图窗口大小 111 CGFloat scale =[[UIScreen mainScreen] scale]; 112 glViewport(self.frame.origin.x * scale, self.frame.origin.y * scale, self.frame.size.width * scale, 113 self.frame.size.height * scale); 114 115 //着色器 program的设置 116 NSString* vFIle= [[NSBundle mainBundle]pathForResource:@"shaderv" ofType:@"vsh"]; 117 NSString* fFIle= [[NSBundle mainBundle]pathForResource:@"shaderf" ofType:@"fsh"]; 118 //顶点,片元的编译流程:读取文件路径,创建着色器shader对象,将着色器源码附着到s对象上,把着色器源代码编译成目标代码 119 //programe:创建程序,附着着色器,链接程序,使用程序 120 self.myProgram = [self loadShader:vFIle frag:fFIle]; 121 glLinkProgram(self.myProgram); 122 //判断是否连上 123 GLint statusLink; 124 glGetProgramiv(self.myProgram, GL_LINK_STATUS, &statusLink); 125 if(statusLink == GL_FALSE) 126 { 127 GLchar message[512]; 128 glGetProgramInfoLog(self.myProgram, sizeof(message), 0, &message[0]); 129 NSString *messageString = [NSString stringWithUTF8String:message]; 130 NSLog(@"Program Link Error:%@",messageString); 131 return; 132 } 133 NSLog(@"Program Link Success!"); 134 glUseProgram(self.myProgram); 135 136 //加载纹理和VERTEX,进行绘制 137 //设置顶点矩阵 138 GLfloat attrArr[] = { 139 0.5f, -0.5f, -1.0f, 1.0f, 0.0f, 140 -0.5f, 0.5f, -1.0f, 0.0f, 1.0f, 141 -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 142 0.5f, 0.5f, -1.0f, 1.0f, 1.0f, 143 -0.5f, 0.5f, -1.0f, 0.0f, 1.0f, 144 0.5f, -0.5f, -1.0f, 1.0f, 0.0f, 145 }; 146 147 148 //顶点数据拷贝到显存 149 GLuint vrtID; 150 glGenBuffers(1, &vrtID); 151 glBindBuffer(GL_ARRAY_BUFFER, vrtID); 152 glBufferData(GL_ARRAY_BUFFER, sizeof(attrArr), attrArr, GL_DYNAMIC_DRAW); 153 154 //0.将顶点数据通过myProgram传递到顶点着色程序的position 155 //1.glGetAttribLocation,用来获取vertex attribute的入口的. 156 //2.告诉OpenGL ES,通过glEnableVertexAttribArray, 157 //3.最后数据是通过glVertexAttribPointer传递过去的。 158 159 //:第二参数字符串必须和shaderv.vsh中的输入变量:position保持一致 160 GLuint Position = glGetAttribLocation(self.myProgram, "position"); 161 //设置合适的格式从buffer里面读取数据 162 glEnableVertexAttribArray(Position); 163 glVertexAttribPointer(Position, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, NULL); 164 165 166 //处理纹理数据 167 //glGetAttribLocation,用来获取vertex attribute的入口的. 168 //注意:第二参数字符串必须和"shaderv.vsh"中的输入变量:textCoordinate保持一致 169 170 GLuint textcoor = glGetAttribLocation(self.myProgram, "textCoordinate"); 171 glEnableVertexAttribArray(textcoor); 172 glVertexAttribPointer(textcoor, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*5,(float *)NULL + 3); 173 //加载纹理 174 175 [self setupTexture]; 176 //-----------------------------------------------------------------------不旋转这块可以不调用,对应的着色器中的 177 //vPos = vPos * rotateMatrix;这句话就可以不调用了,否则,着色器自动✖️一个未知旋转矩阵,课程会导致纹理超出屏幕范围而无法渲染 178 //获取shader里面的变量,这里记得要在glLinkProgram后面,后面,后面! 179 GLuint rotate = glGetUniformLocation(self.myProgram, "rotateMatrix"); 180 181 float radians = 180 * 3.14159f / 180.0f; 182 float s = sin(radians); 183 float c = cos(radians); 184 185 //z轴旋转矩阵 186 GLfloat zRotation[16] = { // 187 1, 0, 0, 0, // 188 0, c, -s , 0,// 189 0, s , c, 0,// 190 0.0, 0, 0, 1.0// 191 }; 192 193 //设置旋转矩阵 194 glUniformMatrix4fv(rotate, 1, GL_FALSE, (GLfloat *)&zRotation[0]); 195 //----------------------------------------------------------------------- 196 glDrawArrays(GL_TRIANGLES, 0, 6); 197 198 199 200 [self.myContext presentRenderbuffer:GL_RENDERBUFFER]; 201 202 203 204 } 205 206 207 208 -(GLuint)loadShader:(NSString*)vert frag:(NSString*)fert{ 209 210 //临时着色对象 211 212 GLuint verShader,fragShader; 213 214 [self compileShader:&verShader type:GL_VERTEX_SHADER file:vert]; 215 216 [self compileShader:&fragShader type:GL_FRAGMENT_SHADER file:fert]; 217 218 219 220 GLuint programs = glCreateProgram(); 221 222 223 224 glAttachShader(programs, verShader); 225 226 glAttachShader(programs, fragShader); 227 228 229 230 //附着上之后可以删掉了 231 232 glDeleteShader(verShader); 233 234 glDeleteShader(fragShader); 235 236 237 238 return programs; 239 240 } 241 242 //编译着色器 243 244 -(void)compileShader:(GLuint*)shader type:(GLenum)type file:(NSString*)file{ 245 246 247 248 NSString* Content= [NSString stringWithContentsOfFile:file encoding:NSUTF8StringEncoding error:nil]; 249 250 const GLchar* source = (GLchar*)[Content UTF8String]; 251 252 //创建着色器对象,加载源码,编译着色器对象 253 254 *shader =glCreateShader(type); 255 256 glShaderSource(*shader, 1, &source, NULL); 257 258 glCompileShader(*shader); 259 260 261 262 263 264 } 265 266 267 268 - (GLuint)setupTexture{ 269 270 // 1获取图片的CGImageRef 271 272 CGImageRef spriteImage = [UIImage imageNamed:@"for_test"].CGImage; 273 274 if (!spriteImage) { 275 276 NSLog(@"Failed to load image"); 277 278 exit(1); 279 280 } 281 282 283 284 // 2 读取图片的大小 285 286 size_t width = CGImageGetWidth(spriteImage); 287 288 size_t height = CGImageGetHeight(spriteImage); 289 290 291 292 GLubyte * spriteData = (GLubyte *) calloc(width * height * 4, sizeof(GLubyte)); //rgba共4个byte 293 294 295 296 CGContextRef spriteContext = CGBitmapContextCreate(spriteData, width, height, 8, width*4, 297 298 CGImageGetColorSpace(spriteImage), kCGImageAlphaPremultipliedLast); 299 300 301 302 // 3在CGContextRef上绘图 303 304 CGContextDrawImage(spriteContext, CGRectMake(0, 0, width, height), spriteImage); 305 306 307 308 CGContextRelease(spriteContext); 309 //上边的操作相当于对图片进行解压缩 310 311 312 // 4绑定纹理到默认的纹理ID(这里只有一张图片,故而相当于默认于片元着色器里面的colorMap,如果有多张图不可以这么做) 313 314 glBindTexture(GL_TEXTURE_2D, 0); 315 316 317 //纹理维度,线性过滤,环绕模式 318 319 320 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); 321 322 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); 323 324 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 325 326 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 327 328 329 330 float fw = width, fh = height; 331 332 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fw, fh, 0, GL_RGBA, GL_UNSIGNED_BYTE, spriteData); 333 334 335 336 glBindTexture(GL_TEXTURE_2D, 0); 337 338 339 340 free(spriteData); 341 342 return 0; 343 344 } 345 346 347 348 @end 349 350
#import "ViewController.h"
#import"showView.h"
@interface ViewController ()
@property(nonatomic ,strong)showView* showV;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.showV = (showView *)self.view;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
注意修改view的class,否则黑屏崩溃

glsl着色器:
片元着色:fsh
varying lowp vec2 varyTextCoord;
uniform sampler2D colorMap;
void main()
{
gl_FragColor = texture2D(colorMap, varyTextCoord);
}
-------------------------------------------------------------------
顶点着色: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;
}
这里图片旋转的时候的旋转矩阵计算方式如下:


上边代码显示的图片是倒着的,愿意是gl中的坐标和iOS坐标是相反的。解决方式:
1修改纹理坐标和顶点坐标的对应关系:
//6.设置顶点、纹理坐标 //前3个是顶点坐标,后2个是纹理坐标 // GLfloat attrArr[] = // { // 0.5f, -0.5f, -1.0f, 1.0f, 0.0f, // -0.5f, 0.5f, -1.0f, 0.0f, 1.0f, // -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, // // 0.5f, 0.5f, -1.0f, 1.0f, 1.0f, // -0.5f, 0.5f, -1.0f, 0.0f, 1.0f, // 0.5f, -0.5f, -1.0f, 1.0f, 0.0f, // }; GLfloat attrArr[] = { 0.5f, -0.5f, -1.0f, 1.0f, 1.0f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, -0.5f, -0.5f, -1.0f, 0.0f, 1.0f, 0.5f, 0.5f, -1.0f, 1.0f, 0.0f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 0.5f, -0.5f, -1.0f, 1.0f, 1.0f, };
2旋转矩阵翻转图形,不翻转纹理
- 在顶点着色器 shaderv.vsh 中,我们传进一个旋转矩阵 rotateMatrix,因为矩阵传进来就不会再修改了,所以我们用 uniform 来修饰,4行4列的矩阵 mat4。然后用顶点坐标 vPos 乘以这个 rotateMatrix 旋转矩阵,让每一个顶点都应用旋转变化。
3 在解压图片时,将图片源文件翻转
使用下面三行代码,对图片进行了平移缩放操作以后,再重新绘制。
CGContextTranslateCTM(spriteContext, 0, rect.size.height);
CGContextScaleCTM(spriteContext, 1.0, -1.0);
CGContextDrawImage(spriteContext, rect, spriteImage);
4、修改片元着色器中的纹理坐标
这里是修改了片元着色器的代码,x 坐标不动,将 y 坐标改为 1-y,这样就达到了翻转的效果。其他地方代码不用动
varying lowp vec2 varyTextCoord;
uniform sampler2D colorMap;
void main() {
//gl_FragColor = texture2D(colorMap, varyTextCoord);
gl_FragColor = texture2D(colorMap, vec2(varyTextCoord.x,1.0-varyTextCoord.y));
}
5、修改顶点着色器中的纹理坐标
在顶点着色器传入的时候,就翻转。相比上面的片元着色器,执行次数要少很多,有几个顶点执行几次。
attribute vec4 position;
attribute vec2 textCoordinate;
varying lowp vec2 varyTextCoord;
void main() {
varyTextCoord = vec2(textCoordinate.x,1.0-textCoordinate.y);
gl_Position = position;
}
浙公网安备 33010602011771号