7

系统玩转OpenGL+AI,实现各种酷炫视频特效

 3 months ago
source link: https://studygolang.com/articles/36484
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

系统玩转OpenGL+AI,实现各种酷炫视频特效

//xia仔k:百度网盘

OpenGL(Open Graphics Library 开放式图形库)是一个定义了跨编程言语、跨平台的编程接口规格的专业图形程序接口。它可用于三维、二维图形图像的渲染,是一个功用强大,调用便当的底层图形库。在一个 RTC 应用中,因视频渲染或算法处置的需求,OpenGL 是一种高效的渲染或处置完成方式。OpenGL 的高效完成在 Windows、Linux 和 macOS 都有相应支持。此外,挪动平台 iOS 和 Android 都能支持 OpenGL ES(OpenGL for Embedded Systems)。应用 OpenGL 停止跨平台的开发是十分便当的。本文就来分享一下如何应用 OpenGL 完成跨平台应用高效渲染。

由于各种硬件和软件的完成不同,OpenGL 在各平台支持的才能和标准也有差异。

挪动平台支持的 OpenGL ES 是 OpenGL 三维图形 API 的一个子集,它是针对手机等嵌入式设备而设计。因而,需求兼容挪动平台的跨平台开发时,尽量选择 OpenGL ES 支持的 API 。

OpenGL 和 OpenGL ES 的早期版本是针对固定管线的,从2.0开端支持着色言语(shading language)和可编程管线。可编程管线的 API运用更灵敏,支持的特性更丰厚。目前多数硬件都曾经支持可编程管线,而且官方曾经倡议废弃固定管线 API 运用,在高版本中也移除了固定管线 API 。因而倡议运用着色言语和可编程管线 API ,并至少支持2.0版本。假如请求支持的设备或系统都比拟新,能够直接运用3.0或更高版本。

在应用程序调用任何 OpenGL 函数之前,都需求首先创立一个 OpenGL 上下文(Context)。这个 OpenGL 上下文能够了解为一个十分庞大的状态机,保管了 OpenGL 中的各种状态,这是 OpenGL 执行各种指令的根底。某种水平上,OpenGL 函数都是对其上下文这个状态机中对象或状态的操作。由于 OpenGL 上下文是一个宏大的状态机,切换上下文的开支常常比拟大。不同的绘制模块又需求不同的状态和对象管理。因而在应用程序中能够创立多个不同的上下文,在不同线程中运用不同的上下文,上下文之间能够共享缓冲区,纹理等资源。应当尽量防止重复切换上下文和修正大量状态,进步处置效率。

固然 OpenGL 是跨平台的,但各平台的完成和 OpenGL 的环境搭建会有所不同。开发过程中各平台的 OpenGL 上下文(Context)的树立和切换都有所不同。开发者既能够运用如 GLFW、GLUT 等开源框架协助完成 OpenGL上下文环境的树立和管理,也能够运用各平台的原生 API 来完成。本文主要引见运用各平台原生 API 的办法。下面分平台引见各平台的 OpenGL 环境树立与上下文创立。

一、Windows

Windows 平台由于微软的 Direct3D 存在,微软对 OpenGL 的支持并不积极。在大多数微软操作系统中所支持 OpenGL 版本还是 1.0 和 1.1 ,仅支持固定管线 API ,关于现代运用 OpenGL 开发的程序并不友好。不过经过 OpenGL 的 ARB 扩展机制能够让我们访问到 OpenGL 的高级特性接口。Windows OpenGL 完成提供了名为 wglGetProcAddress 的函数,允许我们对指向一个由驱动程序提供的 OpenGL 函数的指针停止检索。不过还有一种更为快捷的办法。经过 GLEW(GL Extension Wrangler)库完成这一繁琐的检索过程。只需求引入头文件 glew.h 和 glew 库并在应用程序的开端调用 glewInit(),之后 OpenGL 1.1 以上的扩展和中心特性的一切函数指针都将自动被设置完成。

glewInit 的调用需求先创立一个 OpenGL 上下文环境,在初始化完成后,再删除这个环境。之后重新创立一个支持 WGL_ARB 扩展的 OpenGL 上下文环境。示例代码如下:

