【ARTOOlkit】讓標識卡顯示自己的旋轉角度
有很多不錯的東西,在已有的知識上補充自己的的學習水平。
我們這次要做的是,旋轉我們的標識卡,在螢幕上顯示出我們的旋轉角度。具體是我們利用glVertex()畫點函式,在標識卡上繪製出一個圓,然後利用atan(double x)函式來求旋轉角度,利用函式glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, string[i]),將旋轉角度顯示在螢幕上,我們這次要實現的功能就是這個。
話不多說,接下來給大家分享原始碼
#ifdef _WIN32
#include <windows.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#ifndef __APPLE__
#include <GL/gl.h>
#include <GL/glut.h>
#else
#include <OpenGL/gl.h>
#include <GLUT/glut.h>
#endif
#include <AR/gsub.h>
#include <AR/video.h>
#include <AR/param.h>
#include <AR/ar.h>
#include<math.h>//宣告數學函式庫
#define PI 3.14159//定義變數PI,用來計算圓時用到
double angle = 0;//旋轉角度
const int n =100;
const GLfloat R = 40.0f;//半徑
/* set up the video format globals */
#ifdef _WIN32
char *vconf = "Data\\WDM_camera_flipV.xml";
#else
char *vconf = "";
#endif
int xsize, ysize;
int thresh = 100;
int count = 0;
int mode = 1;//標識位
char *cparam_name = "Data/camera_para.dat";
ARParam cparam;
char *patt_name = "Data/patt.hiro";
int patt_id;
int patt_width = 80.0;
double patt_center[2] = {0.0, 0.0};
double patt_trans[3][4];
static void init(void);
static void cleanup(void);
static void keyEvent( unsigned char key, int x, int y);
static void mainLoop(void);
static void draw( double trans[3][4] );
static void DrawCircle();
static void getResultRaw(ARMarkerInfo *marker_info);
static void print_string(char *string);
int main(int argc, char **argv)
{
glutInit(&argc, argv);
init();
arVideoCapStart();
argMainLoop( NULL, keyEvent, mainLoop );
return (0);
}
static void keyEvent( unsigned char key, int x, int y)
{
/* quit if the ESC key is pressed */
if( key == 0x1b ) {
printf("*** %f (frame/sec)\n", (double)count/arUtilTimer());
cleanup();
exit(0);
}
//如果鍵盤輸入c
if( key == 'c' ) {
printf("*** %f (frame/sec)\n", (double)count/arUtilTimer());
count = 0;
mode = 1 - mode;//讓每次鍵盤輸入c都讓mode變化
//此處mode轉換為布林型,如果mode=0,則為假arGetTransMatCont
if( mode ) printf("Continuous mode: Using arGetTransMatCont.\n");
//如果mode不為假,輸出arGetTransMat
else printf("One shot mode: Using arGetTransMat.\n");
}
}
/* main loop */
static void mainLoop(void)
{
static int contF = 0;//靜態變數
ARUint8 *dataPtr;
ARMarkerInfo *marker_info;
int marker_num;
int j, k;
/* grab a vide frame */
if( (dataPtr = (ARUint8 *)arVideoGetImage()) == NULL ) {
arUtilSleep(2);
return;
}
if( count == 0 ) arUtilTimerReset();
count++;
argDrawMode2D();
argDispImage( dataPtr, 0,0 );
/* detect the markers in the video frame */
if( arDetectMarker(dataPtr, thresh, &marker_info, &marker_num) < 0 ) {
cleanup();
exit(0);
}
arVideoCapNext();
/* check for object visibility */
k = -1;
for( j = 0; j < marker_num; j++ ) {
if( patt_id == marker_info[j].id ) {
if( k == -1 ) k = j;
else if( marker_info[k].cf < marker_info[j].cf ) k = j;
}
}
//if( k == -1 ) {
if(k != -1){
glDisable(GL_DEPTH_TEST);//繪製透明圖片時關閉檢查
getResultRaw( &marker_info[k] );//顯示旋轉角度
}
contF = 0;
//argSwapBuffers();
//return;
/*計算攝像頭的轉移矩陣,標識卡和攝像機之間的轉移資訊通過使用函式*/
//mode為0
if( mode == 0 || contF == 0 ) {
arGetTransMat(&marker_info[k], patt_center, patt_width, patt_trans);
}
else {
//mode為1
arGetTransMatCont(&marker_info[k], patt_trans, patt_center, patt_width, patt_trans);
}
contF = 1;
draw( patt_trans );
argSwapBuffers();
}
static void init( void )
{
ARParam wparam;
/* open the video path */
if( arVideoOpen( vconf ) < 0 ) exit(0);
/* find the size of the window */
if( arVideoInqSize(&xsize, &ysize) < 0 ) exit(0);
printf("Image size (x,y) = (%d,%d)\n", xsize, ysize);
/* set the initial camera parameters */
if( arParamLoad(cparam_name, 1, &wparam) < 0 ) {
printf("Camera parameter load error !!\n");
exit(0);
}
arParamChangeSize( &wparam, xsize, ysize, &cparam );
arInitCparam( &cparam );
printf("*** Camera Parameter ***\n");
arParamDisp( &cparam );
if( (patt_id=arLoadPatt(patt_name)) < 0 ) {
printf("pattern load error !!\n");
exit(0);
}
/* open the graphics window */
argInit( &cparam, 1.0, 0, 0, 0, 0 );
}
/* cleanup function called when program exits */
static void cleanup(void)
{
arVideoCapStop();
arVideoClose();
argCleanup();
}
//畫圖函式
static void DrawCircle()
{
int i;
glLineWidth(5);//線寬為5
glBegin(GL_LINE_LOOP); //利用GL_LINE_LOOP這一函式來畫圖
glColor3f(1, 1, 0);//線的顏色
for(i=0; i<n; i++)
glVertex2f(R*cos(2*PI/n*i), R*sin(2*PI/n*i));//利用迴圈畫圓
glEnd();
glPointSize(5);//設定點的大小
glBegin(GL_POINTS);
for(i=0; i<12; i++)
glVertex2f((R-5)*cos(2*PI/12*i),(R-5)*sin(2*PI/12*i));//畫12個點
glEnd();
glColor3f(1,0,0);
glBegin(GL_TRIANGLES);//畫在圓內的箭頭
glVertex3f(0,40,0);
glVertex3f(3,0,0);
glVertex3f(-3,0,0);
glEnd();
return;
}
//計算角度
static void getResultRaw(ARMarkerInfo *marker_info )
{
char string[256];
//得到角度
angle= atan(-marker_info->line[0][0]/marker_info->line[0][1])/PI*180;
glRasterPos3f(-0.7f, 0.4f,0.3f);//改變光柵位置
sprintf(string,"ANGLE: %3.1f\n",angle);//將字串賦給string
print_string( string );
return;
}
//顯示angel和角度
static void print_string(char *string)
{
int i;
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
/*display the position data*/
glTranslatef(-0.95,-0.20,0.0);//平移
/*draw red text*/
glColor3f(0.75,0.0,0.0);
glRasterPos2i(0.0,0.0);//改變光柵位置
for( i=0; i<(int)strlen(string); i++)
{
if(string[i] != '\n' ){//當讀到/n時,就不顯示字元,因為原字串為"ANGLE: %3.1f\n",其末尾為/n
glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, string[i]);//用於在glut視窗某位置顯示字元,只能顯示英文字元,而且字型,大小都不能任意設定
}
else{
glTranslatef(0.0,-0.07,0.0);//平移
glRasterPos2i(0.0,0.0);//光柵位置
}
}
return;
}
static void draw( double trans[3][4] )
{
double gl_para[16];
GLfloat mat_ambient[] = {0.0, 0.0, 1.0, 1.0};
GLfloat mat_flash[] = {0.0, 0.0, 1.0, 1.0};
GLfloat mat_flash_shiny[] = {50.0};
GLfloat light_position[] = {100.0,-200.0,200.0,0.0};
GLfloat ambi[] = {0.1, 0.1, 0.1, 0.1};
GLfloat lightZeroColor[] = {0.9, 0.9, 0.9, 0.1};
argDrawMode3D();
argDraw3dCamera( 0, 0 );
glClearDepth( 1.0 );
glClear(GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
/* load the camera transformation matrix */
argConvGlpara(trans, gl_para);
glMatrixMode(GL_MODELVIEW);
glLoadMatrixd( gl_para );
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glLightfv(GL_LIGHT0, GL_POSITION, light_position);
glLightfv(GL_LIGHT0, GL_AMBIENT, ambi);
glLightfv(GL_LIGHT0, GL_DIFFUSE, lightZeroColor);
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_flash);
glMaterialfv(GL_FRONT, GL_SHININESS, mat_flash_shiny);
glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
glMatrixMode(GL_MODELVIEW);
DrawCircle();
glRotatef(90,1,0,0);//旋轉函式
glTranslatef( 25.0, 25.0, 10.0 );//平移函式
//glutSolidTeapot(25.0);//茶壺
//glLineWidth(10.0);//設定線寬
glDisable( GL_LIGHTING );
glDisable( GL_DEPTH_TEST );
}
其中畫紅色的部分是在simple2.c的基礎上新增的,接下來給大家講解一下新增的程式碼是什麼意思。glDisable(GL_DEPTH_TEST);//繪製透明圖片時關閉檢查。
glDisable函式的意思是在InitGL() 或者類似的初始化OpenGL的地方,會有glEnable(GL_DEPTH_TEST);啟用了之後,OpenGL在繪製的時候就會檢查,當前畫素前面是否有別的畫素,如果別的畫素擋道了它,那它就不會繪製,也就是說,OpenGL就只繪製最前面的一層。當我們需要繪製透明圖片時,就需要關閉它glDisable(GL_DEPTH_TEST);
getResultRaw( &marker_info[k] );//顯示旋轉角度
因為重要的計算旋轉角度,將角度顯示在螢幕上的函式都包括在了 getResultRaw( &marker_info[k] )函式裡面,所以只用執行此函式即可
static void DrawCircle()//畫圖函式,其中函式中最重要的部分就是:
for(i=0; i<n; i++)
glVertex2f(R*cos(2*PI/n*i), R*sin(2*PI/n*i))
這個函式用到for迴圈,並設定n=100,利用R*cos(2*PI/n*i), R*sin(2*PI/n*i)這一函式來置於畫點函式之中,畫出了一個圓型。
for(i=0; i<12; i++)
glVertex2f((R-5)*cos(2*PI/12*i),(R-5)*sin(2*PI/12*i));//畫12個點
這段函式畫了圓內的12個點。
最後畫出的圓如下圖所示:
//顯示angel和角度
static void print_string(char *string)
接下來給大家介紹如何將字元顯示在螢幕上面,這段函式裡面的核心點就是
for( i=0; i<(int)strlen(string); i++)
{
if(string[i] != '\n' ){//當讀到\n時,就不顯示字元,因為原字串為"ANGLE: %3.1f\n",其末尾為\n
glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, string[i]);//用於在glut視窗某位置顯示字元,只能顯示英文字元,而且字型,大小都不能任意設定
(int)strlen(string)s此處利用strlen函式求出了string的長度,並強制轉換為int型別,在上面的函式中,string裡面的內容是
"ANGLE: %3.1f\n",其中,%3.1f是angle,也就是求到的角度,末尾為\n。
if(string[i] != '\n' ){
glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, string[i]);
一個一個的讀string,如果讀到不是\n,就將字元顯示出來,這裡在stirng最後添加了一個\n,其實是做了一個標誌位,讓這個標誌在函式print_string中被識別出來。
glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, string[i])用於在glut視窗某位置顯示字元,只能顯示英文字元,而且字型,大小都不能任意設定,將字元顯示在視窗螢幕。
最後給大家介紹此篇程式的核心函式
//計算角度
static void getResultRaw(ARMarkerInfo *marker_info )
{
char string[256];
//得到角度
angle= atan(-marker_info->line[0][0]/marker_info->line[0][1])/PI*180);
glRasterPos3f(-0.7f, 0.4f,0.3f);//改變光柵位置
sprintf(string,"ANGLE: %3.1f\n",angle);//將字串賦給string
print_string( string );//讓string顯示在螢幕上
return;
}
開篇設定了變數型別為char string,string是為了儲存,"ANGLE: %3.1f\n",angle。這一字串,將來顯示在螢幕上的也是這一字串。
angle= atan(-marker_info->line[0][0]/marker_info->line[0][1])/PI*180);
然後是利用atan函式得到旋轉角度,並賦予給angle。
atan(double x)是math.h裡面的數學函式,用來計算反正切角。
函式double atan(double x);
引數 x 為任意值,返回值以弧度表示,範圍為 -π/2 到 +π/2 (-1.57079 到 1.57079)
弧度與角度的關係為:
弧度 = 180 / π 角度
角度 = π / 180 弧度
atan(y/x) 僅僅根據正切值為y/x求出對應的角度,此時的double x中的X為計算好的弧度y/x。
其中-marker_info->line[0][0]表示atan(y/x)中的y,marker_info->line[0][1])表示atan(y/x)中的x,-marker_info->line[0][0]/marker_info->line[0][1])表示求出的反正切角度,然後將角度/PI*180表示求出的弧度,因為atan(double x)中的x的返回值必須為弧度值
-marker_info->line[0][0]/marker_info->line[0][1])中line是make_info結構體裡面的一個引數,之前有介紹過make_info結構體裡面的具體內容。