OpenGL蓝宝书源码学习(十一)第五章——Pyramid.cpp

本文介绍了一个OpenGL示例程序,该程序演示了如何为一个金字塔模型应用纹理映射,并详细解析了纹理加载、金字塔构建和渲染过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

进行了纹理贴图的金字塔源码示例

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





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值