文章目录
1、简介
1.1 OpenGL简介
OpenGL作为图形界的工业标准,其仅仅定义了一组2D和3D图形接口API,而对于窗口管理、IO消息响应等并没有规定。也就是说,OpenGL依赖各平台提供用于渲染的context以及具体实现方式,而各平台提供的实现不尽相同。这些实现主要有:Windows平台下的WGL、Linux下的Mesa/GLX、Mac OS X下的Cocoa/NSGL,以及跨平台的GLUT、GLFW、SDL等等。
安装OpenGL库:
# sudo apt-get install build-essential libxmu-dev libxi-dev libgl-dev
sudo apt-get install build-essential
sudo apt-get install libgl1-mesa-dev
sudo apt-get install libglu1-mesa-dev
使用如下的命令来查看对应显卡的OpenGL版本:
glxinfo | grep -i opengl
1.2 Linux上的窗体系统
Linux上的窗体系统主要有X11和Wayland两种,判断Linux系统的窗体系统可以使用以下方式:
- 在X11系统上:
echo $XDG_SESSION_TYPE
Ubuntu 20.04 LTS系统也可以在设置-关于里查看:
- 在某些Wayland系统上:
echo $XDG_SESSION_TYPE
Ubuntu 22.04 LTS系统也可以在设置-关于里查看:
根据系统与窗体系统进行选择依赖并安装。
如果这里是X11,按照编译指南,这里安装xorg-dev:
sudo apt install xorg-dev
Xorg(或 X 显示服务器)是传统的显示服务器,而 Wayland 相对较新。 2017 年,Ubuntu 将其设为默认版本 17.10。实验并不顺利,他们又恢复到使用 Ubuntu 18.04 的 Xorg。现在,Wayland 在 21.04 版本中再次成为默认值。
1.3 Linux中的显示服务器
显示服务器是一个程序,其主要任务是协调客户端与其他操作系统,硬件以及彼此之间的输入和输出。显示服务器通过显示服务器协议与其客户端进行通信。
显示服务器是任何图形用户界面(尤其是窗口系统)中的关键组件。它是图形用户界面(GUI)的基本组件,位于图形界面和内核之间。因此,借助显示服务器,您可以将计算机与GUI一起使用。没有它,您将只能使用命令行界面。
不要将显示服务器与桌面环境混淆是非常重要的。桌面环境(Gnome、KDE、Xfce、MATE等)使用了底层的显示服务器。
显示服务器通过显示服务器协议与其客户端进行通信。Linux中提供了三种显示服务器协议。X11和Wayland是其中两个。第三个Mir超出了本文的范围。
- (1)X Window System, X.Org, X11介绍
X Window System(通常仅称为X或X11)确实很古老。它最初起源于1984年,最终成为大多数类似UNIX的操作系统(包括Linux)的默认窗口系统。
X.Org服务器是X.Org基金会管理的X Window System显示服务器的免费开放源代码实现。它是一个通过X11协议与客户端应用程序进行交互的应用程序,用于在显示器上绘制内容并发送输入事件,例如鼠标移动,单击和击键。通常,将启动一个X服务器,它将等待客户端应用程序连接到它。Xorg基于客户端/服务器模型,因此允许客户端在另一台计算机上本地或远程运行。
在X11的设计中,应用程序和显示器不必在同一台计算机上,这一点并不明显。在开发X时,X server运行在工作站上,而用户在具有更强处理能力的远程计算机上运行应用程序是很常见的。
X11 是网络协议。它描述了如何在客户端(应用程序)和显示器(服务器)之间交换消息。这些消息通常带有原始的绘制命令,例如“绘制框”,“在此位置写这些字符”,“已单击鼠标左键”等。
但是X11已经很古老了,X server协议提供的大部分功能不再使用。X11所做的几乎所有工作都重新分配给了各个应用程序和窗口管理器。然而,所有这些旧特性仍然存在,给所有这些应用程序带来了压力,损害了性能和安全性。
X11(X Window System):X11,也称为 Xorg,几十年来一直是标准显示协议。它使用客户端-服务器架构,其中应用程序(客户端)与中央服务器通信以渲染图形。这种架构允许网络透明,但可能导致效率低下和复杂性。
- (2)下一代显示服务器Wayland
Wayland 由X.Org开发人员Kristian Hogsberg于2008年作为个人项目开始。它是一种通信协议 ,用于指定显示服务器与其客户端之间的通信。Wayland是作为一个免费的开源社区驱动的项目而开发的,目的是用现代,安全和简单的窗口系统代替X Window System(也称为X11或Xorg)。
Wayland:Wayland 采用更现代的方法,采用更简单的架构,旨在消除 X11 的复杂性。在 Wayland 模型中,合成器充当应用程序和显示硬件之间的中介。这种直接沟通使流程更加简化和高效。
Wayland是一套display server(Wayland compositor)与client间的通信协议,而Weston是Wayland compositor的参考实现。其官网为http://wayland.freedesktop.org/。它们定位于在Linux上替换X图形系统。X图形系统经历了30年左右的发展,其设计在今天看来已略显陈旧。在X系统中,X Server作为中心服务,连接clien和硬件以及compositor。但时至今日,原本在X Server中做的事很多已被移到kernel或者单独的库中,因此X Server就显得比较累赘了。Wayland在架构上去掉了这个中间层,将compositor作为display server,使client与compositor直接通信,从而在灵活性和性能等方面上能够比前辈更加出色。
1.4 xrandr命令
xrandr是一款官方的扩展配置工具。它可以设置屏幕显示的大小、方向、镜像等,包括对多屏的设置。详细的使用方法可以通过man xrandr查看。
xrandr
xrandr --verbose
2、Xlib开发
Xlib 是一个库,它为在 X Window 系统(也称为 X)下运行的应用程序提供函数。 这包括窗口管理和事件处理。X 是一个面向网络的系统:一个应用程序 在计算机 A 上运行的计算机 A 可以将其图形输出发送到计算机 B,该计算机位于网络中的其他位置 (网络可以是 LAN 和 Internet),并且可以从计算机 B 接收键盘或鼠标输入等事件。 这要求在两台计算机上都运行一个名为“X-Server”的程序。在 Linux 中,X-server 是使用 命令 startx.您很可能不必手动启动 X-server,因为大多数 Linux 发行版 将系统设置为在启动后自动启动 X。
https://x.org/releases/current/doc/libX11/libX11/libX11.html
2.1 创建空白窗口
- CMakeLists.txt
cmake_minimum_required(VERSION 2.8.1)
project(my_project)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 14)
find_package(X11 COMPONENTS X11 Xft)
add_executable(${PROJECT_NAME} main.cpp)
target_link_libraries(${PROJECT_NAME} ${X11_LIBRARIES})
- main.cpp
#include <X11/Xlib.h>
#include <stdio.h>
#include <cstdlib>
int main() {
//程序创建一个基本的窗口,并在按下任意键后退出
Display *d;
int s;
Window w;
XEvent e;
d = XOpenDisplay(NULL);
if (d == NULL) {
fprintf(stderr, "无法打开显示\n");
exit(1);
}
s = DefaultScreen(d);
w = XCreateSimpleWindow(d, RootWindow(d, s), 10, 10, 600, 400, 1, BlackPixel(d, s), WhitePixel(d, s));
XSelectInput(d, w, ExposureMask | KeyPressMask);
XMapWindow(d, w);
const char* title = "yxy x11 window";
XStoreName( d, w, title );
XSetIconName( d, w, title );
while (1) {
XNextEvent(d, &e);
if (e.type == KeyPress)
break;
}
XCloseDisplay(d);
return 0;
}
运行测试程序如下:
2.2 打印文字
- CMakeLists.txt
cmake_minimum_required(VERSION 2.8.1)
project(my_project)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 14)
find_package(X11 REQUIRED)
add_executable(${PROJECT_NAME} main.cpp)
target_link_libraries(${PROJECT_NAME} ${X11_LIBRARIES})
- main.cpp
/*
* 下边是编译命令
* gcc x11.c -o output -I/usr/X11R6/include -L/usr/X11R6/lib -lX11
*/
#include <X11/Xlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
Display *display;
Window window;
XEvent event;
char *msg = "hello, yxy, 2024";
int s;
/* 与Xserver建立连接 */
display = XOpenDisplay(NULL);
if (display == NULL)
{
fprintf(stderr, "Cannot open display\n");
exit(1);
}
s = DefaultScreen(display);
/* 创建一个窗口 */
window = XCreateSimpleWindow(display, RootWindow(display, s), 10, 10, 500, 200, 1,
BlackPixel(display, s), WhitePixel(display, s));
const char* title = "yxy x11 window";
XStoreName( display, window, title );
XSetIconName( display, window, title );
/* 选择一种感兴趣的事件进行监听 */
XSelectInput(display, window, ExposureMask | KeyPressMask);
/* 显示窗口 */
XMapWindow(display, window);
/* 事件遍历 */
for (;;)
{
XNextEvent(display, &event);
/* 绘制窗口或者重新绘制 */
if (event.type == Expose)
{
XFillRectangle(display, window, DefaultGC(display, s), 20, 20, 10, 10);
XDrawString(display, window, DefaultGC(display, s), 50, 50, msg, strlen(msg));
}
/* 当检测到键盘按键,退出消息循环 */
if (event.type == KeyPress)
break;
}
/* 关闭与Xserver服务器的连接 */
XCloseDisplay(display);
return 0;
}
2.3 键盘响应
- main.cpp
#include <stdio.h>
#include <X11/Xlib.h>
int main ( int argc, char **argv)
{
Display *display;
Window window;
Window root;
XSetWindowAttributes attributes;
Pixmap pixmap;
Visual *visual;
int screen;
int depth;
Atom wmDeleteMessage;
XEvent event;
display = XOpenDisplay (NULL);
screen = DefaultScreen (display);
visual = DefaultVisual (display, screen);
root = XRootWindow (display, screen);
depth = DefaultDepth (display, screen);
attributes.background_pixel = XWhitePixel (display, screen);
attributes.override_redirect = 0;
window = XCreateWindow (display, root,
0, 0, 400, 200,
0, depth, InputOutput,
visual,
CWBackPixel | CWBorderPixel |
CWOverrideRedirect, &attributes);
wmDeleteMessage = XInternAtom (display, "WM_DELETE_WINDOW" , False);
XSetWMProtocols (display, window, &wmDeleteMessage, 1);
XStoreName (display, window, "yxy x11 window" );
XMapWindow (display, window);
do {
XNextEvent (display, &event);
if (event.type == Expose) {
} else if (event.type == ClientMessage
&& event.xclient.data.l[0] == wmDeleteMessage) {
break ;
}
} while (event.type != KeyPress);
printf ( "closing display\n" );
XCloseDisplay (display);
}
3、OpenGL开发
Mesa是Linux下的OpenGL实现。它提供了对AMD Radeon系列、Nvidia GPU、Intel i965, i945, i915以及VMWare虚拟GPU等多种硬件驱动的支持,同时也提供了对softpipe等多种软件驱动的支持。Mesa项目由Brian Paul于1993年8月创建,于1995年2月发布了第一个发行版,此后便受到越来越多的关注,如今Mesa已经是任何一个Linux版本首选的OpenGL实现。
GLX则是在Linux上用于提供GL与窗口交互、窗口管理等等的一组API。它的作用与Windows的WGL、Mac OS X的AGL以及针对OpenGL ES的EGL相似。在Linux上,窗口创建、管理等API遵循X Window接口,而GLX提供了OpenGL与X Window交互的办法。因此GLX也可以运用于其他使用X Window的平台,例如FreeBSD等。
它使用 GLX 扩展到 X windows 系统。在 X11 中使用 XLib 与 GLX 绘制 OpenGL 图形的大致过程如下:
- 1、使用 XOpenDisplay 函数建立窗口(一个 X Client)与 X Server 端的连接。
- 2、根据指定的 GLX 帧缓存格式,调用 glXChooseVisual 函数选择创建匹配的 X 窗口画面(Visual)格式,使得 GLX 能够将 OpenGL 光栅化产生的图像转换为适当的 X 窗口画面格式。
- 3、使用 glXCreateContext 函数创建一个 OpenGL 渲染环境。
- 4、基于步骤 2 中所选择的 X 窗口画面格式,使用 XCreateWindow 构建一个 X 窗口。
- 5、使用 glXMakeCurrent 函数将 OpenGL 渲染环境设为当前的 X 窗口渲染环境,然后可调用 OpenGL 绘图函数。
- 6、使用 XMapWindow 函数显示 X 窗口。
- 7、开始 X 窗口的事件循环。
- 8、退出 X 窗口事件循环后的资源释放。
3.1 绘制矩形
- CMakeLists.txt
cmake_minimum_required(VERSION 2.8.1)
project(my_project)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 14)
find_package(OpenGL REQUIRED)
find_package(X11 REQUIRED)
#INCLUDE_DIRECTORIES(${OPENGL_INCLUDE_DIRS})
#LINK_DIRECTORIES(${OPENGL_LIBRARY_DIRS})
add_executable(${PROJECT_NAME} main.cpp)
target_link_libraries(${PROJECT_NAME} ${X11_LIBRARIES} ${OPENGL_LIBRARIES})
- main.cpp
#include<stdio.h>
#include<stdlib.h>
#include<X11/X.h>
#include<X11/Xlib.h>
#include<GL/gl.h>
#include<GL/glx.h>
#include<GL/glu.h>
Display *dpy;
Window root;
GLint att[] = { GLX_RGBA, GLX_DEPTH_SIZE, 24, GLX_DOUBLEBUFFER, None };
XVisualInfo *vi;
Colormap cmap;
XSetWindowAttributes swa;
Window win;
GLXContext glc;
XWindowAttributes gwa;
XEvent xev;
void DrawAQuad() {
glClearColor(1.0, 1.0, 1.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-1., 1., -1., 1., 1., 20.);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(0., 0., 10., 0., 0., 0., 0., 1., 0.);
glBegin(GL_QUADS);
glColor3f(1., 0., 0.); glVertex3f(-.75, -.75, 0.);
glColor3f(0., 1., 0.); glVertex3f( .75, -.75, 0.);
glColor3f(0., 0., 1.); glVertex3f( .75, .75, 0.);
glColor3f(1., 1., 0.); glVertex3f(-.75, .75, 0.);
glEnd();
}
int main(int argc, char *argv[]) {
dpy = XOpenDisplay(NULL);
if(dpy == NULL) {
printf("\n\tcannot connect to X server\n\n");
exit(0);
}
root = DefaultRootWindow(dpy);
vi = glXChooseVisual(dpy, 0, att);
if(vi == NULL) {
printf("\n\tno appropriate visual found\n\n");
exit(0);
}
else {
printf("\n\tvisual %p selected\n", (void *)vi->visualid); /* %p creates hexadecimal output like in glxinfo */
}
cmap = XCreateColormap(dpy, root, vi->visual, AllocNone);
swa.colormap = cmap;
swa.event_mask = ExposureMask | KeyPressMask;
win = XCreateWindow(dpy, root, 0, 0, 600, 600, 0, vi->depth, InputOutput, vi->visual, CWColormap | CWEventMask, &swa);
XMapWindow(dpy, win);
XStoreName(dpy, win, "yxy x11 window");
glc = glXCreateContext(dpy, vi, NULL, GL_TRUE);
glXMakeCurrent(dpy, win, glc);
glEnable(GL_DEPTH_TEST);
while(1) {
XNextEvent(dpy, &xev);
if(xev.type == Expose) {
XGetWindowAttributes(dpy, win, &gwa);
glViewport(0, 0, gwa.width, gwa.height);
DrawAQuad();
glXSwapBuffers(dpy, win);
}
else if(xev.type == KeyPress) {
glXMakeCurrent(dpy, None, NULL);
glXDestroyContext(dpy, glc);
XDestroyWindow(dpy, win);
XCloseDisplay(dpy);
exit(0);
}
} /* this closes while(1) { */
} /* this is the } which closes int main(int argc, char *argv[]) { */
执行程序如下:
gcc -o main main.c -lX11 -lGL -lGLU
结语
如果您觉得该方法或代码有一点点用处,可以给作者点个赞,或打赏杯咖啡;
╮( ̄▽ ̄)╭
如果您感觉方法或代码不咋地//(ㄒoㄒ)//,就在评论处留言,作者继续改进;
o_O???
如果您需要相关功能的代码定制化开发,可以留言私信作者;
(✿◡‿◡)
感谢各位大佬童鞋们的支持!
( ´ ▽´ )ノ ( ´ ▽´)っ!!!