日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > linux >内容正文

linux

linux opengl 实例,一篇文章入门Ubuntu的OpenGL开发

發(fā)布時(shí)間:2025/3/15 linux 48 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux opengl 实例,一篇文章入门Ubuntu的OpenGL开发 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

因?yàn)橛悬c(diǎn)小野心,想寫(xiě)個(gè)可以在Linux下跑的渲染庫(kù),于是就費(fèi)了點(diǎn)功夫研究Ubuntu下OpenGL的開(kāi)發(fā)。但是,由于完全沒(méi)有Ubuntu下開(kāi)發(fā)的經(jīng)驗(yàn),遇到了各種問(wèn)題,折騰了一陣子,總算是有點(diǎn)收獲,寫(xiě)篇文章分享一下。

由于筆者水平有限,文章中若有什么紕漏,請(qǐng)路過(guò)的讀者指出!

基礎(chǔ)知識(shí)

與Windows不同,linux的內(nèi)核沒(méi)有圖形界面的代碼,它的界面只是運(yùn)行在操作系統(tǒng)上的一個(gè)軟件而已。也就是說(shuō),即便把這個(gè)界面關(guān)掉,系統(tǒng)仍然在運(yùn)行,你還能再打開(kāi)這個(gè)界面,這對(duì)windows來(lái)說(shuō)是不可想象的,因?yàn)樵趙indows下,圖形界面是系統(tǒng)不可分割的一部分。

于是,在Linux上,實(shí)現(xiàn)了一種名為X窗口系統(tǒng)的東西來(lái)模擬窗口界面。X窗口系統(tǒng)是基于X協(xié)議實(shí)現(xiàn)的。所謂的協(xié)議,相當(dāng)于一種語(yǔ)言,你必須要理解并且遵守語(yǔ)言的規(guī)則才能溝通,比如http協(xié)議,客戶(hù)端與服務(wù)器必須都采用http協(xié)議的規(guī)范發(fā)送、接收、解析數(shù)據(jù),才能有絢麗的網(wǎng)頁(yè)呈現(xiàn)在我們面前。Linux下的窗口與這個(gè)過(guò)程及其類(lèi)似,它也是一種客戶(hù)端/服務(wù)器(C/S)結(jié)構(gòu)。不同的是,這個(gè)服務(wù)器和客戶(hù)端是在一臺(tái)機(jī)器上。于是,在Linux下進(jìn)行窗口編程的時(shí)候,你會(huì)看到很多XServer,XClient的字眼,說(shuō)的就是實(shí)現(xiàn)X協(xié)議的服務(wù)器與客戶(hù)端。

窗口實(shí)現(xiàn)

開(kāi)始寫(xiě)代碼前,先做一個(gè)準(zhǔn)備工作:安裝xcb庫(kù)。Ubuntu下的安裝命令是:sudo apt-get install libxcb1-dev。

從編程的角度上看,Linux把窗口的顯示抽象成了這些概念:連接(connect),窗口(window),屏幕(screen),上下文(context)和事件(event)。

連接(connext):一個(gè)xcb_connection_t對(duì)象,表示X客戶(hù)端與X服務(wù)器之間的連接。我們創(chuàng)建的是X客戶(hù)端,客戶(hù)端需要把繪制指令發(fā)送給服務(wù)器,所以必須要有一個(gè)與服務(wù)器之間的連接。

窗口(window):一個(gè)xcb_window_t對(duì)象,這個(gè)不用多說(shuō),字面上的意思。

屏幕(screen):一個(gè)xcb_screen_t對(duì)象,我的理解就是物理意義上的屏幕,也就是顯示器,可以通過(guò)枚舉來(lái)找到所有連接的顯示器。

上下文(context):一個(gè)xcb_gcontext_t對(duì)象,這是與窗口關(guān)聯(lián)的繪制環(huán)境,類(lèi)似于Windows下的DC,所有的繪制都是在上下文上進(jìn)行繪制。

事件(event):一個(gè)xcb_generic_event_t對(duì)象,將用戶(hù)的每一個(gè)操作都當(dāng)成一個(gè)事件進(jìn)行處理,類(lèi)似于Windows下的消息。

實(shí)現(xiàn)的流程是:

1、創(chuàng)建連接。

2、獲取屏幕(需要用到連接)。

3、創(chuàng)建上下文(需要用到屏幕的根窗口)。

4、創(chuàng)建我們要用的窗口(需要用到屏幕)。

