進行了紋理貼圖的金字塔源碼示例
// Pyramid.cpp
// OpenGL SuperBible, Chapter 5
// Demonstrates Texture mapping a pyramid
// Program by Richard S. Wright Jr.
#include // OpenGL toolkit
#include
#include
#include
#include
#include
#include
#ifdef __APPLE__
#include
#else
#define FREEGLUT_STATIC
#include
#endif
/////////////////////////////////////////////////////////////////////////////////
// An assortment of needed classes
GLShaderManager shaderManager;
GLMatrixStack modelViewMatrix;
GLMatrixStack projectionMatrix;
GLFrame cameraFrame;
GLFrame objectFrame;
GLFrustum viewFrustum;
GLBatch pyramidBatch;
GLuint textureID;
GLGeometryTransform transformPipeline;
M3DMatrix44f shadowMatrix;
void MakePyramid(GLBatch& pyramidBatch)
{
pyramidBatch.Begin(GL_TRIANGLES, 18, 1);
// Bottom of pyramid
pyramidBatch.Normal3f(0.0f, -1.0f, 0.0f);
pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
pyramidBatch.Vertex3f(-1.0f, -1.0f, -1.0f);
pyramidBatch.Normal3f(0.0f, -1.0f, 0.0f);
pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
pyramidBatch.Vertex3f(1.0f, -1.0f, -1.0f);
pyramidBatch.Normal3f(0.0f, -1.0f, 0.0f);
pyramidBatch.MultiTexCoord2f(0, 1.0f, 1.0f);
pyramidBatch.Vertex3f(1.0f, -1.0f, 1.0f);
pyramidBatch.Normal3f(0.0f, -1.0f, 0.0f);
pyramidBatch.MultiTexCoord2f(0, 0.0f, 1.0f);
pyramidBatch.Vertex3f(-1.0f, -1.0f, 1.0f);
pyramidBatch.Normal3f(0.0f, -1.0f, 0.0f);
pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
pyramidBatch.Vertex3f(-1.0f, -1.0f, -1.0f);
pyramidBatch.Normal3f(0.0f, -1.0f, 0.0f);
pyramidBatch.MultiTexCoord2f(0, 1.0f, 1.0f);
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 };
M3DVector3f n;
// Front of Pyramid
m3dFindNormal(n, vApex, vFrontLeft, vFrontRight);
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.0f);
pyramidBatch.Vertex3fv(vApex); // Apex
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
pyramidBatch.Vertex3fv(vFrontLeft); // Front left corner
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
pyramidBatch.Vertex3fv(vFrontRight); // Front right corner
m3dFindNormal(n, vApex, vBackLeft, vFrontLeft);
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.0f);
pyramidBatch.Vertex3fv(vApex); // Apex
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
pyramidBatch.Vertex3fv(vBackLeft); // Back left corner
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
pyramidBatch.Vertex3fv(vFrontLeft); // Front left corner
m3dFindNormal(n, vApex, vFrontRight, vBackRight);
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.0f);
pyramidBatch.Vertex3fv(vApex); // Apex
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
pyramidBatch.Vertex3fv(vFrontRight); // Front right corner
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
pyramidBatch.Vertex3fv(vBackRight); // Back right cornder
m3dFindNormal(n, vApex, vBackRight, vBackLeft);
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.0f);
pyramidBatch.Vertex3fv(vApex); // Apex
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
pyramidBatch.Vertex3fv(vBackRight); // Back right cornder
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
pyramidBatch.Vertex3fv(vBackLeft); // Back left corner
pyramidBatch.End();
}
// Load a TGA as a 2D Texture. Completely initialize the state
bool LoadTGATexture(const char *szFileName, GLenum minFilter, GLenum magFilter, GLenum wrapMode)
{
GLbyte *pBits;
int nWidth, nHeight, nComponents;
GLenum eFormat;
// Read the texture bits
pBits = gltReadTGABits(szFileName, &nWidth, &nHeight, &nComponents, &eFormat);
if(pBits == NULL)
return false;
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapMode);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapMode);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexImage2D(GL_TEXTURE_2D, 0, nComponents, nWidth, nHeight, 0,
eFormat, GL_UNSIGNED_BYTE, pBits);
free(pBits);
if(minFilter == GL_LINEAR_MIPMAP_LINEAR ||
minFilter == GL_LINEAR_MIPMAP_NEAREST ||
minFilter == GL_NEAREST_MIPMAP_LINEAR ||
minFilter == GL_NEAREST_MIPMAP_NEAREST)
glGenerateMipmap(GL_TEXTURE_2D);
return true;
}
///////////////////////////////////////////////////////////////////////////////
// This function does any needed initialization on the rendering context.
// This is the first opportunity to do any OpenGL related tasks.
void SetupRC()
{
// Black background
glClearColor(0.7f, 0.7f, 0.7f, 1.0f );
shaderManager.InitializeStockShaders();
glEnable(GL_DEPTH_TEST);
glGenTextures(1, &textureID);
glBindTexture(GL_TEXTURE_2D, textureID);
LoadTGATexture("stone.tga", GL_LINEAR, GL_LINEAR, GL_CLAMP_TO_EDGE);
MakePyramid(pyramidBatch);
cameraFrame.MoveForward(-7.0f);
}
///////////////////////////////////////////////////////////////////////////////
// Cleanup... such as deleting texture objects
void ShutdownRC(void)
{
glDeleteTextures(1, &textureID);
}
///////////////////////////////////////////////////////////////////////////////
// Called to draw scene
void RenderScene(void)
{
static GLfloat vLightPos [] = { 1.0f, 1.0f, 0.0f };
static GLfloat vWhite [] = { 1.0f, 1.0f, 1.0f, 1.0f };
// Clear the window with current clearing color
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
modelViewMatrix.PushMatrix();
M3DMatrix44f mCamera;
cameraFrame.GetCameraMatrix(mCamera);
modelViewMatrix.MultMatrix(mCamera);
M3DMatrix44f mObjectFrame;
objectFrame.GetMatrix(mObjectFrame);
modelViewMatrix.MultMatrix(mObjectFrame);
glBindTexture(GL_TEXTURE_2D, textureID);
shaderManager.UseStockShader(GLT_SHADER_TEXTURE_POINT_LIGHT_DIFF,
transformPipeline.GetModelViewMatrix(),
transformPipeline.GetProjectionMatrix(),
vLightPos, vWhite, 0);
pyramidBatch.Draw();
modelViewMatrix.PopMatrix();
// Flush drawing commands
glutSwapBuffers();
}
// Respond to arrow keys by moving the camera frame of reference
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();
}
///////////////////////////////////////////////////////////////////////////////
// Window has changed size, or has just been created. In either case, we need
// to use the window dimensions to set the viewport and the projection matrix.
void ChangeSize(int w, int h)
{
glViewport(0, 0, w, h);
viewFrustum.SetPerspective(35.0f, float(w) / float(h), 1.0f, 500.0f);
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
}
///////////////////////////////////////////////////////////////////////////////
// Main entry point for GLUT based programs
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;
}
一、源碼解析
GLuint textureID;//無符號整數類型的紋理對象
1、void SetupRC()
//首先先分配分離對象,傳的參數是紋理對象標識的地址
glGenTextures(1, &textureID);//綁定紋理,GL_TEXTURE_2D表示是2D紋理,textureID是需要綁定的紋理對象
glBindTexture(GL_TEXTURE_2D, textureID);//加載一個tga格式的圖像作爲2D的紋理,參數1:紋理的名稱(必要時還需傳入路徑);參數2:收縮紋理時選擇的過濾方式;參數3:拉伸紋理時選擇的過濾方式;//參數4:紋理環繞模式。
LoadTGATexture("stone.tga", GL_LINEAR, GL_LINEAR, GL_CLAMP_TO_EDGE);//繪製金字塔圖形MakePyramid(pyramidBatch);//照相機參考幀向前移動7個像素(z軸的負方向)cameraFrame.MoveForward(-7.0f)2、bool LoadTGATexture(const char *szFileName, GLenum minFilter, GLenum magFilter, GLenum wrapMode)
GLbyte *pBits;//圖像數據指針局部變量
int nWidth, nHeight, nComponents;//返回的指向新的緩衝區、紋理的高低各寬度。
GLenum eFormat;//OpenGL圖像數據格式//讀取tga文件的紋理數據
pBits = gltReadTGABits(szFileName, &nWidth, &nHeight, &nComponents, &eFormat);
if(pBits == NULL)
return false;glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapMode);//設置s紋理座標的紋理環繞方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapMode);//設置t紋理座標的紋理環繞方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter);//設置收縮紋理的過濾方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter);//設置拉伸紋理的過濾方式glPixelStorei(GL_UNPACK_ALIGNMENT, 1);//從數據緩衝區(內存緩衝區)解包圖像數據
glTexImage2D(GL_TEXTURE_2D, 0, nComponents, nWidth, nHeight, 0, //從存儲器緩衝區載入紋理數據,一旦被載入,這些紋理就會成爲當前紋理狀態
eFormat, GL_UNSIGNED_BYTE, pBits);free(pBits);//釋放臨時緩衝區
//如果收縮的過濾方式是下面這4中Mip貼圖過濾模式,那就生成2維的Mip層
if(minFilter == GL_LINEAR_MIPMAP_LINEAR ||
minFilter == GL_LINEAR_MIPMAP_NEAREST ||
minFilter == GL_NEAREST_MIPMAP_LINEAR ||
minFilter == GL_NEAREST_MIPMAP_NEAREST)
glGenerateMipmap(GL_TEXTURE_2D);注意:
1、紋理過濾設置爲GL_LINEAR或GLNEAREST,那麼只有紋理貼圖基層會被使用。所以必須指定其中一個Mip貼圖過濾器,才能使用所有已加載的Mip層。
2、LoadTGATexture對紋理狀態進行設置,因爲它被防止在調用glBindTexture之後,所以就成爲了由textureID標識的對象的一部分。
3、void MakePyramid(GLBatch& pyramidBatch)
注意:前面學習使用GLBatch類時,我們用CopyVertexData函數一次性將整整一個數組的數據複製到一個批次中。GLBatch類還包含一些函數,允許我們每次一個頂點建立一個批次。類似立即模式,它可以很方便手動構建幾何圖形。
pyramidBatch.Begin(GL_TRIANGLES, 18, 1);//開始一個三角形批次,18個頂點,1代表在這個批次中將使用一個紋理
// 使用六個頂點建立金字塔的底部的圖形批次
/*1、Normal3f方法向批次中添加了一個表面法線2、MultiTexCoord2f添加了一個紋理座標3、Vertex3f添加了頂點的位置
如果爲任何頂點指定了法線或紋理座標,那麼就必須爲每個頂點進行同樣的指定。
MultiTexCoord2f指定了紋理座標和通過texture指定紋理層次
表面法線是有方向的向量,它代表表面(或者頂點)面對的方向
*/
pyramidBatch.Normal3f(0.0f, -1.0f, 0.0f);
pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
pyramidBatch.Vertex3f(-1.0f, -1.0f, -1.0f);
pyramidBatch.Normal3f(0.0f, -1.0f, 0.0f);
pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
pyramidBatch.Vertex3f(1.0f, -1.0f, -1.0f);
pyramidBatch.Normal3f(0.0f, -1.0f, 0.0f);
pyramidBatch.MultiTexCoord2f(0, 1.0f, 1.0f);
pyramidBatch.Vertex3f(1.0f, -1.0f, 1.0f);
pyramidBatch.Normal3f(0.0f, -1.0f, 0.0f);
pyramidBatch.MultiTexCoord2f(0, 0.0f, 1.0f);
pyramidBatch.Vertex3f(-1.0f, -1.0f, 1.0f);
pyramidBatch.Normal3f(0.0f, -1.0f, 0.0f);
pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
pyramidBatch.Vertex3f(-1.0f, -1.0f, -1.0f);
pyramidBatch.Normal3f(0.0f, -1.0f, 0.0f);
pyramidBatch.MultiTexCoord2f(0, 1.0f, 1.0f);
pyramidBatch.Vertex3f(1.0f, -1.0f, 1.0f);注意:這裏是6個頂點,是因爲有個兩個頂點是重複使用的,在繪製金字塔的底部時,是繪製個三角形拼接成一個四邊形的。
//建立金字塔側側面的4個三角形表面,這裏拿前向的表面解析,其他三個方向的側面是一樣的
//指定頂尖的頂點和底部的頂點,n被定義爲法線的座標向量
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 };
M3DVector3f n;
m3dFindNormal(n, vApex, vFrontLeft, vFrontRight);//根據三個頂點計算出這三個頂點構成的表面的法線的座標
pyramidBatch.Normal3fv(n);//指定法線座標
pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.0f);//指定紋理座標
pyramidBatch.Vertex3fv(vApex);// 金字塔頂頂點座標
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
pyramidBatch.Vertex3fv(vFrontLeft);// 前面左下角的頂點座標
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
pyramidBatch.Vertex3fv(vFrontRight);// 前面右下角的頂點座標.
.
pyramidBatch.End();//完成批次圖元的設置
4、void RenderScene(void)
static GLfloat vLightPos [] = { 1.0f, 1.0f, 0.0f };//光源的位置
static GLfloat vWhite [] = { 1.0f, 1.0f, 1.0f, 1.0f };//圖元的基本顏色// 使用當前的清除顏色清除當前窗口
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);modelViewMatrix.PushMatrix(); //模型視圖矩陣堆棧默認把單位矩陣壓棧
M3DMatrix44f mCamera;
cameraFrame.GetCameraMatrix(mCamera); //得到照相機參考幀
modelViewMatrix.MultMatrix(mCamera); //使用矩陣堆棧的頂部矩陣(單位矩陣)與照相機矩陣相乘壓棧到模型視圖矩陣堆棧中
M3DMatrix44f mObjectFrame;
objectFrame.GetMatrix(mObjectFrame);//得到物體的矩陣
modelViewMatrix.MultMatrix(mObjectFrame); //使用矩陣堆棧的頂部矩陣(照相機矩陣)相乘壓棧到模型視圖矩陣堆棧中
glBindTexture(GL_TEXTURE_2D, textureID);//綁定紋理,所以後面的使用着色器就是textureID標識對象的一部分
//使用紋理光源着色器
shaderManager.UseStockShader(GLT_SHADER_TEXTURE_POINT_LIGHT_DIFF,
transformPipeline.GetModelViewMatrix(), //從管線中獲得模型視圖矩陣
transformPipeline.GetProjectionMatrix(), //從管線中獲得投影矩陣
vLightPos, vWhite, 0);pyramidBatch.Draw();//提交批次圖元給着色器進行渲染
modelViewMatrix.PopMatrix(); //模型視圖矩陣堆棧出棧,避免對下次圖形渲染的影響。
// 刷新渲染命令
glutSwapBuffers();
5、void ChangeSize(int w, int h)
glViewport(0, 0, w, h);//視口根據窗口的大小改變
viewFrustum.SetPerspective(35.0f, float(w) / float(h), 1.0f, 500.0f);//設置透視投影
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());//加載投影矩陣
transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);//爲模型視圖矩陣堆棧和投影矩陣設置管線管理6、void ShutdownRC(void)
{
glDeleteTextures(1, &textureID); //刪除(解除)紋理
}
二、小結
這節的源碼展示了3D渲染中紋理的應用,在SetupRC一次性設置中,先生成紋理,再進行綁定,最後加載紋理資源,把紋理數據存儲在一個指向紋理數據的指針,通過glTexImage函數進行從緩衝區中載入,載入後,紋理就會變成當前紋理狀態。
在加載紋理資源時,設置了拉伸和收縮的過濾(線性和最臨近)方式,紋理的環繞方式(剪裁和重複),再進行像素包裝,最後從緩衝區載入紋理成當前紋理狀態。
注意:在選擇使用着色器進行渲染前先綁定紋理,所以在使用着色器渲染時就是textureID對象的一部分。