做爲一個圖形圖像方向的研究生,我常常都在和 OpenGL 、OpenCV 等多種 C++ 庫打交道。這些庫遵循着不一樣的規則和用法;另外,爲了讓本身的程序具備更多的交互能力,編寫界面也是一個屢見不鮮的工做。css
然而,隨着工程複雜性的增長,庫的管理和界面的維護也變得愈來愈困難:一方面,庫的增長和刪除不只會增長學習成本,也會對系統的邏輯層帶來影響。而另外一方面,若是要讓本身的項目易於維護,就要儘量地應用設計模式,讓邏輯和界面分離。但對於科研,一味陷入設計模式的桎梏又會帶來過早優化的問題,影響科研進度。html
直到後來,我接觸到了 openFrameworks ,簡直有種相逢恨晚的感受。openFrameworks 封裝了經常使用的 C++ 庫,在此基礎上提供了一個直觀統一的接口,也大幅簡化了編寫界面的流程,使得開發圖形程序變得很輕鬆。linux
本文將爲你們介紹這個讓人着迷的開發框架 —— openFrameworks。git
openFrameworks(如下簡稱 oF) 是一個開源的、跨平臺的 C++ 工具包,它的設計目的爲開發創造過程提供一個更加簡單和直觀的框架。github
oF 的強大之處在於,它不只是一個通用的膠水(glue),同時它還封裝了多種經常使用的庫,包括:json
這些庫雖然遵循着不一樣的規則和用法,但 oF 在它們基礎上提供了一個通用的接口,使得使用它們變得很容易。設計模式
除此以外,oF 的另外一亮點在於它具備很好的跨平臺特性。目前它支持 5 種操做系統(Windows、OSX、Linux、iOS、Android)以及 4 種 集成開發環境(XCode、Code::Blocks、Visual Studio、Eclipse)。bash
下面介紹如何在 Linux 下安裝和配置 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
|
安裝完依賴後,回到上一級目錄:
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
|
其中:
咱們能夠看看 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。。
雙擊 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 插件哦。。
--bb
)。使用這些插件的方法很簡單:
git clone
這個項目;make && make run
編譯和執行它看看結果。