基于linux系统的OpenGL环境

简介

相关资料有
khronos.org/egl
nvidia developer blog
OpenGL without X.org in linux
PyOpenGL headless rendering
khronos论坛
EGL函数文档
  linux系统对于图形渲染的支持是非常复杂繁琐的,对于初学者,人都会看晕了,这其中涉及很多东西如GL,EGL,GLX,WGL,AGL,XGL,XGLX,GLUT,GLFW,GL3W,GLEW,GLAD,GLU,GLM,X11,Xming,Xmanager,Xserver,wayland,Vulkan。
  这里很多东西网上可以找到资料,只说几个关键的地方。因为我是想在linux上做图形渲染,但是linux是没装显示器的,很多图形程序是无法运行的,这称之为离屏渲染或延迟渲染,或者叫headless模式。对于初学者小白,在windows系统上安装OpenGL是比较简单的,但是在没有显示器的linux上做OpenGL离屏渲染,初学者难免一时不太清楚如何配置环境,这里简单介绍一下。
  linux系统对于图形或者窗口系统的支持,来自于X11系统,其将窗口系统分成两部分,分别是X server和X client,X client的运行结果传递到X server上显示出来,所以可以很方便的进行远程桌面显示。这种方式一度很先进很时髦,但是后来,这种方式方式显的有点繁琐,尤其是对于高性能计算来说,这种方式会降低图形渲染效率。为此,最近开始流行wayland窗口系统,新版的ubuntu也已经支持这种桌面窗口了,这种窗口显示系统将其进行了简化,直接本地渲染传输数据,提升了效率。但是对于nvidia英伟达显卡,wayland的驱动是基于开源社区基于逆向工程而实现的,所以效率比不上其私有驱动,所以可能总的效率也看不出多大提高,而amd显卡则对此有更好的支持。
  要使用OpenGL进行离屏渲染,需要创建Framebuffer帧缓存。在窗口系统中创建一个显示窗口的时候会自动创建默认帧缓存,而在离屏渲染的时候,因为没有创建显示窗口,所以需要在程序中手动创建并且配置帧缓存。而配置帧缓存需要配置DISPLAY,config和上下文Context等,这个还是需要窗口系统的底层支持。在原本的linux中可以调用X11的支持,为此需要安装有GLX库,导入方式一般如下所示,之后可以基于GLX来创建Context上下文,在创建Framebuffer帧缓存实现离屏渲染。

#include<GL/glx.h>

  GLX创建的Context离不开X11,而除了GLX之外还有EGL,并且EGL不需要使用X11,wayland窗口系统就是基于EGL的,所以wayland可以摆脱X11。所以也可以使用EGL创建Context和Framebuffer帧缓存,之后就可在纯命令行终端下运行OpenGL,实现headless离屏渲染模式了,导入方式很简单。

#include<EGL/egl.h>

  我刚开始的时候,不知道这个EGL是咋安装上去的,后来才了解到系统或者显卡驱动会自带,但是有时候也会找不到开发库,所以可能需要另外安装。

yum install mesa*
yum install freeglut
yum install *GLEW*
EGL创建Context
#include <EGL/egl.h>
#include <GL/gl.h>

static const EGLint configAttribs[] = {
    EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
    EGL_BLUE_SIZE, 8,
    EGL_GREEN_SIZE, 8,
    EGL_RED_SIZE, 8,
    EGL_DEPTH_SIZE, 8,
    EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
    EGL_NONE
};

//static const int pbufferWidth = 9;
//static const int pbufferHeight = 9;
#define pbufferWidth 9
#define pbufferHeight 9

static const EGLint pbufferAttribs[] = {
    EGL_WIDTH, pbufferWidth,
    EGL_HEIGHT, pbufferHeight,
    EGL_NONE
};

