Install OpenCV Library for Qt Creator (update for 64bit)

Qt 是一個好用且完整的 GUI (Graphical User Interface) 開發程式,本身也提供一個 IDE (Integrated Development Environment) 『Qt Creator』來開發 C++ 專案。這裡要說明如何在 Qt Creator 上建置 OpenCV 的開發環境,讓 Qt 與 OpenCV 結合在一起。雖然 Qt 提供 Visual Studio 的 plug-in,可直接在 VC 上寫 GUI,但透過 Qt Creator 來設計 GUI 會比較快速且美觀。

大致流程可分為,1. Qt 安裝和 OpenCV 安裝;2. 用 mingw compiler 編譯 OpenCV library;3. Qt 專案上的環境設置。

1.Qt 安裝和 OpenCV 安裝

http://www.qt.io/download/ 點入 Qt download 頁面,回答一些問題及輸入基本資料後,Qt 會依據你的作業系統推薦安裝版本,只要按下 Download Now,就會開始安裝了。

2016-03-23_140954

安裝完後就可以使用 Qt Creator 來開發 C++ 專案了,但目前還無法使用 OpenCV。

2016-03-23_141158

接著到 http://opencv.org/ 安裝 OpenCV,選擇所屬的作業系統來安裝,安裝完後在 opencv 資料夾裡面會有兩個子資料夾,分別是C:\opencv\build 和 C:\opencv\sources。

2.compile OpenCV library for mingw/g++ compiler

(1) 到 https://cmake.org/download/ 下載 Cmake,且安裝至 C:\。將系統變數 Path 加入下列 Qt\bin 與 Cmake\bin 兩個路徑。(電腦>>內容>>進階系統設定>>環境變數>> path)

C:\Qt\Tools\mingw492_32\bin

C:\cmake-3.5.0-win32-x86\bin

(2) 新增資料夾 C:\opencv-mingw,開啟 cmake-gui.exe 設定 source code :C:/opencv/sources 和 build the binaries : C:\opencv-mingw。2016-03-23_142654

按下 Configure,選擇 MinGW Makefiles 與 Specify native compilers。2016-03-23_142849

選擇編譯器,到剛安裝 Qt 的資料夾裡面,找已經安裝好的 compiler mingw/g++。

2016-03-23_143359

接著就可以按下 Generate。將 WITH_QT 給打勾。打勾之後,再 Configure 一次,然後 Generate,這樣就完成了初步安裝。2016-03-23_143719

(3) 編譯 OpenCV library。打開 cmd console 到 C:\opencv-mingw,輸入 :  mingw32make。建置需要一些時間...2016-03-23_144230

完成後再輸入 : mingw32make install 2016-03-23_144605現在你已經完成 OpenCV 的建置了,可以在 Qt Creator 上 使用 OpenCV library。

3.在 Qt Creator 上新增專案

新增一個 Qt Widget Application,

2016-03-23_145305

在 pro 裡面設定 includepath 和 libraries location :

INCLUDEPATH += C:\\opencv-mingw\\install\\include
LIBS += -LC:\\opencv-mingw\\install\\x86\\mingw\\lib \
-llibopencv_core2412.dll \
-llibopencv_highgui2412.dll \
-llibopencv_imgproc2412.dll

2016-03-23_145747

接著就可以在 Qt IDE 上面寫 OpenCV 的 code 了,以下是簡單的 OpenCV image read & show 的程式。

#include "mainwindow.h"
#include <QApplication>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/imgcodecs/imgcodecs.hpp>

using namespace cv;

int main(int argc, char *argv[])
 {
 //QApplication a(argc, argv);
 //MainWindow w;
 //w.show();

//return a.exec();
 Mat3b in_img;
 in_img = imread("D://6_QT//lena_std.bmp",1);
 imshow("test", in_img);
 waitKey(0);

return 0;
 }

 

2016-03-23_150556

 

 

4. For Windows 64 bit 環境

在 64 bit 安裝與上述的步驟一樣,但有些要特別注意的地方。

第一,Qt Download 並沒有提供 64bit Source,所以到以下目錄去下載,

https://sourceforge.net/projects/qt64ng/files/qt/x86-64/5.4.1/mingw-4.9/sjlj/

並且安裝至 C:\Qt,除了安裝 64bit 外,還要至 Qt 官網下載 32bit 版本,在官網點選 install online 後,選擇一個版本(這裡以 5.6版為例) 和 Tools (Qtcreator & mingw492_32),一樣安裝至 C:\Qt 資料夾內。而 OpenCV 版本的話,我下載最新版本( OpenCV3.2.0, 2017/05)。

第二,資料都安裝妥當後,接下來設定電腦環境變數,除了在 32bit 所提到的

C:\Qt\Tools\mingw492_32\bin

C:\cmake-3.5.0-win32-x86\bin

還要加入以下兩個路徑,

C:\Qt\qt-5.4.1-x64-mingw492r1-sjlj-opengl-rev1\mingw64\bin (64bit 版本)

C:\Qt\Tools\mingw492_32\i686-w64-mingw32\bin

接下來重新開機後,就可以開始上述 2. CMake 的步驟了。

第三,mingw32-make install 完畢之後,將

C:\opencv-mingw\install\x86\mingw\bin

也加入系統環境變數裡。且在 pro 裡面設定 includepath 和 libraries location 時,

LIBS 多加入 -llibopencv_imgcodecs320.dll (因為 imread 在 OpenCV3 之後被定義到 imgcodecs 裡面)。

完成以上的步驟後,一樣可以在 64bit 環境下,用 QtCreator 寫 OpenCV 。


 