5、映射窗口和連接(需要用到窗口和連接)。

6、監(jiān)聽(tīng)并且處理事件(需要用到連接、窗口和上下文)。

完整代碼如下所示:

#include

#include

#include

#include

int main(void) {

xcb_connection_t *pConn;

xcb_screen_t *pScreen;

xcb_window_t window;

xcb_gcontext_t foreground;

xcb_gcontext_t background;

xcb_generic_event_t *pEvent;

uint32_t mask = 0;

uint32_t values[2];

uint8_t isQuit = 0;

char title[] = "Hello, Engine!";

char title_icon[] = "Hello, Engine! (iconified)";

/* 第一步:創(chuàng)建連接 */

// 建立與X服務(wù)器的連接

pConn = xcb_connect(0, 0);

/* 第二步:獲取屏幕 */

// xcb_get_setup函數(shù)用于從X服務(wù)器獲取數(shù)據(jù),獲取的數(shù)據(jù)包括服務(wù)器支持的圖像格式,

// 可顯示的屏幕列表,可用的視覺(jué)效果列表,服務(wù)器的最大請(qǐng)求長(zhǎng)度等等

// xcb_setup_roots_iterator函數(shù)只查到原型,沒(méi)有函數(shù)的說(shuō)明,從函數(shù)名和使用方式

// 上看,應(yīng)該是查找數(shù)據(jù)用的。

pScreen = xcb_setup_roots_iterator(xcb_get_setup(pConn)).data;

/* 第三步:創(chuàng)建上下文 */

// 先獲取根窗口

window = pScreen->root;

// 創(chuàng)建前景上下文(黑色)

foreground = xcb_generate_id(pConn); // 生成上下文的ID

mask = XCB_GC_FOREGROUND | XCB_GC_GRAPHICS_EXPOSURES; // 上下文的用途,前景&需要事件

values[0] = pScreen->black_pixel; // 填充顏色(黑色)

values[1] = 0; // 結(jié)束標(biāo)志

xcb_create_gc(pConn, foreground, window, mask, values); // 創(chuàng)建上下文

// 創(chuàng)建背景上下文(白色)

background = xcb_generate_id(pConn); // 生成上下文ID

mask = XCB_GC_BACKGROUND | XCB_GC_GRAPHICS_EXPOSURES; // 上下文用途,前景&需要事件

values[0] = pScreen->white_pixel; // 填充顏色(白色)

values[1] = 0; // 結(jié)束標(biāo)志

xcb_create_gc(pConn, background, window, mask, values); // 創(chuàng)建上下文

/* 第四步:創(chuàng)建窗口 */

window = xcb_generate_id(pConn); // 創(chuàng)建窗口ID

mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK; // 覆蓋BackPixmap,需要指定的事件

values[0] = pScreen->white_pixel; // 白色填充

values[1] = XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_KEY_PRESS; // 需要EXPOSE事件和按鍵事件

xcb_create_window (pConn, // 連接

XCB_COPY_FROM_PARENT, // 深度值

window, // 窗口ID

pScreen->root, // 父窗口,屏幕的根窗口

20, 20, // x,y坐標(biāo)

640, 480, // 寬度,高度

10, // 邊緣寬度

XCB_WINDOW_CLASS_INPUT_OUTPUT, // 要么是0,要么是一些指定的值

pScreen->root_visual, // 視覺(jué)效果,暫時(shí)不知道是啥玩意

mask, values); // 需要的功能與值設(shè)定

// 設(shè)置窗口名

xcb_change_property(pConn, XCB_PROP_MODE_REPLACE, window,

XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8,

strlen(title), title);

// 設(shè)置窗口圖標(biāo)

xcb_change_property(pConn, XCB_PROP_MODE_REPLACE, window,

XCB_ATOM_WM_ICON_NAME, XCB_ATOM_STRING, 8,

strlen(title_icon), title_icon);

/* 第五步:關(guān)聯(lián)窗口和連接 */

xcb_map_window(pConn, window);

xcb_flush(pConn); // 刷新

/* 第六步:處理事件 */

while((pEvent = xcb_wait_for_event(pConn)) && !isQuit) {

switch(pEvent->response_type & ~0x80) {

case XCB_EXPOSE: // 繪制或重繪窗口

{

xcb_rectangle_t rect = { 20, 20, 60, 80 };

xcb_poly_fill_rectangle(pConn, window, foreground, 1, &rect); // 繪制一塊矩形區(qū)域

xcb_flush(pConn); // 刷新

}

break;

case XCB_KEY_PRESS: // 按鍵

isQuit = 1;

break;

}

free(pEvent);

}

xcb_disconnect(pConn); // 斷開(kāi)連接

return 0;

}

