前面幾節都是純理論居多看起來有點枯燥,這節來補充一個案例。數組
先來看一下效果,這樣方面你們理解咱們下面的代碼到底在寫些什麼。緩存
怎麼樣是否是很像以前玩的cf裏的隧道地圖,還能經過先後建控制視角往先後移動。效果也看到了後面閒話少說直接上代碼吧bash
int main(int argc, char *argv[]){
gltSetWorkingDirectory(argv[0]);//設置當前工做目錄,針對MAC OS X
glutInit(&argc, argv);//初始化GLUT庫
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);//將顯示模式初始化雙緩衝、RGB顏色模式
glutInitWindowSize(800, 600);//初始化窗口大小
glutCreateWindow("CF");//建立窗口並設置窗口標題爲CF
glutReshapeFunc(ChangeSize);//註冊回調函數ChangeSize 初次建立窗口以及窗口大小改變是調用
glutSpecialFunc(SpecialKeys);//註冊回調函數SpecialKeys 鍵盤特殊鍵位(好比上下左右)按下時調用
glutDisplayFunc(RenderScene);//註冊回調函數RenderScene,窗口內容繪製、窗口大小改變、窗口重繪等都會回調該函數
//初始化驅動程序並堅持有沒有出錯
GLenum err = glewInit();
if (GLEW_OK != err) {
fprintf(stderr, "GLEW Error: %s\n",
glewGetErrorString(err));
return 1;
}
SetupRC();//調用SetupRC函數
glutMainLoop();// 啓動循環
ShutdownRC();//刪除紋理
return 0;複製代碼
main函數做爲程序的入口主要完成初始化、註冊回調函數、啓動運行循環等操做。而後main函數裏面調用了SetupRC這個自定義函數,在這個函數裏咱們處理一些參數的初始化,通常是處理一些只須要初始化一次的參數,固然也能處理渲染環境中任何須要的初始化,看我的喜愛和代碼風格。好比這個項目中咱們設置並初始化紋理對象。
函數
void SetupRC(){
//1.黑色的背景
glClearColor(0.0f, 0.0f, 0.0f,1.0f);
//2.初始化shaderManager
shaderManager.InitializeStockShaders();
GLbyte *pBytes;
GLint iWidth, iHeight, iComponents;
GLenum eFormat;
GLint iLoop;
//3.生成紋理標記
glGenTextures(TEXTURE_COUNT, textures);
//4. 循環設置紋理數組的紋理參數
for(iLoop = 0; iLoop < TEXTURE_COUNT; iLoop++){
glBindTexture(GL_TEXTURE_2D, textures[iLoop]);
pBytes = gltReadTGABits(szTextureFiles[iLoop],&iWidth, &iHeight,&iComponents, &eFormat);//加載紋理、設置過濾器和包裝模式
//GL_TEXTURE_MAG_FILTER(放大過濾器,GL_NEAREST(最鄰近過濾)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
//GL_TEXTURE_MIN_FILTER(縮小過濾器),GL_NEAREST(最鄰近過濾)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
//GL_TEXTURE_WRAP_S(s軸環繞),GL_CLAMP_TO_EDGE(環繞模式強制對範圍以外的紋理座標沿着合法的紋理單元的最後一行或一列進行採樣)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
//GL_TEXTURE_WRAP_T(t軸環繞),GL_CLAMP_TO_EDGE(環繞模式強制對範圍以外的紋理座標沿着合法的紋理單元的最後一行或一列進行採樣)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, iComponents, iWidth, iHeight, 0, eFormat, GL_UNSIGNED_BYTE, pBytes);
glGenerateMipmap(GL_TEXTURE_2D);//爲紋理對象生成一組完整的mipmap
//釋放原始紋理數據,不在須要紋理原始數據了
free(pBytes);
}
//5. 設置幾何圖形頂點/紋理座標(上.下.左.右)
GLfloat z;
floorBatch.Begin(GL_TRIANGLE_STRIP, 28, 1); //Z表示深度,隧道的深度
for(z = 60.0f; z >= 0.0f; z -=10.0f){
floorBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
floorBatch.Vertex3f(-10.0f, -10.0f, z);
floorBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
floorBatch.Vertex3f(10.0f, -10.0f, z);
floorBatch.MultiTexCoord2f(0, 0.0f, 1.0f);
floorBatch.Vertex3f(-10.0f, -10.0f, z - 10.0f);
floorBatch.MultiTexCoord2f(0, 1.0f, 1.0f);
floorBatch.Vertex3f(10.0f, -10.0f, z - 10.0f);
}
floorBatch.End();
ceilingBatch.Begin(GL_TRIANGLE_STRIP, 28, 1);
for(z = 60.0f; z >= 0.0f; z -=10.0f) {
ceilingBatch.MultiTexCoord2f(0, 0.0f, 1.0f);
ceilingBatch.Vertex3f(-10.0f, 10.0f, z - 10.0f);
ceilingBatch.MultiTexCoord2f(0, 1.0f, 1.0f);
ceilingBatch.Vertex3f(10.0f, 10.0f, z - 10.0f);
ceilingBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
ceilingBatch.Vertex3f(-10.0f, 10.0f, z);
ceilingBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
ceilingBatch.Vertex3f(10.0f, 10.0f, z);
}
ceilingBatch.End();
leftWallBatch.Begin(GL_TRIANGLE_STRIP, 28, 1);
for(z = 60.0f; z >= 0.0f; z -=10.0f){
leftWallBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
leftWallBatch.Vertex3f(-10.0f, -10.0f, z);
leftWallBatch.MultiTexCoord2f(0, 0.0f, 1.0f);
leftWallBatch.Vertex3f(-10.0f, 10.0f, z);
leftWallBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
leftWallBatch.Vertex3f(-10.0f, -10.0f, z - 10.0f);
leftWallBatch.MultiTexCoord2f(0, 1.0f, 1.0f);
leftWallBatch.Vertex3f(-10.0f, 10.0f, z - 10.0f);
}
leftWallBatch.End();
rightWallBatch.Begin(GL_TRIANGLE_STRIP, 28, 1);
for(z = 60.0f; z >= 0.0f; z -=10.0f){
rightWallBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
rightWallBatch.Vertex3f(10.0f, -10.0f, z);
rightWallBatch.MultiTexCoord2f(0, 0.0f, 1.0f);
rightWallBatch.Vertex3f(10.0f, 10.0f, z);
rightWallBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
rightWallBatch.Vertex3f(10.0f, -10.0f, z - 10.0f);
rightWallBatch.MultiTexCoord2f(0, 1.0f, 1.0f);
rightWallBatch.Vertex3f(10.0f, 10.0f, z - 10.0f);
}
rightWallBatch.End();
}複製代碼
在這個函數裏先設置了清屏顏色,初始化固定管線,而後設置了紋理相關的參數(頂點座標和紋理座標)。關於頂點座標和紋理座標,這裏只說一個面,以下圖紅色字體和黃色字符分別表示出了底部的頂點座標和紋理座標。其餘面也都大同小異。oop
這裏要注意的是頂點座標,要把他想象成立體的因此越往屏幕裏面z值越小,而不是y值變小。post
處理完這些後接着就是實現main函數裏註冊的各個回調函數了 ,咱們首先來看ChangeSize函數。字體
void ChangeSize(int w, int h)
{
if(h == 0)
h = 1;
//2.將視口設置大小
glViewport(0, 0, w, h);
GLfloat fAspect = (GLfloat)w/(GLfloat)h;
//3.生成透視投影
viewFrustum.SetPerspective(80.0f,fAspect,1.0,120.0);
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
}複製代碼
這個函數是窗口大小改變的回調函數,在這裏能夠依據窗口的寬高,設置視口大小。同時生產透視投影,初始化投影矩陣. 作完這一步就能夠開始咱們的繪製了,下面看最後一個函數RenderScene,用來繪製窗口內容,具體以下:ui
void RenderScene(void)
{
//1.用當前清除色,清除窗口
glClear(GL_COLOR_BUFFER_BIT);
//2.模型視圖壓棧
modelViewMatrix.PushMatrix();
//Z軸平移viewZ 距離
modelViewMatrix.Translate(0.0f, 0.0f, viewZ);
//3.紋理替換矩陣着色器
/*
參數1:GLT_SHADER_TEXTURE_REPLACE(着色器標籤)
參數2:模型視圖投影矩陣
參數3:紋理層
*/
shaderManager.UseStockShader(GLT_SHADER_TEXTURE_REPLACE, transformPipeline.GetModelViewProjectionMatrix(), 0);
//4.綁定紋理
/*
參數1:紋理模式,GL_TEXTURE_1D、GL_TEXTURE_2D、GL_TEXTURE_3D
參數2:須要綁定的紋理
*/
glBindTexture(GL_TEXTURE_2D, textures[TEXTURE_FLOOR]);
floorBatch.Draw();
glBindTexture(GL_TEXTURE_2D, textures[TEXTURE_CEILING]);
ceilingBatch.Draw();
glBindTexture(GL_TEXTURE_2D, textures[TEXTURE_BRICK]);
leftWallBatch.Draw();
rightWallBatch.Draw();
modelViewMatrix.PopMatrix();
//6.緩存區交換
glutSwapBuffers();
}複製代碼
這裏須要注意一下,繪製完畢之後須要交換一下緩存區,由於咱們採用的是雙緩衝區繪製。寫到這一步咱們基本都畫面已經繪製完畢了,最後再看一下怎麼實現先後移動的視覺效果,這裏咱們經過鍵盤輸入先後按鍵來實現先後移動的視覺效果,具體代碼以下:spa
//先後移動視口來對方向鍵做出響應
void SpecialKeys(int key, int x, int y){
if(key == GLUT_KEY_UP)
//移動的是深度值,Z
viewZ += 0.5f;
if(key == GLUT_KEY_DOWN)
viewZ -= 0.5f;
//更新窗口,便可回調到RenderScene函數裏
glutPostRedisplay();
}複製代碼
好了,到這裏這個案例就已經寫完了 這個案例主要是告訴你們紋理的使用,重點是紋理座標的設置以及相關API的靈活運用。code