1、 程序運行的軟硬件環境算法
本次設計在window10系統下進行,運用C++進行編寫,在CodeBlocks環境下使用OpenGL進行設計。編程
所需環境配置分爲2部分,第一部分是CodeBlocks的配置,第二部分爲OpenGL的相關配置。windows
Codeblocks配置:數組
- 打開搜索引擎,搜索CodeBlocks,點擊相關結果進入CodeBlocks官網:codeblocks.org。
- 選擇Download選項卡
- 點擊Download the binary release
- 選擇帶mingw編譯器的版本codeblocks-16.01mingw-setup.exe
- 下載完成後,直接安裝,除了安裝路徑能夠改,其餘的都選擇默認的便可。
- 打開CodeBlocks便可使用。
OpenGL的配置:函數
- 將h文件拷貝到MinGw\include\GL目錄下
- 將dll這個動態連接庫文件拷貝到相應文件夾下,此處注意文件夾根據機器操做系統位數不一樣,拷貝的文件夾路徑也不一樣,具體區別爲,若是是32位操做系統,則應該將glut32.dll文件拷貝到C:\Windows\System32文件夾下,若是是64位操做系統,則應將該文件拷貝到C:\Windows\SysWOW64文件夾下,若是拷貝錯誤,將致使程序編譯的時候由於找不到該文件而沒法經過,以64位機器爲例。
- 將a文件放置到MinGw\lib文件夾下。
- 建立openGL工程,打開CodeBlocks,點擊file->new->project->GLUT project->go,而後一直下一步,直至進入工程建立完畢,點擊工具欄project->build options.在build options中Debug->Linker settings->Add,將咱們第三步放置好的libglut32.a文件添加進來,保存退出。而後自帶的openGL實例程序便可正常編譯運行,至此CodeBlocks下的openGL開發配置完成。
2、涉及的相關算法的原理工具
本次設計運用C++編寫,利用OpenGL進行圖像設計,設計的內容爲:繪製一個球體,在其表面進行紋理映射,而且球體保持做自由落體運動,同時,能夠對球體進行旋轉、縮放等操做。oop
紋理映射的原理:ui
紋理映射使用一個圖案或者紋理來肯定渲染流水線中片元處理階段片元的顏色。簡單來說,紋理就是矩形的數據數組,例如顏色數據、亮度數據、顏色和alpha數據。紋理數組中的單個值經常稱爲紋理單元,也叫紋素(texel),這裏讓它區別於像素,主要是爲了強調它的應用方式。 OpenGL支持1D、2D、3D以及立方體紋理,如今主要考慮2D紋理。搜索引擎
紋理映射就是要實現,如何把紋素映射到幾何對象的每一個點。一個2D的紋理有一個寬度和高度,經過寬度和高度相乘便可獲得有多少個紋素。spa
那麼如何來指定頂點的紋素呢?經過座標來指定,可是這個座標不該該是具體紋理的中座標,而應該是抽象的紋理座標空間中的座標;不然經過指定具體紋理的座標,當更換紋理,例如改變紋理的寬度和高度時,這些座標值可能變得無心義,而不得不更新全部頂點的座標值,所以須要使用抽象的紋理座標空間的座標。紋理座標通常都規範化到[0,1]範圍內。例如一個紋理寬度爲320,高度爲200,而紋理座標(0.5,0.1)則表示紋素的位置在: (320*0.5,200*0.1)=(160,20)。一般使用UV座標系來表示紋理座標系:
這裏注意,OpenGL中V軸從下往上是正方向,U軸從左往右是正方向。在具體使用時,這與應用中紋理Y方向有關。若是紋理從上到下,則須要將紋理的Y方向翻轉來知足這個圖形所示的紋理座標。與紋理映射有關的一個特性是,當模型進行變換時,紋理座標仍然會跟着模型的頂點,他們並不進行變換(固然也有其餘方法能夠改變紋理座標),就好像粘着頂點同樣。例以下圖所示的三角形,若是在其中應用一個小的紋理:
當對三角形進行變換時,紋理座標保持不變,這樣當模型進行旋轉、拉伸和放縮時,紋理也會跟着變化,以下圖所示:
與紋理有關的另外一個特性是紋理採樣。當把紋理座標映射到紋素數組時,正好獲得對應紋素的中心位置的狀況不多出現。解決這一問題的一種方法是,從紋素數組中取這樣一個紋素,該紋素的位置,最佳逼近經過光柵化模塊計算輸出的紋理座標。這樣一方法成爲點採樣(Point sampling),也叫作nearest filtering。例如座標(152.34,745.14)的紋素,就使用(152,745)來代替。用點採樣容易產生走樣偏差。
另一種方法是線性濾波方法(linear filtering)。例如,若是計算出一個紋理座標位於(152.34,745.14),那麼這個值對應的最近的4個紋理座標爲: ( (152,745), (153,745), (152,744) , (153,744) )。那麼咱們能夠利用這4個紋理座標的對應的顏色值進行線性插值,例如計算這一組紋素的加權平均值,並把該值做爲紋理座標映射到紋素數組時的紋素值。這種方法計算量要比點菜用大,效果通常比點採樣好。
OpenGL支持多種濾波類型,能夠經過設置來進行選擇。
在進行紋理映射時,還須要考慮紋素與屏幕像素之間的對應關係。單個的紋素一般並不與屏幕像素對應,當紋素比單個像素大時,屏幕上多個像素對應於單個像素,稱之爲放大(manification);當紋素比單個像素小,屏幕上單個像素對應多個紋素,則稱之爲縮小(minification)。關係以下圖所示:
自由落體實現原理:
定義2個常量和1個變量,常量分別是G=9.8爲重力加速度、t=0.002爲時間間隔;變量爲布爾類型的direction,初始化爲direction=true,記錄球體運動方向,當direction=true時,球體下落,下落到達下極限時,改變direction=false,球體開始上彈。
定義一個結構體,儲存球體對象,即:
在此結構中,定義了球體的y座標屬性y和y座標上的速度屬性vy。在球體運動過程當中,運用牛頓運動定律計算球體的位置及速度,經過OpenGL裏glTranslated()方法對繪製中心重定義,而後對球體進行從新繪製。
在球體運動過程當中,球體位移公式爲:
direction = true à ball.y = ball.y – ( ball.vy*t + 0.5*G*t*t);
direction = false à ball.y = ball.y + ( ball.vy*t – 0.5*G*t*t);
旋轉、縮放實現原理:
旋轉和縮放將用到OpenGL自帶的3個函數實現,即:glRotatef()、gluLookAt()和glScalef()。glRotatef()和gluLookAt()控制左右、先後旋轉,glScalef()控制縮放。整個控制旋轉、縮放的控制放在keyboard()方法下實現。
3、程序設計思想和設計過程
OpenGL中紋理映射的步驟以下:
一、建立紋理對象,併爲他指定一個紋理
二、肯定紋理如何應用到每一個像素上
三、啓用紋理貼圖功能
四、繪製場景,提供紋理座標和幾何圖形座標
實現紋理映射主要關係到4個概念:紋理對象(the texture object), 紋理單元(the texture unit), 採樣器對象(the sampler object )採樣器變量(sampler uniform in the shader).他們的關係以下圖所示:
紋理對象並不直接綁定到着色器,而是綁定到一個紋理單元,紋理單元的索引將會傳遞給着色器。要綁定到一個紋理單元,先要將其激活,可使用glActiveTexture函數,例如glActiveTexture(GL_TEXTURE0)將激活單元0。可使用多個紋理單元,每一個紋理單元能夠綁定到相同或者不一樣的紋理對象。有一點值得注意,只要紋理對象的類型不一樣,一個紋理單元能夠綁定多個紋理對象。例如你能夠分別將兩個紋理對象綁定到同一個紋理單元的1D和 2D不一樣的目標上。能夠經過採樣器變量來使用多個紋理,這個uniform變量有’sampler1D’, ‘sampler2D’, ‘sampler3D’, ‘samplerCube’等不一樣形式。在片元着色器中,採樣函數須要經過採樣器變量來訪問多個紋理單元。採樣器對象與紋理對象不相同。紋理對象中包含了紋理數據,以及配置採樣操做的參數,這些參數是採樣狀態的一部分。然而,你也能夠建立一個採樣對象,用採樣狀態參數配置它,並把它綁定到紋理單元中。這樣,採樣器對象會覆蓋紋理對象中定義的採樣狀態。目前咱們並不使用這一對象。
球體運動控制的流程圖:
對流程圖進行簡單的說明:初始化即對球體的y座標和y軸上的速度vy進行定義,ball.y=10,ball.vy=0;經過glTranslated()函數實現球體的移動,在移動過程當中由牛的你運動定律肯定球體的座標以及速度的變化;每次移動都進行判斷,是否到達下極限-4,若是尚未到達則繼續下落,若是已經到達下極限則改變球體運動方向,使球體上升,每一次上升操做都判斷是否到達上極限,若是尚未到達則繼續上升,不然改變球體運動方向。
用到的主要OpenGL函數:
函數名 | 參數 | 實現功能 |
makeStripeImage | Void | 製做紋理圖形 |
init | Void | 對程序數據進行初始化 |
idle | Void | 空閒時運行的函數 |
display | Void | 控制圖形界面的顯示,包括圖形繪製以及一些屬性的控制 |
glutPostRedisplay | Void | 對圖形進行重繪 |
glTranslated | x、y、z | 對圖形進行移動,x、y、z分別爲對應軸上移動的距離 |
glutSolidSphere | GLdouble radius , GLint slices , GLint stacks | 渲染一個球體 |
glVertex3f | x、y、z | 肯定一個頂點 |
reshape | int w , int h | 對顯示窗口的控制 |
keyboard | unsigned char key,int x,int y | 實現對圖形旋轉、縮放的控制 |
遇到的問題:
在功能實現過程當中,遇到一個很是棘手且奇怪的問題,只怪我對openGL掌握得還不是很熟練加上時間也不是很充足,因此至今沒有解決,這個問題就是:
當t=0.002時,球體運動的下限不能低於-4,上限不能高於10,不然球體在第二次往返時將不能正常改變方向;當t=0.003時,下限不能低於-3,上限不能高於10,不然結果與上述同樣。
4、程序源代碼和使用說明
1)程序源代碼
#include <windows.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>
#include
#include
#include
#define stripeImageWidth 32
#define G 9.8
GLubyte stripeImage [4*stripeImageWidth];
static GLuint texName;
bool direction = true;
double t = 0.002;
typedef struct b //定義儲存球體的結構
{
GLdouble y;
GLdouble vy;
}Ball;
Ball ball;
void makeStripeImage(void){ //製做紋理條紋
int j;
for(j = 0; j < stripeImageWidth; j++){
stripeImage[4*j] = (GLubyte)((j<=4)?255:0); stripeImage[4*j+1] = (GLubyte)((j>4)?255:0);
stripeImage[4*j+2] = (GLubyte)0;
stripeImage[4*j+3] = (GLubyte)255;
}
}
static GLfloat xequalzero[] = {1.0,0.0,0.0,0.0};
static GLfloat *currentCoeff;
static GLenum currentPlane;
static GLint currentGenMode;
void init(void){
ball.y = 10 ; //初始化球體屬性
ball.vy = 0 ;
glClearColor(0.0,0.0,0.0,0.0);
glEnable(GL_DEPTH_TEST);
glShadeModel(GL_SMOOTH);
makeStripeImage();
glPixelStorei(GL_UNPACK_ALIGNMENT,1);
glGenTextures(1,&texName);
glBindTexture(GL_TEXTURE_1D,texName);
glTexParameteri(GL_TEXTURE_1D,GL_TEXTURE_WRAP_S,GL_REPEAT);
glTexParameteri(GL_TEXTURE_1D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_1D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexImage1D(GL_TEXTURE_1D,0,GL_RGBA,stripeImageWidth,0,GL_RGBA,GL_UNSIGNED_BYTE,stripeImage);
glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE);
currentCoeff = xequalzero;
currentGenMode = GL_OBJECT_LINEAR;
currentPlane = GL_OBJECT_PLANE;
glTexGeni(GL_S,GL_TEXTURE_GEN_MODE,currentGenMode);
glTexGenfv(GL_S,currentPlane,currentCoeff);
glEnable(GL_TEXTURE_GEN_S);
glEnable(GL_LIGHTING); //開啓光照
glEnable(GL_LIGHT0); //開啓光源0
glEnable(GL_AUTO_NORMAL);
glEnable(GL_NORMALIZE);
glFrontFace(GL_CW);
glMaterialf(GL_FRONT,GL_SHININESS,64.0);
}
void idle(void) //空閒函數
{
glutPostRedisplay();
}
void display(void){
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glEnable(GL_TEXTURE_1D);
glBindTexture(GL_TEXTURE_1D,texName);
glPushMatrix(); //將當前矩陣壓入矩陣堆棧,本//次操做結束以後將其彈出,使//得本次操做不影響下次操做
glTranslated(0,ball.y,0); //將繪圖中心移動到球體的中心
glutSolidSphere(2,50,50); //繪製一個球體
if(direction){
ball.y = ball.y – (ball.vy*t+0.5*G*t*t); //根據牛頓運動定律計算出球
//的位移公式
ball.vy = ball.vy + G*t; //根據牛頓運動定律計算出球體的速度
if(ball.y <=-4){ direction = false; //觸發轉向條件,改變direction的值,使球//體運動方向改變 } }else{ ball.y = ball.y + (ball.vy*t-0.5*G*t*t); ball.vy = ball.vy – G*t; if(ball.y >= 10){
direction = true;
}
}
glPopMatrix(); //將當前矩陣彈出
glDisable(GL_TEXTURE_1D);
glFlush();
}
void reshape(int w,int h){
glViewport(0,0,(GLsizei)w,(GLsizei)h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if(w<=h)
glOrtho(-15,15,-15*(GLfloat)h/(GLfloat)w,15*(GLfloat)h/(GLfloat)w,-15,15);
else
glOrtho(-15*(GLfloat)h/(GLfloat)w,15*(GLfloat)h/(GLfloat)w,-15,15,-15,15);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void keyboard(unsigned char key,int x,int y){ //控制鍵盤輸入,鍵盤輸入a、d、//s、w時分別實現左右先後的旋轉,
switch(key){ //輸入e、r的時候實現放大和縮小
case ‘a’:
glRotatef(5.0,0.0,0.0,1.0);
glutPostRedisplay();
break;
case ‘d’:
glRotatef(-5.0,0.0,0.0,1.0);
glutPostRedisplay();
break;
case ‘s’:
gluLookAt(0,0.01,0.01,0,0,0,0,1,0);
glutPostRedisplay();
break;
case ‘w’:
gluLookAt(0,-0.01,0.01,0,0,0,0,1,0);
glutPostRedisplay();
break;
case ‘e’:
glScalef(1.1,1.1,1.1);
glutPostRedisplay();
break;
case ‘r’:
glScalef(0.9,0.9,0.9);
glutPostRedisplay();
break;
default:
break;
}
}
int main(int argc,char** argv){
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB|GLUT_DEPTH);
glutInitWindowSize(600,600);
glutInitWindowPosition(450,100);
glutCreateWindow(argv[0]);
init();
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutKeyboardFunc(keyboard);
glutIdleFunc(idle);
glutMainLoop();
return 0;
}
2)程序說明:
該程序引入了3個OpenGL的包,分別是gl.h、glu.h、glut.h,都是一些OpenGL的基礎庫,因此並不須要額外進行庫函數的配置。程序主體能夠分爲5個部分,幾個主要模塊分別爲init()、display()、reshape()、keyboard()、main()。
其中,init()主要對程序數據進行初始化,包括球體屬性設置,渲染屬性設置等。display()用於繪製圖形,包括球體和地面,經過不斷調用display()函數實現動態效果。reshape()函數則是對視口的設置,當窗口大小變化時,爲了防止物體變形,這時要重設投影轉換矩陣,設置視口轉換矩陣,以及視圖轉換矩陣。keyboard()函數則是處理鍵盤輸入,對圖形的旋轉、縮放進行控制:
a:左旋轉 d:右旋轉
s:前旋轉 w:後旋轉
e:放大 r:縮小
main()函數是程序的入口,調用其餘方法實現程序功能。
5、程序運行結果截圖
6、參考目錄
- OpenGL編程指南
- 紋理原理部分由參考自百度
原創文章:引用代表出處