int main(int argc, char *argv[])
{
  // 1. Initialize EGL
  EGLDisplay eglDpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);

  EGLint major, minor;

  eglInitialize(eglDpy, &major, &minor);

  // 2. Select an appropriate configuration
  EGLint numConfigs;
  EGLConfig eglCfg;

  eglChooseConfig(eglDpy, configAttribs, &eglCfg, 1, &numConfigs);

  // 3. Create a surface
  EGLSurface eglSurf = eglCreatePbufferSurface(eglDpy, eglCfg,
                                               pbufferAttribs);

  // 4. Bind the API
  eglBindAPI(EGL_OPENGL_API);

  // 5. Create a context and make it current
  EGLContext eglCtx = eglCreateContext(eglDpy, eglCfg, EGL_NO_CONTEXT,
                                       NULL);

  eglMakeCurrent(eglDpy, eglSurf, eglSurf, eglCtx);

  // from now on use your OpenGL context

  // 6. Terminate EGL when finished
  eglTerminate(eglDpy);
  return 0;
}

  在使用EGL创建了Context之后,就可以创建Framebuffer帧缓存了,但是直接调用帧缓存创建函数,却显示错误,提示未定义函数glGenFramebuffers。

GLuint Framebuffer;
glGenFramebuffers(1, &Framebuffer);

//编译程序,显示错误
gcc -o test_2 test_2.c -lEGL
/tmp/cchBuBdO.o: In function `main':
test_2.c:(.text+0xc3): undefined reference to `glGenFramebuffers'
collect2: error: ld returned 1 exit status

后来才了解到需要使用基于EGL的函数实现

PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers = NULL;
glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC) eglGetProcAddress("glGenFramebuffers");

经过这个进行函数赋值之后,就能调用原先的OpenGL的函数了,当然这个还跟上面的EGL的API绑定有关,因为EGL是可以跨平台的,对于linux,windows和手机移动端都是可以实现的,所以不同的系统平台,要绑定的API也不一样,可以是linux的OpenGL,或者是OpenGL ES等。

eglBindAPI(EGL_OPENGL_API);

  但是需要导入很多函数,这时候就会很麻烦了,为此可以专门写一个头文件,用以导入所有要用到的函数,这里只放一个简单的例子。

#include<EGL/egl.h>
#include<GL/gl.h>
#pragma once
// 纹理
PFNGLCREATETEXTURESPROC glCreateTextures = NULL;
PFNGLTEXTURESTORAGE2DPROC glTextureStorage2D = NULL;
// 渲染缓存
PFNGLCREATERENDERBUFFERSPROC glCreateRenderbuffers = NULL;
PFNGLNAMEDRENDERBUFFERSTORAGEPROC glNamedRenderbufferStorage = NULL;
PFNGLBINDRENDERBUFFERPROC glBindRenderbuffer = NULL;
PFNGLDELETERENDERBUFFERSPROC glDeleteRenderbuffers = NULL;
// 帧缓存
PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers = NULL;
PFNGLCREATEFRAMEBUFFERSPROC glCreateFramebuffers = NULL;
PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer = NULL;
PFNGLNAMEDFRAMEBUFFERRENDERBUFFERPROC glNamedFramebufferRenderbuffer = NULL;
PFNGLCHECKNAMEDFRAMEBUFFERSTATUSPROC glCheckNamedFramebufferStatus = NULL;
PFNGLINVALIDATEFRAMEBUFFERPROC glInvalidateFramebuffer = NULL;
PFNGLDRAWBUFFERSPROC glDrawBuffers = NULL;
// 常用
PFNGLENABLEIPROC glEnablei = NULL;
PFNGLVIEWPORTARRAYVPROC glViewportArrayv = NULL;
PFNGLCLEARCOLORXOESPROC glClearColorxoes = NULL;
PFNGLCLEARBUFFERFIPROC glClearBufferi = NULL;
static void load_OpenGLAPI(){
    glCreateTextures = (PFNGLCREATETEXTURESPROC) eglGetProcAddress("glCreateTextures");
    glTextureStorage2D = (PFNGLTEXTURESTORAGE2DPROC) eglGetProcAddress("glTextureStorage2D");
    glCreateRenderbuffers = (PFNGLCREATERENDERBUFFERSPROC) eglGetProcAddress("glCreateRenderbuffers");
    glNamedRenderbufferStorage = (PFNGLNAMEDRENDERBUFFERSTORAGEPROC) eglGetProcAddress("glNamedRenderbufferStorage");
    glBindRenderbuffer = (PFNGLBINDRENDERBUFFERPROC) eglGetProcAddress("glBindRenderbuffer");
    glDeleteRenderbuffers = (PFNGLDELETERENDERBUFFERSPROC) eglGetProcAddress("glDeleteRenderbuffers");
    glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC) eglGetProcAddress("glGenFramebuffers");
    glCreateFramebuffers = (PFNGLCREATEFRAMEBUFFERSPROC) eglGetProcAddress("glCreateFramebuffers");
    glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC) eglGetProcAddress("glBindFramebuffer");
    glNamedFramebufferRenderbuffer = (PFNGLNAMEDFRAMEBUFFERRENDERBUFFERPROC) eglGetProcAddress("glNamedFramebufferRenderbuffer");
    glCheckNamedFramebufferStatus = (PFNGLCHECKNAMEDFRAMEBUFFERSTATUSPROC) eglGetProcAddress("glCheckNamedFramebufferStatus");
    glInvalidateFramebuffer = (PFNGLINVALIDATEFRAMEBUFFERPROC) eglGetProcAddress("glInvalidateFramebuffer");
    glDrawBuffers = (PFNGLDRAWBUFFERSPROC) eglGetProcAddress("glDrawBuffers");
    glEnablei = (PFNGLENABLEIPROC) eglGetProcAddress("glEnablei");
    glViewportArrayv = (PFNGLVIEWPORTARRAYVPROC) eglGetProcAddress("glViewportArrayv");
    glClearColorxoes = (PFNGLCLEARCOLORXOESPROC) eglGetProcAddress("glClearColorxoes");
    glClearBufferi = (PFNGLCLEARBUFFERFIPROC) eglGetProcAddress("glClearBufferi");
}

  要继续配置Context和window,可能还不太了解,这里有一份参考代码

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

