介紹一個開源的 C++ 開發框架 openFrameworks 。

做爲一個圖形圖像方向的研究生,我常常都在和 OpenGL 、OpenCV 等多種 C++ 庫打交道。這些庫遵循着不一樣的規則和用法;另外,爲了讓本身的程序具備更多的交互能力,編寫界面也是一個屢見不鮮的工做。css

然而,隨着工程複雜性的增長,庫的管理和界面的維護也變得愈來愈困難:一方面,庫的增長和刪除不只會增長學習成本,也會對系統的邏輯層帶來影響。而另外一方面,若是要讓本身的項目易於維護,就要儘量地應用設計模式,讓邏輯和界面分離。但對於科研,一味陷入設計模式的桎梏又會帶來過早優化的問題,影響科研進度。html

直到後來,我接觸到了 openFrameworks ,簡直有種相逢恨晚的感受。openFrameworks 封裝了經常使用的 C++ 庫,在此基礎上提供了一個直觀統一的接口,也大幅簡化了編寫界面的流程,使得開發圖形程序變得很輕鬆。linux

本文將爲你們介紹這個讓人着迷的開發框架 —— openFrameworks。git

什麼是 openFrameworks

openFrameworks(如下簡稱 oF) 是一個開源的、跨平臺的 C++ 工具包,它的設計目的爲開發創造過程提供一個更加簡單和直觀的框架。github

openFrameworks

oF 的強大之處在於,它不只是一個通用的膠水(glue),同時它還封裝了多種經常使用的庫,包括:json

這些庫雖然遵循着不一樣的規則和用法,但 oF 在它們基礎上提供了一個通用的接口,使得使用它們變得很容易。設計模式

除此以外,oF 的另外一亮點在於它具備很好的跨平臺特性。目前它支持 5 種操做系統(Windows、OSX、Linux、iOS、Android)以及 4 種 集成開發環境(XCode、Code::Blocks、Visual Studio、Eclipse)。bash

安裝和配置 oF

下面介紹如何在 Linux 下安裝和配置 oF 。服務器

下載 oF

訪問 oF 的官方下載頁面,找到適用於你的操做系統和 IDE 的版本,點擊下載。例如,個人計算機是 Linux Arch 64位的系統,因此選擇的是 code::blocks (64 bit)。網絡

下載 openFrameworks

安裝依賴

下載完成後,將其解壓,開啓終端,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多閱讀 example 的示例代碼是個好習慣。。

guiExample 程序

編譯項目生成器

爲了方便往後建立工程,oF 還提供了一個項目生成器 projectGenerator 。使用它前一樣須要先編譯。回到 compileOF.sh 腳本所在的目錄,敲入以下命令:

 

1
$ ./compilePG.sh

 

完成後,在 oF 的根目錄下找到 projectGenerator 目錄,進去裏面能夠找到 projectGenerator ,咱們能夠執行它:

 

1
2
$ cd ../../projectGenerator
$ ./projectGenerator

 

程序界面以下圖所示。點擊左側的每一個黑色按鈕將能夠修改項目名、生成路徑,以及依賴的插件(Addon)。

projectGenerator

點擊右下角的 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主要參考了 Jeff Crouse 所編寫的教程 ofTutorials - Chapter 1 - Getting Started。。

testApp.cpp

雙擊 mySketch.cbp 文件,打開 Code::Blocks 開發環境,在左邊的項目管理器中雙擊打開 test.App 文件。以下圖所示:

使用 Code::Blocks 開發程序

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這個交替頻率就是幀率,它的默認值取決於你的電腦的處理速度。update() 一般用來更新你的程序的狀態(例如改變變量的值),而 draw() 則經常使用來在你的窗口中繪製內容;
  • keyPressedkeyReleasedmouseMovedmouseDraggedmousePressedmouseReleasedwindowResizedgotMessagedragEvent - 與前面三種函數不一樣,這類函數僅當用戶觸發某類事件纔會被調用。

咱們先試着直接編譯這個項目,此時的程序窗口裏尚未東西:

繪製圖形

以後,咱們能夠試着在窗口中畫一個圓。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貝塞爾曲線 的控制點比較難以掌握。若是你用過 Photoshop 裏的鋼筆工具,你大概就會明白是怎麼一回事。。

讓形狀動起來

接下來咱們將編寫代碼讓窗口裏的圖形動起來。主要的思路就是用兩個變量控制圓的座標,而後在程序的運行過程當中改變這個變量。在 test.cpp 文件的開頭聲明兩個變量,分別用於存放圓的 x 座標和 y 座標:

 

1
2
3
4
#include "testApp.h"
 
int myCircleX;
int myCircleY;

 

在 setup() 函數中爲這兩個變量添加初始值 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若是不考慮拉伸窗口,也能夠用 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額外提一下, 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 、sad 四個按鍵控制圓圈的運動。出於魯棒性考慮,小寫和大寫的字母都要考慮進去,由於按鍵是經過十進制的 ASCII 碼來判斷的,而大寫字母和小寫字母的 ASCII 碼是不一樣的。上面的代碼也能夠等價的用 ASCII 碼來代替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什麼?有牆?!其實幾乎全部插件都是託管在 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)。

使用這些插件的方法很簡單:

  1. 訪問這個插件的 Github 項目主頁;
  2. 複製它的代碼倉庫地址;
  3. 進入你的 oF 根目錄下的 addons 目錄,git clone 這個項目;
  4. 若是這個項目自帶 example ,能夠直接 make && make run 編譯和執行它看看結果。

相關連接

相關文章
相關標籤/搜索