reference : 

http://www.laganiere.name/opencvCookbook/chap1s1_2.shtml

https://zahidhasan.wordpress.com/2014/08/19/qt-5-3-1-64-bit-mingw-windows/

reference for 64 bit :

https://wiki.qt.io/How_to_setup_Qt_and_openCV_on_Windows

http://stackoverflow.com/questions/34497099/opencv-undefined-reference-to-imread

Changing the icon of an exe in VC2008 & Qt Creator (執行檔圖示的更改)

在程式設計完成後,執行檔(.exe) 圖示永遠都長得一樣,只是替換不同名稱而已。程式越寫越多就想要更改 .exe 的 icon 來區別不同的執行檔。

更換執行檔的圖示基本上有兩個步驟,(1) 產出想要的 .ico 圖檔(2) 在 compile 之前完成 icon 的設定

(1) 產出 .ico 圖檔

基本上我們常見的 icon 都是簡單明瞭的二維圖像,這裡推薦兩個 icon 圖集,可以從這裡面下載 .png 來做修改,或直接下載 .ico 檔。(個人比較推薦 IconArchive,這裡面的 icon 內容比較生動活潑。)

Iconfinder :: https://www.iconfinder.com/

IconArchive :: http://www.iconarchive.com/

或者如果本身有設計好的圖檔,可以透過 http://convertico.com/ 將圖檔轉成 .ico 檔。

上圖說明就是將黑蝙蝠圖檔,改成藍色蝙蝠,再轉成 .ico 檔。

 

(2) 在 compile 前完成 icon 設定

<1> in VC2008

在 project 工作列表上 >> 檢視(V) >> 資源檢視(R);

在 project 名稱上按下右鍵  >> 加入 >> 資源(R);

點選 icon。此時你已經新增 icon1.ico 到你的 project 資料夾裡面了。

接著就把我剛剛的 bat-icon-blue.ico 檔案改成 icon1.ico ,並且複製到 project 資料夾裡覆蓋掉原本的 icon1.ico 就OK了,然後再重新 compile 後,就可以看到 .exe 執行檔的 icon 被改成蝙蝠的圖示了。

<2> in Qt Creator

在 project 上按右鍵 >> 新增檔案... >> General >> Empty File >> 輸入 test.rc 檔名;

接著會在其他檔案內看到新增的檔案,接著在 test.rc 裡面編輯:

IDI_ICON1 ICON DISCARDABLE "bat-icon-blue.ico"

接著回到 .pro 檔,在

DISTFILES += \

test.rc   下加入

RC_FILE += \

test.rc

 

然後再重新 compile 後,就可以看到 qtest.exe 執行檔的 icon 也順利被更換了。

 

Deinterlacing Overview

接觸 deinterlacing 也有半年之久,最近有空閒來寫一下關於 deinterlacing 的概念與精神。(其實 wiki 上面已經描述得很詳細了...但只是想用自己的經驗與觀點來介紹 deinterlacing 。)

deinterlacing,中文叫做去交錯。deinterlacing 基本概念就是將隔行掃描( interlace )的影像訊號轉換成逐行掃描( progressive ) 訊號的一種方法。在早期因為傳輸頻寬不足的情況下,使用隔行掃描能夠在相同的 frame rate 節省一半的資料量,像是北美的 NTSC 制式的電視訊號就是隔行掃描,每秒60張;歐盟的 PAL 的電視訊號是隔行掃描,每秒50張。而 CRT 電視即利用視覺暫留影響,使得 interlace video 在 CRT 顯示器上能夠正常的顯示;但現今的液晶顯示螢幕已經有夠快的速度,能即時處理逐行掃描,所以在播放 interlace video 前必須先將 video 轉換成 progressive video,影像才不會有閃爍的現象發生。然而我們常見的視頻格式縮寫 720p、480i、576i ... 前面的數字表示影片垂直解析度,後面的英文字 p 表示逐行掃描,i 表示隔行掃描。

interlace&progressive[ 隔行掃描 vs 逐行掃描]

Deinterlacing Algorithm

deinterlacing 方法基本上可粗略分為 intra-field deinterlacinginter-field deinterlacingmotion adaptive deinterlacingmotion compensated deinterlacing 這四種方法。field 的解釋如下圖,當一張影像在隔行掃描時,只會有奇數行資訊或偶數行資訊,這一半的資訊量我們稱作場 (field),消失的另一半(灰色區域)就是靠 deinterlacing 方法內插出來的。

interlace&progressive2

intra-field deinterlacing 方法是最省硬體資源的方式,就從單一 field 裡面的資料訊息做內插,可用 line repetition、linear interpolation,或其他內插演算法來實現,但這種做法得到的畫面品質較差,畫面中有細橫線時,容易有閃爍的現象發生。

inter-field deinterlacing 方法,則是參考前後不同 fields 的訊息來做內插(需要有 DRAM 來存 frame buffer),這方法在靜態區域會有很好的影像品質,但當畫面移動或有移動物體存在時,會有鋸齒或橫紋的現象發生( feathering effect, combing effect, zipper ... paper 上常見到的說法)。

interlace&progressive3[右邊為 inter-field deinterlacing 的結果。]

motion adaptive deinterlacing 則是綜合 intra-field 與 inter-field 的優點來實現的演算法,利用 field 之間的像素差來判斷動態區域與靜態區域,動態區域用 intra-field deinterlacing,靜態區域使用 inter-field deinterlacing。此方式是目前 deinterlacing 最為廣泛的做法,因為在硬體資源可接受範圍下能做到不錯的效果。因為使用廣泛,所以演算法要一較高下,除了在 motion detection 要做得好之外,intra-field deinterlacing 也是一大重點。