#include<EGL/egl.h>
#include<X11/Xlib.h>

struct my_display{
    Display *x11;
    EGLDisplay egl;
};

struct my_config{
    struct my_display dpy;
    XVisualInfo *x11;
    Colormap colormap;
    EGLConfig egl;
};

struct my_window{
    struct my_config config;
    Window x11;
    EGLSurface egl;
};

struct my_pixmap{
    struct my_config config;
    Pixmap x11;
    EGLSurface egl;
};

static void
check_extensions(void){
#ifdef USE_EGL_EXT_PLATFORM_X11
    const char *client_extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
    if(!client_extensions){
        // EGL_EXT_client_extensions is unsupported.
        abort();
    }
    if(!strstr(client_extensions, "EGL_EXT_platform_x11")){
        abort();
    }
#endif
}

static struct my_display
get_display(void){
    struct my_display dpy;
    dpy.x11 = XOpenDisplay(NULL);
    if(!dpy.x11){
        abort();
    }
#ifdef USE_EGL_EXT_PLATFORM_X11
    dpy.egl = eglGetPlatformDisplayEXT(EGL_PLATFORM_X11_EXT, dpy.x11, NULL);
#else
    dpy.egl = eglGetDisplay(dpy.x11);
#endif
    if(dpy.egl == EGL_NO_DISPLAY){
        abort();
    }
    return dpy;
}
static struct my_config
get_config(struct my_display dpy){
    struct my_config config = {
        .dpy = dpy,
    };
    EGLint egl_config_attribs[] = {
        EGL_BUFFER_SIZE, 32,
        EGL_RED_SIZE, 8,
        EGL_GREEN_SIZE, 8,
        EGL_BLUE_SIZE, 8,
        EGL_ALPHA_SIZE, 8,
        EGL_DEPTH_SIZE, EGL_DONT_CARE,
        EGL_STENCIL_SIZE, EGL_DONT_CARE,
        EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
        EGL_SURFACE_TYPE, EGL_WINDOW_BIT | EGL_PIXMAP_BIT,
        EGL_NONE,
    };
    EGLint num_configs;
    if(!eglChooseConfig(dpy.egl,
                        egl_config_attribs,
                        &config.egl, 1,
                        &num_configs)){
        abort();
    }
    if(num_configs == 0){
        abort();
    }
    XVisualInfo x11_visual_info_template;
    if(!eglGetConfigAttrib(dpy.egl,
                           config.egl,
                           EGL_NATIVE_VISUAL_ID,
                           (EGLint*)
                           &x11_visual_info_template.visualid)){
        abort();
    }
    int num_visuals;
    config.x11 = XGetVisualInfo(dpy.x11,
                                VisualIDMask,
                                &x11_visual_info_template,
                                &num_visuals);
    if(!config.x11){
        abort();
    }
    config.colormap = XCreateColormap(dpy.x11,
                                      RootWindow(dpy.x11, 0),
                                      config.x11->visual,
                                      AllocNone);
    if(config.colormap == None){
        abort();
    }
    return config;
}