完成代碼,保存成.c文件(比如helloengine_xcb.c)。用gcc helloengine_xcb.c -lxcb -o helloengine_xcb命令構(gòu)建,或者如果你用clang編譯器的話(huà)使用clang -lxcb -o helloengine_xcb helloengine_xcb.c命令構(gòu)建,就可以看到生成的可執(zhí)行文件helloengine.xcb.out。運(yùn)行文件,得到如下的結(jié)果:

OpenGL繪制

純種的OpenGL繪制方式使用的是GLX庫(kù)。GLX(全稱(chēng)OpenGL Extension to the X Window System,X窗口系統(tǒng)的OpenGL擴(kuò)展)是OpenGL和X窗口系統(tǒng)的一個(gè)橋梁,它提供了用OpenGL在X窗口繪制的接口。GLX本身使用Xlib庫(kù)做窗口創(chuàng)建等工作,它出現(xiàn)的時(shí)候還沒(méi)有xcb這東西,xcb的出現(xiàn)本身就是為了代替Xlib,但是目前還沒(méi)有基于xcb的GLX,所以我們的OpenGL繪制將會(huì)使用Xlib創(chuàng)建窗口。(也就是說(shuō)上面的代碼我們用不到-_-)

畫(huà)布——Drawable

在X系統(tǒng)中,一個(gè)可以渲染的表面被稱(chēng)為一個(gè)Drawable(因?yàn)闆](méi)有找到什么中文詞語(yǔ)能夠準(zhǔn)確表達(dá)它的意思,所以還是用英文稱(chēng)呼最準(zhǔn)確)。X系統(tǒng)提供了兩種不同的Drawable:Window和Pixmap。GLX把Window封裝成了GLXWindow,把Pixmap封裝成GLXPixmap。要理解這個(gè)Drawable,最好的方法就是將它類(lèi)比成畫(huà)布,所謂的渲染就是在畫(huà)布上作畫(huà),清晰、準(zhǔn)確而且簡(jiǎn)單。

不管是GLXWindow還是GLXPixmap,在創(chuàng)建的時(shí)候都需要一個(gè)GLXFBConfig來(lái)說(shuō)明這塊畫(huà)布是什么樣的,也就是畫(huà)布的屬性。畫(huà)布的屬性包括顏色緩沖區(qū)的深度以及輔助緩沖區(qū)的類(lèi)型、質(zhì)量、大小等等。

為了兼容GLX1.2以及之前的版本,還有一種Drawable類(lèi)型——Window,注意這不是GLX封裝過(guò)后的GLXWindow,而是原始的Window。于是,GLXDrawable包括四種Drawable:GLXWindow、GLXPixmap、GLXPBuffer(對(duì)我們不重要,忽略之)以及Window。在X系統(tǒng)中,Window是與Visual結(jié)構(gòu)相關(guān)的,可以通過(guò)Visual結(jié)構(gòu)創(chuàng)建。

關(guān)于Visual,XVisualInfo,以及GLXFBConfig

早期X窗口系統(tǒng)使用Visual封裝相關(guān)的顏色屬性值(例如顏色類(lèi)型,顏色深度),這時(shí)候OpenGL還沒(méi)出世。

當(dāng)OpenGL出來(lái)之后,就弄出了一個(gè)XVisualInfo來(lái)擴(kuò)展Visual,添加了更多的功能,比如輔助緩沖區(qū),雙緩沖區(qū)等。這個(gè)XVisualInfo被用來(lái)創(chuàng)建OpenGL上下文。

1998年,GLX的1.3版出世,為了支持更多的功能(透明度,多重采樣,樣本緩沖區(qū)等等),就需要往里面加更多的東西,但是這些屬性已經(jīng)和視覺(jué)效果(Visual)關(guān)系不大了,于是就用推出了GLXFBconfig。

所以,到現(xiàn)在,最穩(wěn)妥的方式是使用GLXFBConfig來(lái)創(chuàng)建OpenGL上下文,但是你還是可以從FBConfig中獲取到Visual信息。

繪畫(huà)工具——Render Context