motion compensated deinterlacing 利用動態補償方式來做,需要大量運算每個 block 的 motion vector 來估測每個像素的移動向量,此作法會有非常好的影像品質,但所需要硬體資源非常可觀。就我所知,除了在 encoder 或 frame rate transfer 上會用 MEMC (motion estimation and motion compensation) 外,其他 image processing 不會這麼下重本。

Future Work

除了上述基本的 deinterlacing 方法外,在實際應用上還會遇到許許多多的問題,像是 interlaced video 被標示為 progressive video,或 video 是 32 pulldown 的情況,又或者畫面中有固定速度移動的物體(如新聞跑馬燈)...在這些雜七雜八的問題出現下,如何設計演算法讓畫面能有最好的品質呈現,這就是 deinterlacing algorithm 能夠繼續發展的原因,但在 .265 HEVC encoder 不再支援 interlacing video 的情況下,deinterlacing 也將會漸漸式微。

 


reference :

http://www.vlsi.uwindsor.ca/presentations/elham_seminar1.pdf

https://zh.wikipedia.org/wiki/%E5%8E%BB%E4%BA%A4%E9%8C%AF

 

C/C++ Q&A

1. typedef 與 #define 差異?

typedef 是用來定義一個新的資料型態名稱,而 #define 是定義巨集,但編譯器只是用來取代。

ex.

typedef char char_1; // 定義一個新的資料型態名稱,從此多了一個型態。

#define char_2 char; //定義了一個 char 型態,往後遇到 char_2 型態的變數都認為是 char。

2. *& reference to pointer 用意

#include <iostream>
char *str1 = "Hello";
char *str2 = "World";
char *ptr = str1;
char *&rptr = str1;
rptr = str2;
std::cout << ptr << str1 << std::endl;

上面程式會輸出 "Hello World",因為 rptr 是一個 char 指標的『參考』(rptr 是 str1 的參考)。

Sphere Mapping in OpenGL (L12)

2016-03-10_135900

Sphere Environment Mapping 是個可以創造出反射環境的金屬幾何體或者是反射屏幕內容到二次曲面上。雖然本篇介紹的方法不是真正的將屏幕裡的內容,確切的反射到幾何體正確位置上面,但 Sphere Mapping 是個快速製造出反射圖形的一種方法。

在了解本篇內容前,請先參考 Quadrics in OpenGL (L9) ,裡面介紹了 Quadric 的繪製方法。至於本篇主旨雖然是說球形映射,但其實也可以用在任何二次曲面上或方體上面。首先,要創造出如金屬般的反射效果,要先從 Photoshop 製造出一個球形曲面,即是利用 Photoshop 裡面的濾鏡 >> 扭曲 >> 魚眼效果,得到一張球形曲面影像,取代用魚眼鏡頭拍攝。(PS. 在 OpenGL 裡面的 texture image size 盡量都改成 2 的冪次方,這樣 rendering 效果會比較好。)

castle

castle_fisheye

[ 背景影像;球形扭曲影像。]

所以首先將上面兩張圖 load 到 texture[6] 陣列裡面,而每張影像有三種不同的 texture filter,code 如下。

void myopenGL::loadGLTexture()
{
	//@ texture 1
	string in_file = "D:\\4_OpenGL\\castle_fisheye.bmp";
	QImage img[2], GL_img[2];
	if(!img[0].load(in_file.c_str()))
	{
		qWarning("Could not read the image file.");
		QImage dummy(512, 512, QImage::Format_RGB32);
		dummy.fill(Qt::green);
		img[0] = dummy;
	}
	GL_img[0] = QGLWidget::convertToGLFormat( img[0] ); // convert QImage to GLformat
	//@ texture 2
	string in_file2 = "D:\\4_OpenGL\\castle.bmp";
	if(!img[1].load(in_file2.c_str()))
	{
		qWarning("Could not read the image file.");
		QImage dummy(512, 512, QImage::Format_RGB32);
		dummy.fill(Qt::green);
		img[1] = dummy;
	}
	GL_img[1] = QGLWidget::convertToGLFormat( img[1] ); // convert QImage to GLformat

	glGenTextures(6, &amp;amp;amp;amp;amp;amp;texture[0]);	// create three texture for two images

	for(int loop = 0; loop &amp;amp;amp;amp;amp;lt; 2; loop++)
	{
		//@ nearesr filtered texture
		glBindTexture(GL_TEXTURE_2D, texture[loop]);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
		glTexImage2D(GL_TEXTURE_2D, 0, 3, GL_img[loop].width(), GL_img[loop].height(), 0,
					GL_RGBA, GL_UNSIGNED_BYTE, GL_img[loop].bits());
		//@ linear filtered texture
		glBindTexture(GL_TEXTURE_2D, texture[loop+2]);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexImage2D(GL_TEXTURE_2D, 0, 3, GL_img[loop].width(), GL_img[loop].height(), 0,
					GL_RGBA, GL_UNSIGNED_BYTE, GL_img[loop].bits());
		//@ MipMapped texture
		glBindTexture(GL_TEXTURE_2D, texture[loop+4]);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
		gluBuild2DMipmaps(GL_TEXTURE_2D, 3, GL_img[loop].width(), GL_img[loop].height(),
						GL_RGBA, GL_UNSIGNED_BYTE, GL_img[loop].bits());
	}

}

接著,這裡是本篇 sphere mapping 的重點,我們透過 glTexGeni 來產生紋理,而紋理座標(S, T, R, Q)相應著物體座標(x, y, z, w),所以如果今天用的是一維紋理( 1D ) 則只需要定義 S 座標,而 2D texture 則是定義 S 和 T 座標。

glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP) 透過 glTexGeni 來定義 S 軸(水平軸) 的紋理特性。

glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP) 則是定義 T 軸(垂直軸)的紋理特性。

在 glTexGeni 裡第三個參數是用來決定紋理的行為模式,除了 GL_SPHERE_MAP 外,還有 GL_OBJECT_LINEARGL_EYE_LINEARGL_SPHERE_MAPGL_REFLECTION_MAPGL_NORMAL_MAP

GL_OBJECT_LINEAR : 物體模式,紋理會隨著物體轉動而轉動。

GL_EYE_LINEAR : 視覺模式,紋理不會隨著物體轉動,始終保持原樣。

GL_SPHERE_MAP : 環境紋理(球體貼圖),具有反射效果。

GL_REFLECTION_MAP : 環境紋理(反射紋理),也具有反射效果。

GL_NORMAL_MAP : 用於立方體貼圖。

所以要在 initializeGL() 裡面定義紋理的行為模式。

	//@ sphere mapping
	glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
	glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);

最後,在 painGL() 裡面將 GL_TEXTURE_GEN_SGL_TEXTURE_GEN_T 啟動後,即可看到 sphere mapping 的效果了,painGL() 裡面我透過 quadratic_object 來變化不同的二次曲面,這部分在 lesson 9 有詳細的說明了,這裡就不再敘述。後面就是把背景圖也給畫出來而已。

void myopenGL::paintGL()
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glLoadIdentity();		
	glTranslatef(0.0f, 0.0f, -10.0f);	
	//@ sphere mapping
	glEnable(GL_TEXTURE_GEN_S);
	glEnable(GL_TEXTURE_GEN_T);
	glBindTexture(GL_TEXTURE_2D, texture[4]);
	glPushMatrix();
	glRotatef(rota_x, 1.0f, 0.0f, 0.0f);
	glRotatef(rota_y, 0.0f, 1.0f, 0.0f);
	glRotatef(rota_z, 0.0f, 0.0f, 1.0f);

	if(quadratic_object &amp;amp;gt; 3)
		quadratic_object = 0;

	switch(quadratic_object)
	{
	case 0:
		drawCube();
		break;
	case 1:
		glTranslatef(0.0f, 0.0f, -1.5f);
		gluCylinder(quadratic, 1.0f, 1.0f, 3.0f, 32, 32);
		break;
	case 2:
		gluSphere(quadratic, 1.3f, 32, 32);
		break;
	case 3:
		glTranslatef(0.0f, 0.0f, -1.5f);
		gluCylinder(quadratic, 1.0f, 0.0f, 3.0f, 32, 32);
		break;
	}
	glPopMatrix();
	glDisable(GL_TEXTURE_GEN_S);
	glDisable(GL_TEXTURE_GEN_T);
	//@ print the background
	glBindTexture(GL_TEXTURE_2D, texture[5]);
	glPushMatrix();
	glTranslatef(0.0f, 0.0f, -24.0f);
	glBegin(GL_QUADS);
	glNormal3f(0.0f, 0.0f, 1.0f);
	glTexCoord2f(0.0f, 0.0f); glVertex3f(-13.3f, -10.0f, 10.0f);
	glTexCoord2f(1.0f, 0.0f); glVertex3f( 13.3f, -10.0f, 10.0f);
	glTexCoord2f(1.0f, 1.0f); glVertex3f( 13.3f,  10.0f, 10.0f);
	glTexCoord2f(0.0f, 1.0f); glVertex3f(-13.3f,  10.0f, 10.0f);
	glEnd();

	glPopMatrix();
}

 

執行結果:

2016-03-10_135153[球型反射曲面。]

2016-03-10_135249[錐形反射曲面。]

 

 


reference : 

http://nehe.gamedev.net/tutorial/sphere_mapping_quadrics_in_opengl/15005/

http://tiankefeng0520.iteye.com/blog/2007939

 

在 Visual Studio 2008 下 Compile C Program

最近由於要執行別人的 C model,才發現 Visual Studio 2008 支持的 C 語言規範只到 C89 (是最早的 C 語言規範,在90年由美國國家表準協會(ANSI)推出 ANSI 版本,所以也稱做 C90)。然而 C89 coding 方式繁瑣且不直觀,所有的變數宣告都要在函式執行之前,簡單來說就是所有變數都要宣告在頂端就對了。

所以要讓 C model 可以執行,有以下的解決方法,

  1. 最直覺也最簡單的方式,將 C code 全部轉成 .cpp 的格式,利用 VC 裡面的 C++ compiler 來執行。(但這個方法會存在些差異)
  2. IDE 用 VC 或 eclipse,然後 compiler 用 MinGW (MinGW 支援 C99)。
  3. 升級到VC2013,VC2013 裡的 C++ compiler 支援 C99 的變數宣告方式。

 


 

reference :

http://stackoverflow.com/questions/13308944/how-to-simulate-c99-in-visual-studio-for-variables-declaration

http://jashliao.pixnet.net/blog/post/167143629

Masking in OpenGL (L11)

2016-01-07_100026

今天要介紹如何將 Texture 蓋上一層面具,或實現一個特殊的遮蔽物。基本上是透過 L4 所講的 Blending 方式來實現,只是今天要前置處理得到一個 Texture 的 Mask ,再透過 Blending Function 來呈現。

● glBlendFunc()

首先細部解說 glBlendFunc() 函數,