static struct my_window
get_window(struct my_config config){
    XSetWindowAttributes attr;
    unsigned long mask;
    struct my_window window = {
        .config = config,
    };
    attr.colormap = config.colormap;
    mask = CWColormap;
    window.x11 = XCreateWindow(config.dpy.x11,
                               DefaultRootWindow(config.dpy.x11), // parent
                               0, 0, // x, y
                               256, 256, // width, height
                               0, //border_width
                               config.x11->depth,
                               InputOutput, // class
                               config.x11->visual,
                               mask, // valuemask
                               &attr); // attributes
    if(!window.x11){
        abort();
    }
#ifdef USE_EGL_EXT_PLATFORM_X11
    window.egl = eglCreatePlatformWindowSurfaceEXT(config.dpy.egl,
                                                   config.egl,
                                                   &window.x11,
                                                   NULL);
#else
    window.egl = eglCreateWindowSurface(config.dpy.egl,
                                        config.egl,
                                        window.x11,
                                        NULL);
#endif
    if(window.egl == EGL_NO_SURFACE){
        abort();
    }
    return window;
}

static struct my_pixmap
get_pixmap(struct my_config config){
    struct my_pixmap pixmap = {
        .config = config,
    };
    pixmap.x11 = XCreatePixmap(config.dpy.x11,
                               DefaultRootWindow(config.dpy.x11),
                               256, 256, // width, height
                               config.x11->depth);
    if(!pixmap.x11){
        abort();
    }
#ifdef USE_EGL_EXT_PLATFORM_X11
    pixmap.egl = eglCreatePlatformPixmapSurfaceEXT(config.dpy.egl,
                                                   config.egl,
                                                   &pixmap.x11,
                                                   NULL);
#else
    pixmap.egl = eglCreatePixmapSurface(config.dpy.egl,
                                        config.egl,
                                        pixmap.x11,
                                        NULL);
#endif
    if(pixmap.egl == EGL_NO_SURFACE){
        abort();
    }
    return pixmap;
}
int main()
{
    check_extensions();
    struct my_display dpy = get_display();
    struct my_config config = get_config(dpy);
    struct my_window window = get_window(config);
    struct my_pixmap pixmap = get_pixmap(config);
    printf("Hello world\n");
    return 0;
}

  • 5
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
用c++/qt写的项目,可供自己学习,项目都经测试过,真实可靠,请放心使用。Qt支持 Windows、Linux/Unix、Mac OS X、Android、BlackBerry、QNX等多种平台,并为这些不同的平台提供了统一的开发环境。 面向对象 C++是完全面向对象的,这一点和Objective-c等在开发很相似。而Qt又是基于C++一种语言的扩展,大家都知道C++ 有快速、简易、面向对象等很多优点,所以Qt自然也继承者C++这些的优点。 Qt良好的封装机制使得Qt的模块化程度非常高,可重用性较好,对用户开发来货是非常方便的。Qt提供一种为signals/slots(信号和槽) 的安全类型来替代callback,使得各个元件之间的协同工作变得十分简单。 丰富的API Qt包括多达 250 个以上的 C++ 类,还提供基于模板的 collections, serialization, file, I/Odevice, directory management, date/time 类。甚至还包括正则表达式的处理功能。 支持 2D/3D 图形渲染,支持 OpenGL。 大量的开发文档。 XML支持 Webkit 引擎的集成,可以实现本地界面与Web内容的无缝集成, 但是真正使得 Qt 在自由软件界的众多 Widgets (如 Lesstif,Gtk,EZWGL,Xforms,fltk 等等)中脱颖而出的还是基于 Qt 的重量级软件 KDE。 信号和槽机制 Qt提供了信号和槽机制用于完成见面操作的响应,是完成任意两个Qt对象之通信机制。其中,信号会在某个特定情况或动作下被触动,槽是等同于接受并处理信号的函数。 为什么方法不是直接调用的。中间用到 Signal 和槽机制不是多此一举? 其实在我们生活也是一样,老板级别的好说话,老板给助理分派任务也好说话,但是助理给老板分任务,可想而知会有什么后果,在以前的统治阶层肯定不允许这样的事发生。所以在分层思想中,我们所调用的函数也是这样的,上层可以调用下层和同一层的函数,下层函数不可以调用上层函数,否则程序的层次性会被打破,导致结构错综复杂,难以维护和管理。 那么怎样才能做到向上管理呢,有任务分配给老板怎么办? 老板会设立一个机构,也就是一个函数,用无限循环来查询助理的状态,如果助理真的有事情,这个机构就把这消息拿到老板来处理。但是这种处理方式显得有些复杂,我们想要的简单明了的方式是,如果助理有事件发生,可以直接调用老板函数处理。 说了这么多其实就是想说,信号和槽的最大优势在于,它完善了程序分层的思想,可以在不改变程序的层次性的情况下,完成由下层到上层的调用。在下层发出一个 Signal,这时上层与其想关联的 Slot 函数就会响应。
用c++/qt写的项目,可供自己学习,项目都经测试过,真实可靠,请放心使用。Qt支持 Windows、Linux/Unix、Mac OS X、Android、BlackBerry、QNX等多种平台,并为这些不同的平台提供了统一的开发环境。 面向对象 C++是完全面向对象的,这一点和Objective-c等在开发很相似。而Qt又是基于C++一种语言的扩展,大家都知道C++ 有快速、简易、面向对象等很多优点,所以Qt自然也继承者C++这些的优点。 Qt良好的封装机制使得Qt的模块化程度非常高,可重用性较好,对用户开发来货是非常方便的。Qt提供一种为signals/slots(信号和槽) 的安全类型来替代callback,使得各个元件之间的协同工作变得十分简单。 丰富的API Qt包括多达 250 个以上的 C++ 类,还提供基于模板的 collections, serialization, file, I/Odevice, directory management, date/time 类。甚至还包括正则表达式的处理功能。 支持 2D/3D 图形渲染,支持 OpenGL。 大量的开发文档。 XML支持 Webkit 引擎的集成,可以实现本地界面与Web内容的无缝集成, 但是真正使得 Qt 在自由软件界的众多 Widgets (如 Lesstif,Gtk,EZWGL,Xforms,fltk 等等)中脱颖而出的还是基于 Qt 的重量级软件 KDE。 信号和槽机制 Qt提供了信号和槽机制用于完成见面操作的响应,是完成任意两个Qt对象之通信机制。其中,信号会在某个特定情况或动作下被触动,槽是等同于接受并处理信号的函数。 为什么方法不是直接调用的。中间用到 Signal 和槽机制不是多此一举? 其实在我们生活也是一样,老板级别的好说话,老板给助理分派任务也好说话,但是助理给老板分任务,可想而知会有什么后果,在以前的统治阶层肯定不允许这样的事发生。所以在分层思想中,我们所调用的函数也是这样的,上层可以调用下层和同一层的函数,下层函数不可以调用上层函数,否则程序的层次性会被打破,导致结构错综复杂,难以维护和管理。 那么怎样才能做到向上管理呢,有任务分配给老板怎么办? 老板会设立一个机构,也就是一个函数,用无限循环来查询助理的状态,如果助理真的有事情,这个机构就把这消息拿到老板来处理。但是这种处理方式显得有些复杂,我们想要的简单明了的方式是,如果助理有事件发生,可以直接调用老板函数处理。 说了这么多其实就是想说,信号和槽的最大优势在于,它完善了程序分层的思想,可以在不改变程序的层次性的情况下,完成由下层到上层的调用。在下层发出一个 Signal,这时上层与其想关联的 Slot 函数就会响应。
用c++/qt写的项目,可供自己学习,项目都经测试过,真实可靠,请放心使用。Qt支持 Windows、Linux/Unix、Mac OS X、Android、BlackBerry、QNX等多种平台,并为这些不同的平台提供了统一的开发环境。 面向对象 C++是完全面向对象的,这一点和Objective-c等在开发很相似。而Qt又是基于C++一种语言的扩展,大家都知道C++ 有快速、简易、面向对象等很多优点,所以Qt自然也继承者C++这些的优点。 Qt良好的封装机制使得Qt的模块化程度非常高,可重用性较好,对用户开发来货是非常方便的。Qt提供一种为signals/slots(信号和槽) 的安全类型来替代callback,使得各个元件之间的协同工作变得十分简单。 丰富的API Qt包括多达 250 个以上的 C++ 类,还提供基于模板的 collections, serialization, file, I/Odevice, directory management, date/time 类。甚至还包括正则表达式的处理功能。 支持 2D/3D 图形渲染,支持 OpenGL。 大量的开发文档。 XML支持 Webkit 引擎的集成,可以实现本地界面与Web内容的无缝集成, 但是真正使得 Qt 在自由软件界的众多 Widgets (如 Lesstif,Gtk,EZWGL,Xforms,fltk 等等)中脱颖而出的还是基于 Qt 的重量级软件 KDE。 信号和槽机制 Qt提供了信号和槽机制用于完成见面操作的响应,是完成任意两个Qt对象之通信机制。其中,信号会在某个特定情况或动作下被触动,槽是等同于接受并处理信号的函数。 为什么方法不是直接调用的。中间用到 Signal 和槽机制不是多此一举? 其实在我们生活也是一样,老板级别的好说话,老板给助理分派任务也好说话,但是助理给老板分任务,可想而知会有什么后果,在以前的统治阶层肯定不允许这样的事发生。所以在分层思想中,我们所调用的函数也是这样的,上层可以调用下层和同一层的函数,下层函数不可以调用上层函数,否则程序的层次性会被打破,导致结构错综复杂,难以维护和管理。 那么怎样才能做到向上管理呢,有任务分配给老板怎么办? 老板会设立一个机构,也就是一个函数,用无限循环来查询助理的状态,如果助理真的有事情,这个机构就把这消息拿到老板来处理。但是这种处理方式显得有些复杂,我们想要的简单明了的方式是,如果助理有事件发生,可以直接调用老板函数处理。 说了这么多其实就是想说,信号和槽的最大优势在于,它完善了程序分层的思想,可以在不改变程序的层次性的情况下,完成由下层到上层的调用。在下层发出一个 Signal,这时上层与其想关联的 Slot 函数就会响应。
用c++/qt写的项目,可供自己学习,项目都经测试过,真实可靠,请放心使用。Qt支持 Windows、Linux/Unix、Mac OS X、Android、BlackBerry、QNX等多种平台,并为这些不同的平台提供了统一的开发环境。 面向对象 C++是完全面向对象的,这一点和Objective-c等在开发很相似。而Qt又是基于C++一种语言的扩展,大家都知道C++ 有快速、简易、面向对象等很多优点,所以Qt自然也继承者C++这些的优点。 Qt良好的封装机制使得Qt的模块化程度非常高,可重用性较好,对用户开发来货是非常方便的。Qt提供一种为signals/slots(信号和槽) 的安全类型来替代callback,使得各个元件之间的协同工作变得十分简单。 丰富的API Qt包括多达 250 个以上的 C++ 类,还提供基于模板的 collections, serialization, file, I/Odevice, directory management, date/time 类。甚至还包括正则表达式的处理功能。 支持 2D/3D 图形渲染,支持 OpenGL。 大量的开发文档。 XML支持 Webkit 引擎的集成,可以实现本地界面与Web内容的无缝集成, 但是真正使得 Qt 在自由软件界的众多 Widgets (如 Lesstif,Gtk,EZWGL,Xforms,fltk 等等)中脱颖而出的还是基于 Qt 的重量级软件 KDE。 信号和槽机制 Qt提供了信号和槽机制用于完成见面操作的响应,是完成任意两个Qt对象之通信机制。其中,信号会在某个特定情况或动作下被触动,槽是等同于接受并处理信号的函数。 为什么方法不是直接调用的。中间用到 Signal 和槽机制不是多此一举? 其实在我们生活也是一样,老板级别的好说话,老板给助理分派任务也好说话,但是助理给老板分任务,可想而知会有什么后果,在以前的统治阶层肯定不允许这样的事发生。所以在分层思想中,我们所调用的函数也是这样的,上层可以调用下层和同一层的函数,下层函数不可以调用上层函数,否则程序的层次性会被打破,导致结构错综复杂,难以维护和管理。 那么怎样才能做到向上管理呢,有任务分配给老板怎么办? 老板会设立一个机构,也就是一个函数,用无限循环来查询助理的状态,如果助理真的有事情,这个机构就把这消息拿到老板来处理。但是这种处理方式显得有些复杂,我们想要的简单明了的方式是,如果助理有事件发生,可以直接调用老板函数处理。 说了这么多其实就是想说,信号和槽的最大优势在于,它完善了程序分层的思想,可以在不改变程序的层次性的情况下,完成由下层到上层的调用。在下层发出一个 Signal,这时上层与其想关联的 Slot 函数就会响应。
### 回答1: 要开发基于OpenGL ES的实时滤镜相机,您需要以下开发环境: 1. 一台计算机:您需要一台配有OpenGL ES 2.0或更高版本的计算机,以便能够编写和测试您的代码。 2. 编辑器:您需要一个能够编写OpenGL ES代码的编辑器。一些常用的编辑器包括Visual Studio Code、Sublime Text和Atom。 3. OpenGL ES SDK:您需要安装适用于您的目标平台的OpenGL ES SDK。例如,如果您的目标平台是Android设备,则需要安装Android NDK。 4. 相机API:您需要使用相机API来访问设备的摄像头。对于Android设备,您可以使用Camera2 API。 5. 滤镜库:您需要选择并集成一个滤镜库,以便能够添加各种滤镜效果。一些常用的滤镜库包括GPUImage和OpenCV。 6. 设备测试:最后,您需要在目标设备上测试您的应用程序。您可以使用Android模拟器进行测试,但最好在实际设备上进行测试,以确保您的应用程序能够正常工作并获得所需的性能。 ### 回答2: 开发基于OpenGL ES的实时滤镜相机需要具备以下开发环境: 1. 操作系统:开发可以在 Windows、Mac OS X 或 Linux 上进行,这些操作系统都支持OpenGL ES开发。 2. 集成开发环境(IDE):可以选择使用常见的IDE,如Android Studio、Visual Studio Code、Eclipse等。这些IDE都支持OpenGL ES开发,并且提供了强大的代码编辑和调试功能。 3. SDK:如果是开发Android应用,需要下载并安装Android SDK。Android SDK包含了Android平台的开发工具和资源,提供了OpenGL ES库和相关文档。 4. OpenGL ES库:OpenGL ES库是实现OpenGL ES的软件包,提供了OpenGL ES的函数接口和硬件加速功能。开发者需要下载并集成OpenGL ES库到项目中。 5. 设备和模拟器:为了测试应用程序,可以使用Android设备或者Android模拟器。如果要在真机上进行测试,需要确保设备支持OpenGL ES。 6. 学习资源:了解OpenGL ES的基本原理和开发技巧是非常重要的。可以通过阅读官方文档、教程、书籍或者参加培训来学习OpenGL ES。 7. 文件导入:将想要应用的滤镜效果的着色器代码以及资源文件导入项目中。 8. 编写代码:根据项目需求,使用OpenGL ES提供的函数接口编写相机和滤镜效果的相关代码。通过OpenGL ES的API可以设置相机预览、处理图像数据,应用滤镜效果并实现实时预览。 9. 测试和调试:在开发过程中,需要通过测试和调试来确保应用程序的正常运行。可以利用IDE提供的工具进行应用调试,检查是否出现错误和异常情况。 10. 发布和部署:完成开发后,可以将应用程序打包并发布到相应的应用商店。根据目标平台的要求,将应用程序部署到Android设备或者发布到其他平台。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值