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





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值