void glBlendFunc (GLenum sfactor, GLenum dfactor) 函數裡有兩個主要變數分別為 sfactor 表示 source factor (新加入顏色的權重值);dfactor 表示 destination factor (原本在屏幕緩衝區裡的權重值),兩個 factor 數值介於[0,1]。

FinalColor = sfactor * SourceColor + dfactor * DestinationColor

factor 參數設定有多組型態可以設定,如下圖:2016-01-07_122533參數裡面的kG kR kB kA 代表像素的最大值(以 8bit 為例就是 255);而當選用 GL_CONSTANT_COLOR 時,需要透過 glBlendColor() 來決定混和顏色的數值。

 


● Masking

接下來就是本篇重頭戲了,要製造出 mask 的效果,必須先將你的 texture image 轉成二值化影像,將 image 有 texture 轉成 black,沒有 texture 的部分轉成 white,得到二值化影像後,接著透過 glBlendFunc(GL_DST_COLOR, GL_ZEOR) 來實現 masking 的效果。左下圖為 texture image,右下圖為 mask image。

aboutdada2 aboutdada_mask


void myopenGL::paintGL()
{
 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 glLoadIdentity();
 
 // @ masking
 glTranslatef(0.0f, 0.0f, -2.0f);
 glRotatef(rota_x, 1.0f, 0.0f, 0.0f);
 glRotatef(rota_y, 0.0f, 1.0f, 0.0f);
 glRotatef(rota_z, 0.0f, 0.0f, 1.0f);
 glColor4f(1.0f, 1.0f, 1.0f, 1.0f);// set texture alpha 
 glBindTexture(GL_TEXTURE_2D, texture[0]);
 glBegin(GL_QUADS);
 glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f,-1.0f, 0.0f);
 glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f,-1.0f, 0.0f);
 glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 0.0f);
 glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 0.0f);
 glEnd();

 glEnable(GL_BLEND);
 glDisable(GL_DEPTH_TEST);

if(masking)
 {
 glBlendFunc(GL_DST_COLOR, GL_ZERO);

glPushMatrix();
 glTranslatef(0.0f, 0.0f, -0.2f); // for cookie cutter 
 glColor4f(1.0f, 1.0f, 1.0f, 1.0f);// set texture alpha
 glBindTexture(GL_TEXTURE_2D, texture[1]);
 glBegin(GL_QUADS);
 glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.1f,-1.1f, 0.0f);
 glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.1f,-1.1f, 0.0f);
 glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.1f, 1.1f, 0.0f);
 glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.1f, 1.1f, 0.0f);
 glEnd();
 glPopMatrix();
 }

 glEnable(GL_DEPTH_TEST);
 glDisable(GL_BLEND);

}

注意到在 blending 之前要先 glDisable(GL_DEPTH_TEST),當繪製完成後,再 glEnable(GL_DEPTH_TEST),是為了避免錯誤的行為發生。 還有 mask image 的 size 會比 texture image 還來得大一些,主要是為了有好的遮蔽效果,透過 masking 參數來開關 mask,執行結果會有種餅乾切割器( cookie cutter )的效果,只會顯現出 texture 的輪廓,執行結果:picasion.com_8d092731cb98e2622b67517464af1da0

此外,這裡我另外加入兩個 mask,分別是以下兩個影像,這裡的 blending 方式就直接用自己的顏色數值來決定glBlendFunc(GL_SRC_COLOR, GL_DST_COLOR)。 

aboutdada_mask2 aboutdada_mask3


if(masking2)
 {
 glPushMatrix();
 glTranslatef(0.0f, 0.0f, 0.1f); 
 glBlendFunc(GL_SRC_COLOR, GL_DST_COLOR);
 glBindTexture(GL_TEXTURE_2D, texture[2]);

 glBegin(GL_QUADS);
 glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.1f,-1.1f, 0.0f);
 glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.1f,-1.1f, 0.0f);
 glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.1f, 1.1f, 0.0f);
 glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.1f, 1.1f, 0.0f); 
 glEnd();
 glPopMatrix();
 }
 if(masking3)
 {
 glPushMatrix();
 glTranslatef(0.0f, 0.0f, 0.2f); 
 glBlendFunc(GL_SRC_COLOR, GL_DST_COLOR);
 glBindTexture(GL_TEXTURE_2D, texture[3]);

 glBegin(GL_QUADS);
 glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.1f,-1.1f, 0.0f);
 glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.1f,-1.1f, 0.0f);
 glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.1f, 1.1f, 0.0f);
 glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.1f, 1.1f, 0.0f);
 glEnd();
 glPopMatrix();
 }

透過 masking2 與 masking3 來開關 mask,執行結果如下:

picasion.com_e76d9d3f3a2b6f6b55d7cae83462d0eb

 


reference :

http://nehe.gamedev.net/tutorial/masking/15006/

https://www.opengl.org/sdk/docs/man/html/glBlendFunc.xhtml

 

 

QThread & Taipei101 Fireworks in OpenGL

2015-12-31_130012

在 2015 的最後一天,就用 Taipei101 Firework 這個程式來做個階段型的結尾吧~猶如降龍十八掌的第十八式,集結前面所學,所施展出來的招式。今天這個 Project 也是從之前所學到的繪製 2D & 3D Object、Texture Mapping、Quadric Object、Keyboard Control 到 Particle Engine 來完成的。但今天不再細說前面曾經提過的內容,今天主要是介紹 Qt 裡面的 QThread 來實現我的煙火特效。

● QThread

