OpenGL藍寶書源碼學習(十一)第五章——Pyramid.cpp

進行了紋理貼圖的金字塔源碼示例

// 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對象的一部分。





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章