RenderContext的中文翻譯是渲染上下文,我認(rèn)為將它理解成繪畫(huà)工具比較貼切。渲染上下文就是一系列已經(jīng)存在的繪畫(huà)工具,這些工具叫狀態(tài)。設(shè)置好狀態(tài)之后就可以繪制了。繪畫(huà)工具和畫(huà)布的屬性必須要匹配,具體來(lái)說(shuō)就是:

支持相同類(lèi)型的渲染(RGBA或者顏色索引)

顏色緩沖區(qū)和輔助緩沖區(qū)的深度相同(RGB分量的尺寸大小要一樣)

都由一種X屏幕創(chuàng)建

只要畫(huà)布與繪畫(huà)工具兼容,那么多個(gè)繪畫(huà)工具(上下文)可以繪制到一張畫(huà)布(Drawable)上,同樣,一個(gè)繪畫(huà)工具可以繪制到多張畫(huà)布上。

在實(shí)際的代碼中,我們首先要?jiǎng)?chuàng)建的是和XServer的連接,這個(gè)連接在代碼里的名字是Display。很容易引起誤解的一個(gè)名字,我們要知道它就是與X服務(wù)器的連接。

使用OpenGL的繪制過(guò)程包括:

1、創(chuàng)建連接

2、選擇合適的顯示配置(需要用到連接)

3、創(chuàng)建窗口(需要用到連接和配置)

4、映射窗口(需要用到連接和窗口)

5、創(chuàng)建上下文(需要用到連接,配置)

6、關(guān)聯(lián)窗口和上下文(需要用到連接,窗口,配置)

7、繪制

完整的可運(yùn)行代碼如下所示:

#include

#include

#include

#include

#include

#include

#include

#include

#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091

#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092

typedef GLXContext (* glXCreateContextAttribsARBProc) (Display*, GLXFBConfig, GLXContext, Bool, const int*);

int main (int argc, char* argv[])

{

// The XOpenDisplay() function returns a Display structure that serves as the connection

// to the X server and that contains all the information about that X server.

// Display這個(gè)東西,與之前所講的那些都不同,它更像是一個(gè)畫(huà)布和繪畫(huà)工具集合。所有的東西都和它有關(guān),需要通過(guò)它來(lái)創(chuàng)建,狀態(tài)也會(huì)保存在它里面。

Display* display = XOpenDisplay(NULL);

if (!display)

{

printf("Failed to open X display\n");

exit(1);

}

// FBConfigs were added in GLX version 1.3

// 確保你的GLX版本在1.3之上

int glx_major, glx_minor;

if ( !glXQueryVersion( display, &glx_major, &glx_minor ) ||

( ( glx_major == 1 ) && ( glx_minor < 3 ) ) || ( glx_major < 1 ) )

{

printf("Invalid GLX version");

exit(1);

}

// Get a matching FB config

// A list of attribute/value pairs.

// 我們需要的顯示屬性,一會(huì)查查系統(tǒng)中有沒(méi)有滿(mǎn)足要求的

static int visual_attribs[] =

{

GLX_X_RENDERABLE, True, // If drawables can be renderd to by X.

GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, // Indicating what drawable types the frame buffer configuration supports. GLX_WINDOW_BIT, GLX_PIXMAP_BIT, GLX_PBUFFER_BIT

GLX_RENDER_TYPE, GLX_RGBA_BIT, // Indicating what type of GLX contexts can be made current to the frame buffer configuration. GLX_RGBA_BIT, GLX_COLOR_INDEX_BIT

GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR, // Visual type of associated visual.

GLX_RED_SIZE, 8,

GLX_GREEN_SIZE, 8,

GLX_BLUE_SIZE, 8,

GLX_ALPHA_SIZE, 8,

GLX_DEPTH_SIZE, 24,

GLX_STENCIL_SIZE, 8,

GLX_DOUBLEBUFFER, True,

None

};

printf( "Getting matching framebuffer configs\n" );

int fbcount;

GLXFBConfig* fbc = glXChooseFBConfig(display, DefaultScreen(display), visual_attribs, &fbcount); // 找找有沒(méi)有滿(mǎn)足要求的配置

if (!fbc)

{

printf( "Failed to retrieve a framebuffer config\n" );

exit(1);

}

printf( "Found %d matching FB configs.\n", fbcount );

GLXFBConfig bestFbc = fbc[0]; // 找一個(gè)配置保存

// Be sure to free the FBConfig list allocated by glXChooseFBConfig()

XFree( fbc );

// Get a visual

// 獲取視覺(jué)效果信息

XVisualInfo *vi = glXGetVisualFromFBConfig( display, bestFbc );

printf( "Chosen visual ID = 0x%x\n", vi->visualid );

printf("Creating colormap\n");

// 窗口屬性,最重要的是顏色,必須創(chuàng)建與視覺(jué)效果匹配的顏色屬性,這在創(chuàng)建Window的時(shí)候有用

XSetWindowAttributes swa;

Colormap cmap;

swa.colormap = cmap = XCreateColormap(display,

RootWindow(display, vi->screen),

vi->visual, AllocNone);

swa.background_pixmap = None;

swa.border_pixel = 0;

swa.event_mask = StructureNotifyMask;

printf("Creating window\n");

Window win = XCreateWindow(display, RootWindow(display, vi->screen),

0, 0, 100, 100, 0, vi->depth, InputOutput,

vi->visual,

CWBorderPixel | CWColormap | CWEventMask, &swa);

if (!win)

{

printf("Failed to create window.\n");

exit(1);

}

// Done with the visual info data

XFree (vi); // 釋放獲取的視覺(jué)效果信息

XStoreName(display, win, "GL 3.0 Window");

printf("Mapping window\n");

XMapWindow(display, win);

// NOTE:It is note necessary to create or make current to a context before

// calling glXGetProcAddressARB

// 獲取創(chuàng)建上下文的函數(shù)地址

glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0;

glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc)