在 QThread 裡面有三個主要的 Public Slots 來控制 Thread 開始、結束和退出。分別是 void start(); void terminate(); void quit();;而 Thread 要執行的內容則是透過一個 protected function : void run(); 來實現,所以今天要求 Thread 做甚麼事情全都要寫在 run() 函式裡面。

class myThread : public QThread
{
 Q_OBJECT
public:
 myThread();
 ~myThread();
public slots:
 void start_mythread();
signals:
 void signal_firework();
protected:
 void run();
};

上面的 myThread 是我在 Taipei101 Firework 這個專案裡面所設定的 Thread。

void myThread::start_mythread()
{
 this->start();
}

start_mythread() 是一個 slots,作用就是啟動我的 Thread。

void myThread::run()
{
 double a = 0;
 while(1)
 {
 if(FIREWORK_UPDATE)
 {
 emit signal_firework();
 FIREWORK_UPDATE = false;
 }}}
connect(mythread, SIGNAL(signal_firework()),
        myqgl, SLOT(firework_update()));

接著在 run() 裡面就是指派我的 Thread 裡的工作,工作內容很簡單,就是在一個 while 迴圈內一直執行 emit signal_firework() 。這個 emit 指的是去發送 signal_firework() 訊號出去,讓接收到這個訊號的人做下一步的動作。例如我這裡將 mythread 訊號與我的 mygql 做連結(connect),所以當 myqgl 接收到 signla_firework() 訊號後,就會執行 firework_update() 的動作。特別注意到我這裡用 FIREWORK_UPDATE 來控制 emit 的動作,當我完成上一個 update 後,才會再發送下一次的 signal_firework(),作用是為了避免程式不停的發送 emit ,而使序列(queue) 裡面堆積太多的訊號要執行,到最後整個程式會因為有做不完的 update 而當掉。

 

 


● Taipei101 Fireworks

這個程式有三種形式的煙火,由三種不同的 Particle Engine 來實現,分別是迴旋式煙火、階段式煙火以及噴發式煙火。Particle Engine 的設定請看 L10

101_firework

最後 demo 影片效果囉。Happy New Year~~~

 

 


reference : http://doc.qt.io/qt-4.8/qthread.html

 

Triangle Types and Particle Engine in OpenGL (L10)

 

2015-12-22_142743

今天要介紹 Particle EngineTriangle Types。本篇主要是參考 NeHe Tutorial19 所改寫的,內容概要差不多,只是呈現方式不一樣而已。

● Triangle Types

首先介紹繪畫三角形的方式,在這之前我們都會使用 glBegin(GL_QUADS) 來繪製多邊形,今天介紹三種不同繪製三角形的方法,或者說是利用三角形繪畫法來描繪不同型態的多邊形。這三種方法分別為 GL_TRIANGLESGL_TRIANGLE_STRIPGL_TRIANGLE_FAN

1334232485_5764

  1. GL_TRIANGLES 為基礎的三角形繪畫法,給定三個點座標,就可以畫出所想要的三角形了,與 GL_QUADS 作法相同。
  2. GL_TRIANGLE_STRIP 此繪畫方式可以只用 N+2 個頂點畫出 N 個三角形出來,但必須注意每個三角形在繪製頂點時的方向,必須都為順時針或逆時針來繪製。以上圖為例,繪製第一個三角形時,頂點順序為 V0, V1, V2;第二個三角形頂點順序為 V2, V1, V3;第三個三角形 V2, V3, V4 ... 以此類推。
  3. GL_TRIANGLE_FANGL_TRIANGLE_STRIP 類似,但繪畫順序為 V0, V1, V2 ... 第一個點為扇形的圓心點(V0)。
glBegin(GL_TRIANGLE_FAN); // triangle fan
glTexCoord2f(1,1);	glVertex3f(0.0f, 0.0f, z);// V0
glTexCoord2f(0,1);	glVertex3f(2.0f, 0.0f, z);// V1
glTexCoord2f(1,0);	glVertex3f(1.4f, 0.8f, z);// V2
glTexCoord2f(0,0);	glVertex3f(0.8f, 1.4f, z);// V3
glTexCoord2f(1,1);	glVertex3f(0.0f, 2.0f, z);// V4
glEnd();

 


● Particle Engine

接下來介紹本篇的重點,粒子引擎( Particle Engine )。粒子引擎的目的是在屏幕上繪出大量的粒子,再藉由畫面的更新,讓粒子在三維空間中移動,製造出像粒子爆發或者射散出去的視覺觀感。在這裡我是藉由粒子引擎模擬出婚禮拉炮的視覺效果。 首先定義出粒子的 struct 和粒子顏色。

//@ particle
struct Particle
{
 bool active;
 float life;
 float fade;
 float r, g, b;
 float x, y, z;
 float xi, yi, zi;
 float xg, yg, zg;
};
Particle particle[1000];
GLfloat particle_colors[12][3] =
{ 
 {1.0f, 0.0f, 0.0f} , {1.0f, 1.0f, 0.0f}, {0.0f, 1.0f, 0.0f}, {0.0f, 1.0f, 1.0f},
 {0.0f, 0.0f, 1.0f} , {1.0f, 0.0f, 1.0f}, {1.0f, 1.0f, 0.5f}, {0.5f, 1.0f, 0.3f},
 {0.2f, 0.5f, 0.8f} , {1.0f, 0.0f, 0.5f}, {0.5f, 1.0f, 1.0f}, {0.0f, 1.0f, 0.2f}
};

active 表示粒子是否活化;life 是粒子存活與否;fade 則是粒子褪色,這會影響到 life 參數(後面會說明);rgb 為粒子顏色;xyz 為粒子位置;xi yi zi 為粒子的移動方向;xg yg zg 為環境的重力場分量;particle[1000] 為每次在屏幕出現的粒子數為 1000 個;particle_colors 為定義多組顏色給各個不同的粒子使用。

