进行了纹理贴图的金字塔源码示例
// 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对象的一部分。