glXGetProcAddressARB((const GLubyte*)"glXCreateContextAttribsARB");

GLXContext ctx = 0;

int context_attribs[] =

{

GLX_CONTEXT_MAJOR_VERSION_ARB, 3,

GLX_CONTEXT_MINOR_VERSION_ARB, 0,

None

};

printf("Creating context 3.0\n");

// 創(chuàng)建上下文

ctx = glXCreateContextAttribsARB(display, bestFbc, 0, True, context_attribs);

// Sync to ensure any errors generated are processed.

XSync(display, False);

if (ctx == 0)

{

printf("Can't create GL 3.0 context.\n");

exit(1);

}

// Sync to ensure any errors generated are processed.

XSync(display, False);

printf("Draw with context\n");

glXMakeCurrent(display, win, ctx);

glClearColor(0, 0.5, 1, 1);

glClear(GL_COLOR_BUFFER_BIT);

glXSwapBuffers(display, win);

sleep(1);

glClearColor(1, 0.5, 0, 1);

glClear(GL_COLOR_BUFFER_BIT);

glXSwapBuffers(display, win);

sleep(1);

glXMakeCurrent(display, 0, 0);

glXDestroyContext(display, ctx);

XDestroyWindow(display, win);

XFreeColormap(display, cmap);

XCloseDisplay(display);

return 0;

}

在編譯前,首先需要安裝OpenGL的運(yùn)行環(huán)境,需要安裝兩個(gè)庫(kù):libgl1-mesa-dev和Xlib,命令是sudo apt install libgl1-mesa-dev Xlib。

安裝完成后,使用g++ -o helloengine_openglxlib helloengine_openglxlib.cpp -lGL -lX11命令來(lái)編譯代碼,-o后接的是輸出的可執(zhí)行文件名,你可以隨便取名字;再后面是代碼文件名,之后跟的是鏈接庫(kù)的名字(GL和X11)。這里解釋一下X11是什么,所謂的X11就是X協(xié)議的第11個(gè)版本,也就是說(shuō)11只不過(guò)是版本號(hào)而已。

運(yùn)行helloengine_openglxlib,我們得到了如下的結(jié)果:

總結(jié)

一開(kāi)始寫(xiě)代碼的時(shí)候非常不適應(yīng),不僅是對(duì)開(kāi)發(fā)環(huán)境,對(duì)這些結(jié)構(gòu)名、類(lèi)名都不熟悉,導(dǎo)致敲代碼頻頻敲錯(cuò),然后編譯的時(shí)候各種報(bào)錯(cuò)。但是,多敲幾遍,多看幾遍之后慢慢就熟悉了,漸漸的就有了“代碼感”。下面列出的參考資料里推薦閱讀linux圖形界面編程基礎(chǔ)知識(shí)和OpenGL? Graphics with the X Window System?加深對(duì)Linux和Linux下OpenGL編程的理解,非常有用,強(qiáng)烈推薦!

參考資料

總結(jié)

以上是生活随笔為你收集整理的linux opengl 实例,一篇文章入门Ubuntu的OpenGL开发的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。