/ 注册窗口类 / WNDCLASSEX wcex; ZeroMemory(&wcex, sizeof(WNDCLASSEX)); wcex.cbSize = sizeof(WNDCLASSEX); wcex.hInstance = GetModuleHandle(NULL); wcex.lpfnWndProc = GLEW_WindowProc; wcex.lpszClassName = kszGlewInitClassName; / 创立窗口以初始化glew / HWND hwnd = CreateWindow(kszGlewInitClassName,L"", (WS_POPUP | WS_DISABLED), CW_USEDEFAULT, CW_USEDEFAULT, 10, 10,NULL, NULL, GetModuleHandle(NULL), NULL); / 设置像素格式 / HDC hdc = GetDC(hwnd); PIXELFORMATDESCRIPTOR pfd; memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR)); pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); pfd.nVersion = 1; pfd.dwFlags=PFD_DOUBLEBUFFER | PFD_SUPPORT_OPENGL |PFD_DRAW_TO_WINDOW; pfd.iPixelType = PFD_TYPE_RGBA; pfd.cColorBits = 24; pfd.cDepthBits = 32; pfd.cStencilBits = 8; pfd.iLayerType = PFD_MAIN_PLANE; int32_t nPixelFormat = ChoosePixelFormat(hdc,&pfd); BOOL bRet = SetPixelFormat(hdc, nPixelFormat,&pfd); / 创立OpenGL上下文环境并设置为当前上下文/ HGLRC hglrc = wglCreateContext(hdc); wglMakeCurrent(hdc, hglrc); / 初始化glew / if (glewInit()) { printf("glewInitfailed\n"); // handleinit fail …… } / 释放当前OpenGL上下文环境/ wglMakeCurrent(NULL, NULL); wglDeleteContext(hglrc); ReleaseDC(hwnd, hdc); DestroyWindow(hwnd); UnregisterClass(kszGlewInitClassName, NULL); / 开端选择真正的格式并创立相应的OpenGL上下文/ / 再次创立窗口 / …… / 设置关怀的重要属性 / int32_t pixAttribs[] = { WGL_DRAW_TO_WINDOW_ARB, GL_TRUE, // 绘制在窗口的像素格式 WGL_SUPPORT_OPENGL_ARB, GL_TRUE, //支持OpenGL渲染 WGL_DOUBLE_BUFFER_ARB, GL_TRUE, // 支持双缓冲 WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB, // 像素格式为RGBA WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB, // 支持硬件加速 WGL_COLOR_BITS_ARB, 32, // 颜色缓冲位深32 WGL_ALPHA_BITS_ARB, 8, // alpha通道位深 8 WGL_DEPTH_BITS_ARB,24, // 深度缓冲位深 24 WGL_STENCIL_BITS_ARB, 8, // 模板缓冲位深 8 WGL_SAMPLE_BUFFERS_ARB, GL_TRUE,// 开启多重采样MSAA WGL_SAMPLES_ARB, 4, // 4倍多重采样MSAA 0}; // 以NULL完毕 / 请求寻觅与我们属性相匹配的最佳格式,并取回一种/ uint32_t pixCount = 0; int32_t nPixelFormat = 0; wglChoosePixelFormatARB(hdc,&pixAttribs[0],NULL,1, &nPixelFormat, &pixCount); if (nPixelFormat != -1) { / 设置选中的最佳格式 / SetPixelFormat(hdc, nPixelFormat, &pfd); / 创立相应的OpenGL上下文环境/ int32_tattribs[] = { WGL_CONTEXT_MAJOR_VERSION_ARB, 3, WGL_CONTEXT_MINOR_VERSION_ARB, 3, WGL_CONTEXT_FLAGSARB, 0, 0}; HGLRCwglrc = wglCreateContextAttribsARB(hdc, 0, attribs); if(wglrc) { wglMakeCurrent(hdc, wglrc); hglrc = wglrc; } else { printf("wglCreateContextAttribsARBfailed\n"); //handle failed …… } } else { printf("ChoosePixelFormatfailed\n"); // handlefailed …… } / 查询并打印OpenGL版本 / const GLubyte *GLVersionString = glGetString(GL_VERSION); int32_t OpenGLVersion[2]; glGetIntegerv(GL_MAJOR_VERSION, &OpenGLVersion[0]); glGetIntegerv(GL_MINOR_VERSION,&OpenGLVersion[1]); printf("OpenGLversion %d.%d\n", OpenGLVersion[0], OpenGLVersion[1]); 二、macOS

macOS 提供了 glut,NSOpenGL,CGL 等接口来创立和管理 OpenGL环境。本文以 NSOpenGL 为例来引见。

NSOpenGLView 是一个轻量级的 NSView 子类封装,便当地提供了OpenGL 绘制环境的创立与管理。在其内部维护了 NSOpenGLPixelFormat 和 NSOpenGLContext 对象,用户能够便当的对其停止访问和管理。

NSOpenGLView 的创立很简单,首先经过设定 NSOpenGLPixel FormatAttribute 属性值创立 NSOpenGLPixelFormat对象,再用 NS OpenGLPixelFormat 创立 NSOpenGLView。

static NSOpenGLPixelFormatAttribute kDefaultAttributes[]= { NSOpenGLPFADoubleBuffer, //双缓冲 NSOpenGLPFADepthSize, 24, //深度缓冲位深 NSOpenGLPFAStencilSize, 8, //模板缓冲位深 NSOpenGLPFAMultisample, //多重采样 NSOpenGLPFASampleBuffers, (NSOpenGLPixelFormatAttribute)1, //多重采样buffer NSOpenGLPFASamples, (NSOpenGLPixelFormatAttribute)4, // 多重采样数 NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core, // OpenGL3.2 0}; NSOpenGLPixelFormat pixelFormat = [[NSOpenGLPixelFormatalloc] initWithAttributes: kDefaultAttributes]; NSOpenGLViewrenderView=[[NSOpenGLView alloc]initWithFrame:frameRect pixelFormat:pixelFormat]; 开发者能够继承 NSOpenGLView 完成子类,并经过完成 -(void)prepareOpenGL ;和 - (void)clearGLContext ;自定义 OpenGL Context 初始化和删除时的一些行为。详细的绘制操作能够在 -(void)drawRect: (NSRect) bounds; 中完成。需求留意的是,为保证Retina 屏的显现效果,能够设置 NSOpenGLView 的属性 wantsBestResolutionOpenGLSurface 为 YES。

  • (void)prepareOpenGL { [super prepareOpenGL]; [self ensureGLContext]; // setupOpenGL resources …… }
  • (void)clearGLContext { [self ensureGLContext]; // cleanup OpenGL resources …… [super clearGLContext]; } -(void) drawRect: (NSRect) bounds { [self ensureGLContext]; // draw OpenGL …… [super drawRect:rect]; }
  • (void)ensureGLContext { NSOpenGLContext*context = [self openGLContext]; if([NSOpenGLContext currentContext] != context) {
      [context makeCurrentContext];
    
    } } 三、iOS

iOS 从2013年9月上线的 iOS 7及同期发布的新设备开端支持 OpenGL ES 3.0,Apple 也是从这个时间点开端发布了64位设备。因而目前市面上除了少量早期的iOS设备外,绝大多数iOS设备都已支持 OpenGL ES 3.0。

和macOS 相似,iOS 也提供了封装好的 UIView——GLKView,开发者能够便当天时用此 View 完成 OpenGL ES 的绘制。此外也提供了 OpenGL ES 的 framebuffer 对象能够完成离屏渲染,或基于 CAEAGLLayer 完成 CALayer 层面的绘制。本文还是以 GLKView 为例停止阐明。

EAGLContext glContext = [[EAGLContext alloc]initWithAPI: kEAGLRenderingAPIOpenGLES3]; if (!glContext) { // OpenGL ES 3 创立失败,创立OpenGL ES 2 glContext=[[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; } GLKViewglkView=[[GLKView alloc] initWithFrame:frameRect context:glContext]; // 配置renderbuffers glkView.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888; glkView.drawableDepthFormat = GLKViewDrawableDepthFormat24; glkView.drawableStencilFormat = GLKViewDrawableStencilFormat8; glkView.drawableMultisample = GLKViewDrawableMultisample4X; glkView.delegate = self; 详细的渲染操作经过GLKViewDelegate的办法-(void)glkView:(GLKView*)view drawInRect:(CGRect)rect;完成

  • (void)glkView:(GLKView*)viewdrawInRect:(CGRect)rect { if ([EAGLContext currentContext] != glContext_) {
      [EAGLContext setCurrentContext:glContext_];
    
    } // draw OpenGL …… } 四、Android

Android 也是从2013年发面的 Android 4.3 开端支持 OpenGL ES 3的,但相比封锁的 iOS 生态,真正支持 OpenGL ES 3 的设备并不容易判别。能够经过创立 OpenGL ES 3 的上下文能否胜利来判别。

Android 也有和 iOS GLKView 相似的封装好的 OpenGL View——GLSurfaceView,开发 者能够直接经过 GLSurfaceView 的办法来创立和管理 OpenGL ES 上下文。也能够基于 SurfaceView 和 EGL 的接口创立本人的 OpenGL 上下文环境和渲染 View。这里引见一种基于 EGL native接口的办法(运用 EGL 的 Java 接口,调用流程也是一样的)。

EGL 是图形渲染 API(如 OpenGL ES)与本地平台窗口系统之间的一层接口,它保证了 OpenGL ES 的平台独立性。提供了创立渲染外表(rendering surface)、图形上下文(graphics context),同步应用程序战争台渲染 API,显现设备访问,渲染配置等管理功用。基于 EGL 的创立上下文环境主要有初始化、选择和设置适宜的配置、创立外表、创立上下文四个步骤。

EGL初始化 EGLBoolean success = EGL_FALSE; EGLint err = 0; EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); if (EGL_NODISPLAY == display) { printf("eglGetDisplay error %d\n",eglGetError()); return; } EGLint major=0, minor=0; if (EGL_FALSE == eglInitialize(display, &major, &minor);) { printf("eglInitialize error %d\n", eglGetError()); return; } 选择和设置适宜的配置 const EGLint configAttribs[] = { EGL_RED_SIZE,8, EGL_GREEN_SIZE,8, EGL_BLUE_SIZE,8, EGL_ALPHA_SIZE, 8, EGL_STENCIL_SIZE, 8, EGL_SAMPLE_BUFFERS, 1, EGL_SAMPLES,4, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_SURFACE_TYPE, EGL_WINDOW_BIT, // 基于窗口的surface EGL_NONE }; EGLConfig config; EGLint numConfigs; if (EGLFALSE == eglChooseConfig(display,configAttribs, &config, 1, &numConfigs)) { // handle andselect other configs …… } 创立外表 EGLint format; if (!eglGetConfigAttrib(display_, config,EGL_NATIVE_VISUAL_ID, &format)) { printf("eglGetConfigAttrib error %d\n", eglGetError()); return; } ANativeWindow *window; // ANativeWindow能够为SurfaceView中获取的Surface对象 ANativeWindowsetBuffersGeometry(window, 0, 0, format); EGLSurface surface = eglCreateWindowSurface(display, config, window, NULL); if (!surface) { printf("eglCreateWindowSurface error %d\n", eglGetError()); return; } 创立上下文 EGLint contextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE }; EGLContext context = eglCreateContext(display, config,EGL_NO_CONTEXT, contextAttribs); if (context == EGL_NO_CONTEXT) { printf("eglCreateContext create OpenGL ES 3 contextfailed\n"); EGLintcontextAttribs2[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGLNONE }; context = eglCreateContext(display,config, NULL, contextAttribs2); if (context_== EGL_NO_CONTEXT) { printf("eglCreateContextcreate OpenGL ES 2 context failed\n"); return; } } eglMakeCurrent(display, surface, surface, context)) { 至此,本文引见了几大主流平台上 OpenGL 原生开发的上下文环境创立与管理。除了 Windows 对 OpenGL 的支持相对较弱,需求依赖第三方库才干便利的运用。其它平台都能够相对较快的树立 OpenGL 上下文环境,以至有封装好的 View 协助开发者快速接入。不过 OpenGL 制定的较早,曾经不太顺应现代 GPU 图形技术的开展了,遇到了一些问题:如现代 GPU 渲染管线发作了变化,不支持多线程操作,不支持异步处置等。

将来新一代的图形 API Vulkan 可能会取代 OpenGL。Vulkan 会大幅降低绘制命令开支,发送多线程性能,渲染性能更快。谷歌也曾经明白 Android会支持 Vulkan。微软的 DirectX12 背后理念与 Vulkan 也是分歧的。苹果公司则在2014年推出了自行设计的 Metal API,目的也是替代OpenGL,以顺应现代 GPU 技术,其指令开支和渲染性能等也得到大幅提升。2018年苹果曾经宣布 OpenGL 和 OpenGL ES 相关 API 从 macOS 10.14 和 iOS 12 中废弃,今后不再维护。开发者今后迁移到新的图形 API也是大势所趋,不过 OpenGL 作为主流图形 API 存在了超越20年,其最终消亡肯定还有很长的路。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK