轉自:https://www.cnblogs.com/lidabo/p/9134174.html 此處僅供學習,版權屬原作者; 作為一個圖形圖像方向的研究生,我經常都在和 OpenGL 、OpenCV 等多種 C++ 庫打交道。這些庫遵循著不同的規則和用法;另外,為了讓自己的程式具有更多的交互能力, ...
轉自:https://www.cnblogs.com/lidabo/p/9134174.html 此處僅供學習,版權屬原作者;
作為一個圖形圖像方向的研究生,我經常都在和 OpenGL 、OpenCV 等多種 C++ 庫打交道。這些庫遵循著不同的規則和用法;另外,為了讓自己的程式具有更多的交互能力,編寫界面也是一個家常便飯的工作。
然而,隨著工程複雜性的增加,庫的管理和界面的維護也變得越來越困難:一方面,庫的增加和刪除不僅會增加學習成本,也會對系統的邏輯層帶來影響。而另一方面,如果要讓自己的項目易於維護,就要儘可能地應用設計模式,讓邏輯和界面分離。但對於科研,一味陷入設計模式的桎梏又會帶來過早優化的問題,影響科研進度。
直到後來,我接觸到了 openFrameworks ,簡直有種相逢恨晚的感覺。openFrameworks 封裝了常用的 C++ 庫,在此基礎上提供了一個直觀統一的介面,也大幅簡化了編寫界面的流程,使得開發圖形程式變得很輕鬆。
本文將為大家介紹這個讓人著迷的開發框架 —— openFrameworks。
什麼是 openFrameworks
openFrameworks(以下簡稱 oF) 是一個開源的、跨平臺的 C++ 工具包,它的設計目的為開發創造過程提供一個更加簡單和直觀的框架。
oF 的強大之處在於,它不僅是一個通用的膠水(glue),同時它還封裝了多種常用的庫,包括:
- OpenGL、GLEW、GLUT、libtess2、cairo - 用於處理圖形;
- rtAudio、PortAudio、OpenAL、Kiss FFT、FMOD - 用於音頻的輸入、輸出和分析;
- FreeType - 用於字體顯示;
- FreeImage - 用於圖像存儲和載入;
- Quicktime、GStreamer、videoInput - 用於視頻播放和截取;
- Poco - 用於開髮網絡應用;
- OpenCV - 用於電腦視覺;
- Assimp - 用於讀入 3D 模型。
這些庫雖然遵循著不同的規則和用法,但 oF 在它們基礎上提供了一個通用的介面,使得使用它們變得很容易。
除此之外,oF 的另一亮點在於它具有很好的跨平臺特性。目前它支持 5 種操作系統(Windows、OSX、Linux、iOS、Android)以及 4 種 集成開發環境(XCode、Code::Blocks、Visual Studio、Eclipse)。
安裝和配置 oF
下麵介紹如何在 Linux 下安裝和配置 oF 。
下載 oF
訪問 oF 的官方下載頁面,找到適用於你的操作系統和 IDE 的版本,點擊下載。例如,我的電腦是 Linux Arch 64位的系統,所以選擇的是 code::blocks (64 bit)。
安裝依賴
下載完成後,將其解壓,開啟終端,cd
到解壓後目錄,例如:
1 | $ cd $HOME/Documents/programming/openFrameworks |
之後,根據你的 Linux 發行版的不同,cd
進入 scripts/linux/<操作系統發行版名稱> ,例如:
1 | $ cd scripts/linux/archlinux |
執行兩個命令,安裝 code::block 和其他依賴(需要 root 許可權):
1 2 3 | $ sudo ./install_codeblocks.sh $ sudo ./install_dependencies.sh $ sudo ./install_codecs.sh |
編譯 oF
安裝完依賴後,回到上一級目錄:
1 | $ cd .. |
編譯 oF:
1 | $ ./compileOF.sh |
編譯過程中,如果你和我一樣遇到找不到 freetype.h 的問題,可能是 FreeType 在 2.5.1 之後改變了頭文件的結構導致的。需要將根目錄里的 /libs/openFrameworks/graphics/
目錄下的 ofTrueTypeFont.cpp 開頭部分改為:
1 2 3 4 5 6 7 8 9 10 11 | #include "ft2build.h" /* Corrected setup of include files for freetype as of 2.5.1 dh #include "freetype2/freetype/freetype.h" #include "freetype2/freetype/ftglyph.h" #include "freetype2/freetype/ftoutln.h" #include "freetype2/freetype/fttrigon.h" */ #include FT_FREETYPE_H #include FT_GLYPH_H #include FT_OUTLINE_H #include FT_TRIGONOMETRY_H |
此時的 oF 已經可以工作了。我們可以測試它提供的示例。cd 到根目錄里的 /examples/gui/guiExample/ 目錄,編譯該工程並執行:
1 2 | $ make $ make run |
將會運行一個界面如下圖的程式。與左側面板里的控制項交互將可以改變該形狀的屬性1 1多閱讀 example 的示例代碼是個好習慣。。
編譯項目生成器
為了方便日後創建工程,oF 還提供了一個項目生成器 projectGenerator 。使用它前同樣需要先編譯。回到 compileOF.sh
腳本所在的目錄,敲入如下命令:
1 | $ ./compilePG.sh |
完成後,在 oF 的根目錄下找到 projectGenerator 目錄,進去裡面可以找到 projectGenerator ,我們可以執行它:
1 2 | $ cd ../../projectGenerator $ ./projectGenerator |
程式界面如下圖所示。點擊左側的每個黑色按鈕將可以修改項目名、生成路徑,以及依賴的插件(Addon)。
點擊右下角的 GENERATE PROJECT
按鈕後,將會在 Path 欄位指定的路徑中生成一個項目,如上圖所示就是 /home/ehome/Documents/programming/openframeworks/apps/myApps/mySketch
:
1 2 3 | $ cd /home/ehome/Documents/programming/openframeworks/apps/myApps $ ls addons.make bin config.make Makefile mySketch.cbp mySketch.workspace src |
其中:
- addons.make 文件 - 用於維護這個工程所依賴的插件列表;
- config.make 文件 - 用於添加查找路徑,修改優化標記以及其他的設置;
- Makefile 文件 - 工程的 Makefile ,一般不需要直接修改它。在 oF 中,make 的目標包括:
- Debug:生成帶有調試標記的可執行程式;
- Release:生成經編譯器優化的可執行程式;
- clean:清除目標文件和可執行程式;
- CleanDebug:只清除 debug 目標的生成結果;
- CleanRelease:只清除 release 目標的生成結果;
- help:列印幫助信息;
- run:執行生成的可執行程式。
- mySketch.cbp 和 mySketch.workspace 文件 - Code::Blocks 的工程文件。
註冊環境變數
我們可以看看 Makefile 文件的內容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | $ cat Makefile # Attempt to load a config.make file. # If none is found, project defaults in config.project.make will be used. ifneq ($(wildcard config.make),) include config.make endif # make sure the the OF_ROOT location is defined ifndef OF_ROOT OF_ROOT=../../.. endif # call the project makefile! include $(OF_ROOT)/libs/openFrameworksCompiled/project/makefileCommon/compile.project.mk |
如上所示,編譯 openFrameWorks 的工程時,系統需要從 oF 的根路徑中引入另一個名為 compile.project.mk
的 Makefile,這個根路徑存儲在 OF_ROOT
變數中,預設值是 ../../..
,即當前目錄往上三級的目錄。之所以使用這個預設值,是因為使用 projectGenerator 生成的項目都預設存放在 oF根目錄/apps/myApps
目錄下。為了方便在其他地方創建和編譯工程,可以人為地定義一個 OF_ROOT
變數。將下麵這一行添加到用戶主目錄下的 .bashrc
文件中:
1 | export OF_ROOT=<你的 oF 根目錄> |
入門實例
接下來將介紹如何開發基於 oF 的 C++ 程式2 2主要參考了 Jeff Crouse 所編寫的教程 ofTutorials - Chapter 1 - Getting Started。。
testApp.cpp
雙擊 mySketch.cbp 文件,打開 Code::Blocks 開發環境,在左邊的項目管理器中雙擊打開 test.App 文件。如下圖所示:
testApp.cpp 將會是你的好朋友。在編輯視窗中,你可以看到如下的內容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #include "testApp.h" //-------------------------------------------------------------- void testApp::setup(){ } //-------------------------------------------------------------- void testApp::update(){ } //-------------------------------------------------------------- void testApp::draw(){ } //-------------------------------------------------------------- void testApp::keyPressed(int key){ } ... |
上面的代碼包含了 4 類函數:
setup
- 這個函數將在應用程式生命期的最開始就被調用,甚至在你編寫的程式視窗打開之前。利用這個函數,我們可以做一些準備工作,例如在視窗打開之前,先修改視窗的大小;update
和draw
- 當setup
函數運行完成後,系統將進入一個update
和draw
不斷交替運行的迴圈,這個迴圈將持續到程式結束。也就是說,setup()
運行完成後,update()
開始運行,然後是draw()
,然後又是update()
,然後又是draw()
…… 3 3這個交替頻率就是幀率,它的預設值取決於你的電腦的處理速度。update()
通常用來更新你的程式的狀態(例如改變變數的值),而draw()
則常用來在你的視窗中繪製內容;keyPressed
、keyReleased
、mouseMoved
、mouseDragged
、mousePressed
、mouseReleased
、windowResized
,gotMessage
、dragEvent
- 與前面三種函數不同,這類函數僅當用戶觸發某類事件才會被調用。
我們先試著直接編譯這個項目,此時的程式視窗里還沒有東西:
繪製圖形
之後,我們可以試著在視窗中畫一個圓。oF 提供了 ofCircle
函數用於繪製圓。
往 draw
函數裡頭添加一句內容,:
1 2 3 | void testApp::draw(){ ofCircle(200, 300, 60); } |
第二行告訴系統在坐標 (200, 300) 處畫一個半徑為 60 的圓。
添加顏色
現在這個圓看起來很單調,可以給這個圓添加顏色。oF 提供了 ofSetColor
函數用於設置顏色。將 draw()
函數改為:
1 2 3 4 | void testApp::draw(){ ofSetColor(255, 0, 255); ofCircle(200, 300, 60); } |
新加的這一行(第2行)告訴系統在繪製圖形前選擇一個顏色,這個顏色的 R、G、B 三原色的色值分別為 (255, 0, 255) 。
我們可以用同樣的方法再畫一個青色的圓:
1 2 3 4 5 6 7 | void testApp::draw(){ ofSetColor(255, 0, 255); ofCircle(200, 300, 60); ofSetColor(0, 255, 255); ofCircle(500, 500, 100); } |
其他的形狀
除了畫圓,oF 也可以畫其他的圖案:
ofRect
- 畫一個矩形。參數是:(x, y, width, height) ;ofTriangle
- 畫一個三角形。參數是三個頂點的坐標:(x1, y1, x2, y2, x3, y3)ofLine
- 畫一條線段。參數是兩個端點的坐標 (x1, y1, x2, y2)ofEllipse
- 畫橢圓。參數是:(x, y, width, height)ofCurve
- 畫一條從點 (x1, y1) 到 (x2, y2) 的貝塞爾曲線,曲線的形狀由兩個控制點 (x0, y0) 和 (x3, y3) 控制 4 4貝塞爾曲線 的控制點比較難以掌握。如果你用過 Photoshop 里的鋼筆工具,你大概就會明白是怎麼一回事。。
讓形狀動起來
接下來我們將編寫代碼讓視窗里的圖形動起來。主要的思路就是用兩個變數控制圓的坐標,然後在程式的運行過程中改變這個變數。在 test.cpp 文件的開頭聲明兩個變數,分別用於存放圓的 x 坐標和 y 坐標:
1 2 3 4 | #include "testApp.h" int myCircleX; int myCircleY; |
在 setup()
函數中為這兩個變數添加初始值 5 5別忘了前面介紹的每類函數的用途。:
1 2 3 4 | void testApp::setup(){ myCircleX = 0; myCircleY = 200; } |
用這兩個變數繪製圖形:
1 2 3 4 | void testApp::draw(){ ofSetColor(255, 0, 255); ofCircle(myCircleX, myCircleY, 60); } |
要在運行過程中修改這兩個變數,可以在 update()
函數中編寫相關代碼。例如,讓這個圓一直向右移動,當超出屏幕時,再回到原來開始的地方:
1 2 3 4 5 | void testApp::update(){ myCircleX++; if (myCircleX > ofGetWindowWidth()) myCircleX = 0; } |
其中,第 3 行的 ofGetWindowWidth()
函數用來獲取視窗的寬度6 6如果不考慮拉伸視窗,也可以用 1024 這個值代替,因為 oF 的預設視窗大小是 1024x768 。。
改變幀率
你可能會發現上面的程式在運行的時候有一個問題:圓圈的運動存在時快時慢的情況。如前面所說,這是由於你的程式的幀率,或者說 update()
函數和 draw()
函數交替執行的頻率不穩定造成的。在 draw()
函數中添加下麵這一行代碼可以在視窗的左上方顯示幀率信息:
1 | ofDrawBitmapString(ofToString(ofGetFrameRate())+"fps", 10, 15); |
你可以發現這個數值會在程式運行的過程中存在較大波動,尤其是當你同時還在執行其他耗費計算資源的任務時,這個數值會下降得更加明顯,相應的這個圓圈的運動速度也會跟著變慢。
讓視窗中的動畫變得更加平滑的方法是把幀率限制在一個合理的值,例如 60 fps :
1 2 3 4 5 6 | void testApp::setup(){ ofSetFrameRate(60); myCircleX = 300; myCircleY = 200; } |
如果你覺得經過這麼一改動之後這個圓圈慢的讓你無法忍受,你可以通過修改圓圈的移動速度來加速。例如:
1 2 3 4 5 | void testApp::update(){ myCircleX+=4; if (myCircleX > ofGetWindowWidth()) myCircleX = 0; } |
添加交互
接下來,我們將為這個程式添加鍵盤和滑鼠的交互。要添加鍵盤交互,可以通過修改 keyPressed()
函數和 keyReleased()
函數來完成。其中,keyPressed()
捕獲的是按下鍵盤按鍵的事件,而 keyReleased()
捕獲的是鬆開鍵盤按鍵的事件 7 7額外提一下, oF 似乎並不能很好的識別 DVORAK 等其他鍵盤佈局。解決方法見這個帖子。。
例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | void testApp::keyPressed(int key){ if('w' == key || 'W' == key) { myCircleY-=4; } if('s' == key || 'S' == key) { myCircleY+=4; } if('a' == key || 'A' == key) { myCircleX-=4; } if('d' == key || 'D' == key) { myCircleX+=4; } } |
將通過 w
、s
、a
、d
四個按鍵控制圓圈的運動。出於魯棒性考慮,小寫和大寫的字母都要考慮進去,因為按鍵是通過十進位的 ASCII 碼來判斷的,而大寫字母和小寫字母的 ASCII 碼是不同的。上面的代碼也可以等價的用 ASCII 碼來代替8 8溫馨小提示:Linux 下可以通過 man ascii
查詢每個字母對應的 ASCII 編碼。一般人我不告訴他。:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | void testApp::keyPressed(int key){ if(119 == key || 87 == key) // w key { myCircleY-=4; } if(115 == key || 83 == key) // s key { myCircleY+=4; } if(97 == key || 65 == key) // a key { myCircleX-=4; } if(100 == key || 68 == key) // d key { myCircleX+=4; } } |
添加滑鼠事件則通過修改 mouseMoved()
、mouseDragged()
、mousePressed()
和 mouseReleased()
來完成,顧名思義,分別捕獲的是滑鼠的移動、拖拽、單擊、鬆開操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | void testApp::mouseMoved(int x, int y ){ } void testApp::mouseDragged(int x, int y, int button){ } void testApp::mousePressed(int x, int y, int button){ } void testApp::mouseReleased(int x, int y, int button){ } |
例如,我們可以編寫代碼實現滑鼠拖動圓圈:
1 2 3 4 5 6 7 8 9 10 | void testApp::mouseDragged(int x, int y, int button){ if (0 == button) { // left button float distance = ofDist(myCircleX, myCircleY, x, y); if (distance < 100){ myCircleX = x; myCircleY = y; } } } |
第 2 行用於判斷觸發此事件的按鍵是否為左鍵;第 3 行的 ofDist()
函數用於計算滑鼠當前位置和圓心的距離。如果這個距離小於半徑 100 ,則可以判斷當前滑鼠落在這個圓圈的範圍以內,可以用滑鼠的位置代替圓心的位置。
其他優化
調整圓圈精度
如果近一點觀察圓圈,你可能會發現圓圈的周圍有點粗糙。
可以修改圓圈的繪製精度來讓圓圈更加平滑。在 setup()
函數中添加這一句:
1 | ofSetCircleResolution(120); |
抗鋸齒和垂直同步
抗鋸齒和垂直同步也是常常使用的優化畫面的手段:
1 2 | ofSetVerticalSync(true); ofEnableSmoothing(); |
實用的插件
oF 的另外一大殺手鐧在於它的社區非常活躍,現在已經開發出了數量可觀的第三方插件。這裡只收集了極小一部分實用插件。更全面的插件列表可以訪問 ofxaddons.com 9 9什麼?有牆?!其實幾乎所有插件都是托管在 Github 上的。所以如果在 Github 上搜 “ofx” ,也可以找到這些 oF 插件哦。。
- ofxUI - 華麗麗的 UI 庫。提供了很多新穎而實用的界面控制項。
- ofxCv - OpenCV 的另一套可選的 oF 插件(oF 自帶一個 oFOpenCv 插件);
- ofxLibRocket - 對 librocket 庫的封裝,這個庫允許你使用 html 和 css 來佈局 C++ 視窗;
- ofxTrueTypeFontUC - 對 ofTrueTypeFont 類的擴展,使其支持 Unicode 字元(例如漢字);
- ofxPCL - 對 PCL(一個專門用於處理點雲的庫) 的封裝;
- ofxTimeline - 一個用來繪製可編輯的 timeline 控制項的插件;
- ofxMidi - Midi 音樂的插件;
- ofxSpeech - 語音識別插件;
- ofxVideoRecorder - 錄製視頻插件;
- ofxImageSequence - 一個用於像播放視頻一樣播放圖像序列的插件;
- ofxGifEncoder - 生成 Gif 動畫的插件;
- ofxVolumetrics - 簡單的體繪製插件;
- ofxDelaunay -
- ofxFft - 對兩個用於進行傅里葉變換的庫 FFTW 和 KissFFT 的封裝;
- ofxNodejs - 橋接 Node.js 的插件;
- ofxLua - 橋接 Lua 的插件;
- ofxBox2d - 對流行的 2D 物理模擬庫 box2d 的封裝;
- ofxBullet - 對另一個物理模擬庫 Bullet Physics 的封裝;
- ofxLearn - 通用的機器學習插件,支持分類、回歸、聚類等任務;
- ofxJSON - 對 Json 庫 JsonCpp 的封裝;
- ofxHttpServer - 一個基於 libmicrohttpd 的 http 伺服器插件;
- ofxAddonTemplate - 一個空的目錄框架,可以借鑒它自己編寫插件(這都有……
--bb
)。
使用這些插件的方法很簡單:
- 訪問這個插件的 Github 項目主頁;
- 複製它的代碼倉庫地址;
- 進入你的 oF 根目錄下的 addons 目錄,
git clone
這個項目; - 如果這個項目自帶 example ,可以直接
make && make run
編譯和執行它看看結果。
相關鏈接
- About openFrameworks:更多關於 openFrameworks 的資料,包括設計方法;
- tutorials:更多 openFrameworks 的教程;
- Official Documentation:官方文檔;
- oF Forum:官方論壇;
- ofxaddons.com:這裡搜集了非常豐富的 openFrameworks 插件;
- Programming Interactivity:一本介紹 openFrameworks、Processing 以及 Arduino 的好書;