//@ particle initialize
 for(int loop = 0; loop < 1000; loop++)
 {
 particle[loop].active = 1;
 particle[loop].life = 1.0f;
 particle[loop].fade = float(rand()%100)/1000.0f + 0.03f;
 particle[loop].r = particle_colors[loop%12][0];
 particle[loop].g = particle_colors[loop%12][1];
 particle[loop].b = particle_colors[loop%12][2];

particle[loop].x = 0.0f;
 particle[loop].y = 0.0f;
 particle[loop].z = 0.0f;

particle[loop].xi = float( rand()%50 + 25.0f) * 10.0f;
 particle[loop].yi = float((rand()%50) - 25.0f) * 10.0f;
 particle[loop].zi = float((rand()%50) - 25.0f) * 10.0f;

particle[loop].xg = 0.0f;
 particle[loop].yg = -9.8f;
 particle[loop].zg = 0.0f;
 }

接著在 initializeGL() 裡面定義粒子參數,這裡定義每個粒子的 active 都是1表示存活;life 設為 1.0f,而 fade 則會根據 rand() 函式來決定每個粒子退化的速度;rgb 與 xyz 就是顏色與起始位置;特別注意到 xi yi zi 的數值範圍,xi 介於 25~74、yi 與 zi 都介於 -25~24 之間,由於我這裡要模擬婚禮拉炮的效果,所以 x 向量的分量要是正的(單一方向),且要大於 y分量與 z分量,表示粒子大部分受力來至於 xi 的方向;重力場只有 gy=-9.8 有值,且每個粒子所受的重力一致。

void myopenGL::paintGL()
{
 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 glLoadIdentity();
 glTranslatef(0.0f, 0.0f, -40.0f);
 glRotatef(rota_x, 1.0f, 0.0f, 0.0f);
 glRotatef(rota_y, 0.0f, 1.0f, 0.0f);
 glRotatef(rota_z, 0.0f, 0.0f, 1.0f);

 //# blend
 glColor4f(1.0f, 1.0f, 1.0f,0.5f);
 glBlendFunc(GL_ONE, GL_ZERO);
 glEnable(GL_BLEND);
 //# texture
 glEnable(GL_TEXTURE_2D);
 glBindTexture(GL_TEXTURE_2D, texture[0]);
 //# cone
 glPushMatrix();
 glTranslatef(-20.0f, 0.0f, 0.0f);
 glRotatef(-90, 0.0f, 1.0f, 0.0f);
 gluCylinder(quadratic, 3.0f, 0.0f, 8.0f, 32, 32); //cone
 glPopMatrix();
 double slowdown = 1.0f; //velocity
 glTranslatef(-20.0f, 0.0f, 0.0f);
 for(int loop = 0; loop < 1000; loop++)
 {
 if(particle[loop].active)
 {
 float x = particle[loop].x;
 float y = particle[loop].y;
 float z = particle[loop].z;

 //# blend
 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
 glColor4f(particle[loop].r, particle[loop].g, particle[loop].b, particle[loop].life);
 //# texture
 glDisable(GL_TEXTURE_2D);
 // Triangle Strip
 glBegin(GL_TRIANGLE_STRIP);
 glTexCoord2f(1,1); glVertex3f(x+0.5f,y+0.5f,z);//top right
 glTexCoord2f(0,1); glVertex3f(x-0.5f,y+0.5f,z);//top left
 glTexCoord2f(1,0); glVertex3f(x+0.5f,y-0.5f,z);//bot right
 glTexCoord2f(0,0); glVertex3f(x-0.5f,y-0.5f,z);//bot left
 glEnd();

particle[loop].x += particle[loop].xi/(slowdown*1000);
 particle[loop].y += particle[loop].yi/(slowdown*1000);
 particle[loop].z += particle[loop].zi/(slowdown*1000);

particle[loop].xi += particle[loop].xg;
 particle[loop].yi += particle[loop].yg;
 particle[loop].zi += particle[loop].zg;
 particle[loop].life -= particle[loop].fade;

 if(particle[loop].life < 0)
 {
 particle[loop].life = 1.0f;
 particle[loop].fade = float(rand()%100)/1000.0f + 0.003f;
 particle[loop].x = 0.0f;
 particle[loop].y = 0.0f;
 particle[loop].z = 0.0f;
 particle[loop].xi = float( rand()%50 + 25.0f) * 10.0f;
 particle[loop].yi = float((rand()%50) - 25.0f) * 10.0f;
 particle[loop].zi = float((rand()%50) - 25.0f) * 10.0f;
}}}}

最後就是在 paintGL() 裡繪出每個粒子的位置與顏色,可以特別注意每個粒子的位置會受到 slowdown 變數影響(粒子速度)和 重力g 影響;至於 life 會隨著 fade 而慢慢減少(life 代表每個粒子的透明度),當 life 減至零時,表示粒子已經死亡,則會再生成另一個粒子出來。另外可以注意到 code 裡面 #texture #blend 的差異,因為畫面裡的圓錐是非透明的 texture,而粒子是有透明的顏色,所以透過 texture 與 blend 的開關來做轉換。

 


執行結果:

 


reference :

http://nehe.gamedev.net/tutorial/particle_engine_using_triangle_strips/21001/

http://blog.csdn.net/xiajun07061225/article/details/7455283

 

Quadrics in OpenGL (L9)

2015-12-17_195331

