在前面的例子中我們繪制過金字塔重荠,但是我們填充的顏色是綠色的如下圖:
金字塔
今天我們將要在金字塔各個面上使用紋理繪制上相應(yīng)的像素?cái)?shù)據(jù),如下圖:
紋理填充金字塔
這次我們將要用到上節(jié)講的紋理相關(guān)APi了条篷。
首先看下執(zhí)行流程:
main函數(shù)——>SetupRC函數(shù)——>LoadTGATexture函數(shù)——>MakePyramid函數(shù)——>ChangeSize函數(shù)——>RenderScene函數(shù)——>ShutdownRC函數(shù)
在我們控制旋轉(zhuǎn)或者別的操作的時候會多次調(diào)用RenderScene函數(shù)來重新渲染:
簡單說下幾個函數(shù)的作用
main:這個不用多說侮叮,程序入口
int main(int argc, char* argv[])
{
gltSetWorkingDirectory(argv[0]);
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
glutInitWindowSize(800, 600);
glutCreateWindow("Pyramid");
glutReshapeFunc(ChangeSize);
glutSpecialFunc(SpecialKeys);
glutDisplayFunc(RenderScene);
GLenum err = glewInit();
if (GLEW_OK != err) {
fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
return 1;
}
SetupRC();
glutMainLoop();
ShutdownRC();
return 0;
}
SetupRC:初始化數(shù)據(jù)
Void SetupRC()
{
//1.
glClearColor(0.7f, 0.7f, 0.7f, 1.0f );
shaderManager.InitializeStockShaders();
//2.
glEnable(GL_DEPTH_TEST);
//3.
//分配紋理對象 參數(shù)1:紋理對象個數(shù),參數(shù)2:紋理對象指針
glGenTextures(1, &textureID);
//綁定紋理狀態(tài) 參數(shù)1:紋理狀態(tài)2D 參數(shù)2:紋理對象
glBindTexture(GL_TEXTURE_2D, textureID);
//將TGA文件加載為2D紋理寡润。
//參數(shù)1:紋理文件名稱
//參數(shù)2&參數(shù)3:需要縮小&放大的過濾器
//參數(shù)4:紋理坐標(biāo)環(huán)繞模式
LoadTGATexture("stone.tga", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR, GL_CLAMP_TO_EDGE);
//4.創(chuàng)造金字塔pyramidBatch
MakePyramid(pyramidBatch);
//5.
/**相機(jī)frame MoveForward(平移)
參數(shù)1:Z我注,深度(屏幕到圖形的Z軸距離)
*/
cameraFrame.MoveForward(-10);
}
LoadTGATexture:加載紋理
// 將TGA文件加載為2D紋理按咒。
bool LoadTGATexture(const char *szFileName, GLenum minFilter, GLenum magFilter, GLenum wrapMode)
{
GLbyte *pBits;
int nWidth, nHeight, nComponents;
GLenum eFormat;
//1、讀紋理位但骨,讀取像素
//參數(shù)1:紋理文件名稱
//參數(shù)2:文件寬度地址
//參數(shù)3:文件高度地址
//參數(shù)4:文件組件地址
//參數(shù)5:文件格式地址
//返回值:pBits,指向圖像數(shù)據(jù)的指針
pBits = gltReadTGABits(szFileName, &nWidth, &nHeight, &nComponents, &eFormat);
if(pBits == NULL)
return false;
//2励七、設(shè)置紋理參數(shù)
//參數(shù)1:紋理維度
//參數(shù)2:為S/T坐標(biāo)設(shè)置模式
//參數(shù)3:wrapMode,環(huán)繞模式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapMode);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapMode);
//參數(shù)1:紋理維度
//參數(shù)2:線性過濾
//參數(shù)3: 縮小/放大過濾方式.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter);
//3.載入紋理
//參數(shù)1:紋理維度
//參數(shù)2:mip貼圖層次
//參數(shù)3:紋理單元存儲的顏色成分(從讀取像素圖是獲得)
//參數(shù)4:加載紋理寬
//參數(shù)5:加載紋理高
//參數(shù)6:加載紋理的深度
//參數(shù)7:像素?cái)?shù)據(jù)的數(shù)據(jù)類型(GL_UNSIGNED_BYTE智袭,每個顏色分量都是一個8位無符號整數(shù))
//參數(shù)8:指向紋理圖像數(shù)據(jù)的指針
glTexImage2D(GL_TEXTURE_2D, 0, nComponents, nWidth, nHeight, 0,
eFormat, GL_UNSIGNED_BYTE, pBits);
//使用完畢釋放pBits
free(pBits);
//只有minFilter 等于以下四種模式,才可以生成Mip貼圖
//GL_NEAREST_MIPMAP_NEAREST具有非常好的性能掠抬,并且閃爍現(xiàn)象非常弱
//GL_LINEAR_MIPMAP_NEAREST常常用于對游戲進(jìn)行加速吼野,它使用了高質(zhì)量的線性過濾器
//GL_LINEAR_MIPMAP_LINEAR 和GL_NEAREST_MIPMAP_LINEAR 過濾器在Mip層之間執(zhí)行了一些額外的插值,以消除他們之間的過濾痕跡两波。
//GL_LINEAR_MIPMAP_LINEAR 三線性Mip貼圖瞳步。紋理過濾的黃金準(zhǔn)則,具有最高的精度腰奋。
if(minFilter == GL_LINEAR_MIPMAP_LINEAR ||
minFilter == GL_LINEAR_MIPMAP_NEAREST ||
minFilter == GL_NEAREST_MIPMAP_LINEAR ||
minFilter == GL_NEAREST_MIPMAP_NEAREST)
//4.紋理生成所有的Mip層
//參數(shù):GL_TEXTURE_1D单起、GL_TEXTURE_2D、GL_TEXTURE_3D
glGenerateMipmap(GL_TEXTURE_2D);
return true;
}
MakePyramid:設(shè)置金字塔頂點(diǎn)數(shù)據(jù)及紋理坐標(biāo)
//繪制金字塔
void MakePyramid(GLBatch& pyramidBatch)
{
/*1劣坊、通過pyramidBatch組建三角形批次
參數(shù)1:類型
參數(shù)2:頂點(diǎn)數(shù)
參數(shù)3:這個批次中將會應(yīng)用1個紋理
注意:如果不寫這個參數(shù)馏臭,默認(rèn)為0。
*/
pyramidBatch.Begin(GL_TRIANGLES, 18, 1);
/***前情導(dǎo)入
2)設(shè)置紋理坐標(biāo)
void MultiTexCoord2f(GLuint texture, GLclampf s, GLclampf t);
參數(shù)1:texture讼稚,紋理層次,對于使用存儲著色器來進(jìn)行渲染绕沈,設(shè)置為0
參數(shù)2:s:對應(yīng)頂點(diǎn)坐標(biāo)中的x坐標(biāo)
參數(shù)3:t:對應(yīng)頂點(diǎn)坐標(biāo)中的y
(s,t,r,q對應(yīng)頂點(diǎn)坐標(biāo)的x,y,z,w)
pyramidBatch.MultiTexCoord2f(0,s,t);
3)void Vertex3f(GLfloat x, GLfloat y, GLfloat z);
void Vertex3fv(M3DVector3f vVertex);
向三角形批次類添加頂點(diǎn)數(shù)據(jù)(x,y,z);
pyramidBatch.Vertex3f(-1.0f, -1.0f, -1.0f);
*/
//塔頂
M3DVector3f vApex = { 0.0f, 1.0f, 0.0f };
M3DVector3f vFrontLeft = { -1.0f, -1.0f, 1.0f };
M3DVector3f vFrontRight = { 1.0f, -1.0f, 1.0f };
M3DVector3f vBackLeft = { -1.0f, -1.0f, -1.0f };
M3DVector3f vBackRight = { 1.0f, -1.0f, -1.0f };
//金字塔底部
//底部的四邊形 = 三角形X + 三角形Y
//三角形X = (vBackLeft,vBackRight,vFrontRight)
//vBackLeft
pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
pyramidBatch.Vertex3fv(vBackLeft);
//vBackRight
pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
pyramidBatch.Vertex3fv(vBackRight);
//vFrontRight
pyramidBatch.MultiTexCoord2f(0, 1.0f, 1.0f);
pyramidBatch.Vertex3fv(vFrontRight);
//三角形Y =(vFrontLeft,vBackLeft,vFrontRight)
//vFrontLeft
pyramidBatch.MultiTexCoord2f(0, 0.0f, 1.0f);
pyramidBatch.Vertex3fv(vFrontLeft);
//vBackLeft
pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
pyramidBatch.Vertex3fv(vBackLeft);
//vFrontRight
pyramidBatch.MultiTexCoord2f(0, 1.0f, 1.0f);
pyramidBatch.Vertex3fv(vFrontRight);
// 金字塔前面
//三角形:(Apex锐想,vFrontLeft,vFrontRight)
pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.0f);
pyramidBatch.Vertex3fv(vApex);
pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
pyramidBatch.Vertex3fv(vFrontLeft);
pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
pyramidBatch.Vertex3fv(vFrontRight);
//金字塔左邊
//三角形:(vApex, vBackLeft, vFrontLeft)
pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.0f);
pyramidBatch.Vertex3fv(vApex);
pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
pyramidBatch.Vertex3fv(vBackLeft);
pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
pyramidBatch.Vertex3fv(vFrontLeft);
//金字塔右邊
//三角形:(vApex, vFrontRight, vBackRight)
pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.0f);
pyramidBatch.Vertex3fv(vApex);
pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
pyramidBatch.Vertex3fv(vFrontRight);
pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
pyramidBatch.Vertex3fv(vBackRight);
//金字塔后邊
//三角形:(vApex, vBackRight, vBackLeft)
pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.0f);
pyramidBatch.Vertex3fv(vApex);
pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
pyramidBatch.Vertex3fv(vBackRight);
pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
pyramidBatch.Vertex3fv(vBackLeft);
//結(jié)束批次設(shè)置
pyramidBatch.End();
}
在這函數(shù)中我們要把紋理坐標(biāo)和三角形坐標(biāo)相對應(yīng):
紋理坐標(biāo)和實(shí)際坐標(biāo)映射圖
因?yàn)榻鹱炙?個面乍狐,且底面是兩個三角形組成赠摇,所以上述代碼中我們設(shè)置了5個三角形對應(yīng)的紋理坐標(biāo);
金字塔的各個面與紋理的映射
RenderScene:渲染函數(shù)浅蚪,綁定紋理藕帜、繪制金字塔
void RenderScene(void)
{
//1.顏色值&光源位置
static GLfloat vLightPos [] = { 1.0f, 1.0f, 0.0f };
static GLfloat vWhite [] = { 1.0f, 1.0f, 1.0f, 1.0f };
//2.清理緩存區(qū)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
//3.當(dāng)前模型視頻壓棧
modelViewMatrix.PushMatrix();
//添加照相機(jī)矩陣
M3DMatrix44f mCamera;
//從camraFrame中獲取一個4*4的矩陣
cameraFrame.GetCameraMatrix(mCamera);
//矩陣乘以矩陣堆棧頂部矩陣,相乘結(jié)果存儲到堆棧的頂部 將照相機(jī)矩陣 與 當(dāng)前模型矩陣相乘 壓入棧頂
modelViewMatrix.MultMatrix(mCamera);
//創(chuàng)建mObjectFrame矩陣
M3DMatrix44f mObjectFrame;
//從objectFrame中獲取矩陣惜傲,objectFrame保存的是特殊鍵位的變換矩陣
objectFrame.GetMatrix(mObjectFrame);
//矩陣乘以矩陣堆棧頂部矩陣洽故,相乘結(jié)果存儲到堆棧的頂部 將世界變換矩陣 與 當(dāng)前模型矩陣相乘 壓入棧頂
modelViewMatrix.MultMatrix(mObjectFrame);
//4.綁定紋理,因?yàn)槲覀兊捻?xiàng)目中只有一個紋理盗誊。如果有多個紋理时甚。綁定紋理很重要
glBindTexture(GL_TEXTURE_2D, textureID);
//5.紋理替換矩陣著色器
/*
參數(shù)1:GLT_SHADER_TEXTURE_REPLACE(著色器標(biāo)簽)
參數(shù)2:模型視圖投影矩陣
參數(shù)3:紋理層
*/
shaderManager.UseStockShader(GLT_SHADER_TEXTURE_REPLACE, transformPipeline.GetModelViewProjectionMatrix(), 0);
//pyramidBatch 繪制
pyramidBatch.Draw();
//模型視圖出棧,恢復(fù)矩陣(push一次就要pop一次)
modelViewMatrix.PopMatrix();
//6.交換緩存區(qū)
glutSwapBuffers();
}
SpecialKeys:圖形根據(jù)特殊鍵位的出發(fā)進(jìn)行旋轉(zhuǎn)
void SpecialKeys(int key, int x, int y)
{
if(key == GLUT_KEY_UP)
objectFrame.RotateWorld(m3dDegToRad(-5.0f), 1.0f, 0.0f, 0.0f);
if(key == GLUT_KEY_DOWN)
objectFrame.RotateWorld(m3dDegToRad(5.0f), 1.0f, 0.0f, 0.0f);
if(key == GLUT_KEY_LEFT)
objectFrame.RotateWorld(m3dDegToRad(-5.0f), 0.0f, 1.0f, 0.0f);
if(key == GLUT_KEY_RIGHT)
objectFrame.RotateWorld(m3dDegToRad(5.0f), 0.0f, 1.0f, 0.0f);
glutPostRedisplay();
}
ShutdownRC:清理數(shù)據(jù)哈踱,類似于iOS中的dealloc函數(shù)
// 清理…例如刪除紋理對象
void ShutdownRC(void)
{
glDeleteTextures(1, &textureID);
}