以往我們只會畫平面、立方體或三角錐...等,由平面組成的立體圖形,今天要介紹複雜一點的圖形,那就是二次曲面( Quadric ),包含了圓形、扇形、球體、圓柱體以及圓錐體。實作方面不難,只要套用 GLUquadricObj 這個物件即可。

 GLuint quadratic_object;
 GLUquadric * quadratic;

首先在自己的 Class 裡面加入兩個 data members 分別是 quadratic_objectquadratic。quadratic_object 是待會用來切換不同二次曲面的參數,quadratic 則是儲存 GLUquadric 物件的指標。

quadratic = gluNewQuadric();
gluQuadricNormals(quadratic, GLU_SMOOTH);//GLU_NONE, GLU_FLAT
gluQuadricTexture(quadratic, GL_TRUE);

接著,在 initializeGL() 裡面定義 quadratic,

quadratic = gluNewQuadric() 表示建立一個二次曲面空間給 quadratic。

gluQuadricNormals(quadratic, GLU_SMOOTH) 設定外觀效果給 lighting 使用,除了 GLU_SMOOTH 外,還有 GLU_NONEGLU_FLAT 可以使用。

gluQuadricTexture(quadratic, GL_TRUE) 讓 texture 可以 mapping 到我們的二次曲面上。

switch(quadratic_object)
{
case 0:
	drawCube();	//cube
	break;
case 1:
	glTranslatef(0.0f, 0.0f, -1.5f);
	gluCylinder(quadratic, 1.0f, 1.0f, 3.0f, 32, 32);	//cylinder
	break;
case 2:
	gluDisk(quadratic, 0.5f, 1.5f, 32, 32);	//CD disk
	break;
case 3:
	gluSphere(quadratic, 1.3f, 32, 32);	//sphere
	break;
case 4:
	glTranslatef(0.0f, 0.0f, -1.5f);
	gluCylinder(quadratic, 1.0f, 0.0f, 3.0f, 32, 32);	//cone
	break;
case 5:
	int angle_start = 0;
	int sweep_angle = 180;
	gluPartialDisk(quadratic, 0.5f, 1.5f, 32, 32, angle_start, sweep_angle);	//fan
	break;
}

最後就是在 paintGL() 上繪出不同形狀的圖形了,透過不一樣的 function 來繪製不同的形狀。

drawCube() 是前面一直會用到的很簡單的立方體。


glBegin(GL_QUADS);
//front
glNormal3f(0.0f, 0.0f, 1.0f);
glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f,-1.0f, 1.0f);
glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f,-1.0f, 1.0f);
glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f);
glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f);
//back
glNormal3f(0.0f, 0.0f,-1.0f);
glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f,-1.0f,-1.0f);
glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f,-1.0f);
glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, 1.0f,-1.0f);
glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f,-1.0f,-1.0f);
//top
glNormal3f(0.0f, 1.0f, 0.0f);
glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f,-1.0f);
glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, 1.0f, 1.0f);
glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, 1.0f, 1.0f);
glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f,-1.0f);
// bottom
glNormal3f(0.0f,-1.0f, 0.0f);
glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f,-1.0f,-1.0f);
glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f,-1.0f,-1.0f);
glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f,-1.0f, 1.0f);
glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f,-1.0f, 1.0f);
//right
glNormal3f(1.0f, 0.0f, 0.0f);
glTexCoord2f(1.0f, 0.0f); glVertex3f(1.0f,-1.0f,-1.0f);
glTexCoord2f(1.0f, 1.0f); glVertex3f(1.0f, 1.0f,-1.0f);
glTexCoord2f(0.0f, 1.0f); glVertex3f(1.0f, 1.0f, 1.0f);
glTexCoord2f(0.0f, 0.0f); glVertex3f(1.0f,-1.0f, 1.0f);
//left
glNormal3f(-1.0f, 0.0f, 0.0f);
glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f,-1.0f,-1.0f);
glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f,-1.0f, 1.0f);
glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f);
glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f,-1.0f);
glEnd();

gluCylinder(quadratic, 1.0f, 1.0f, 3.0f, 32, 32) 為繪製圓柱體,第一個參數是表示底部的半徑,第二個參數表示頂部的半徑,第三個參數表示圓柱體的高,第四個參數 32 表示 slices,代表多少切片圍繞在圓柱體,第五個參數 32 表示 stacks,表示圓柱體是用多少堆疊起來,數值越高表示精度越高

gluDisk(quadratic, 0.5f, 1.5f, 32, 32) 表示圓盤,0.5f 表示內圈半徑,1.5f 表示外圈半徑。內圈半徑一定要小於外圈半徑。

gluSphere(quadratic, 1.3f, 32, 32) 繪製球體,1.3f 為球體半徑。

gluCylinder(quadratic, 1.0f, 0.0f, 3.0f, 32, 32) 繪製圓錐,特別注意到這裡的 function 與繪製圓柱體一樣,只是今天第二個參數(頂部的半徑設為零),就會呈現圓錐體。

gluPartialDisk(quadratic, 0.5f, 1.5f, 32, 32, angle_start, sweep_angle) 繪製扇形。0.5f 表示內半徑,1.5f 表示外半徑,angle_start 表示扇形起始點,sweep_angle 表示掃過的角度有多少。

 

 


透過變更 quadratic_object 來選擇不同的圖形輸出,執行結果:

picasion.com_bbf0c4deb01ab0c797f51566ae4a9a0e

至於文章前面像釘子網的圖形,我是用陣列方式匯出多個圓錐形來實現的。

 

 


reference : http://nehe.gamedev.net/tutorial/quadrics/20001/