10、OpenGL初探之OpenGL纹理及金字塔纹理绘制实战

    德者,本也。财者,末也。但行好事,莫问前程。

一、认识纹理


          纹理是什么?你可以把它理解成一张贴纸一样的东西,在物体表面贴上图案,纹理的作用就是用来装饰我们的物体模型,就像装修的时候,在家里的墙壁上贴墙纸一样,当然纹理的作用应该远远不止这些。

个人思考:在一些游戏场景中,特别是地图,比如吃鸡这种主机游戏,场景都是用很多相同的,类似3D纹理的立体图形拼出来的,比如一片草地,一片树林,一片房屋,这些3D场景和墙纸及纹理的定义没有本质上的区别,不知广义上把这些“场景贴纸”称为纹理合不合适?大佬可以在评论区告知我或者可以一起讨论。

二、纹理基础


    了解纹理基础之前,让我们先来了解一下图像的存储

1、原始图像数据在内存中的存储

图像占用的存储空间 = 图像的高度 * 图像的宽度 * 每个像素点的字节数

2、认识OpenGL图像存储相关函数

1、改变像素存储方式

void glPixelStorei(GLenum pname, GLint param)

2、恢复像素存储方式

void glPxielStoref (GLenum pname, GLFloat param)

参数1:GL_UNPACK_ALIGNMENT,指定OpenGL如何从数据缓存区中解包图像数据;

参数2:表示参数GL_UNPACK_ALIGNMENT设置的值。GL_UNPACK_ALIGNMENT指内存中的每一个像素行起点的排列请求,允许设置为1(byte排列)、2(排列为偶数byte的行)、4(字word排列)8(行从双字节边界开始)

例:glPxielStorei(GL_UNPACK_ALIGNMENT, 1)

3、从颜色缓冲区内容作为像素图直接读取(重要)

void glReadPixels(GLint x,  GLint y, GLSizei width, GLSizei height, GLenum format, GLenum type, const void*pixels);

参数1:x,矩形左下角的窗口坐标x值

参数2:y,矩形左下角的窗口坐标y值

参数3:width,矩形的宽,以像素为单位

参数4:height,矩形的高,以像素为单位

参数5:format,OpenGL的像素格式,参照下表1

参数6:type,解释参数pxiels指向的数据,告诉OpenGL使用缓冲区的什么数据类型来存储颜色分量,像素数据的数据类型,参照下表2

参数7:pxiels,指向图形数据的指针


表1、OpenGL的像素格式,常用的是GL_RGB和GL_RGBA
表2、像素数据的数据类型,常用的有GL_UNSIGNED_INT

4、载入纹理

void glTexImage1D(GLenum target , GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, void*data)

void glTexImage2D(GLenum target , GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, void*data)

void glTexImage3D(GLenum target , GLint level, GLint internalformat, GLsizei width, GLsizei height,GLsizei depth, GLint border, GLenum format, GLenum type, void*data)

参数1:target:GL_TEXTURE_2D,GL_TEXTURE_1D, GL_TEXTURE_3D

参数2:level指定所加载的mip贴图层次,一般我们都把这个参数设置为0.

参数3:internalformat:每个纹理单元中存储多少颜色成分。

参数4:width,height,depth参数:指的是加载纹理的宽度、高度、深度;需要注意的是这些数必须是2的整数次方。这个因为OpenGL旧版本上遗留下的一个要求,当然现在已经支持可以不是2的整数次方,但是开发者已经习惯使用2的证书此房去设置这些参数。

参数5:border:允许为纹理贴图制定一个边界宽度

参数6:format、type、data参数与glDrawPxiels函数对于的参数相同。

5、更新纹理

void glTexSubImage1D(GLenum target,GLint level,GLint xOffset, GLint yoffset,GLSizei width, GLenum format, GLenum type,const  GLvoid * data)

void glTexSubImage2D(GLenum target,GLint level,GLint xOffset, GLint yoffset,GLSizei width, GLSizei height, GLenum format, GLenum type,const  GLvoid * data)

void glTexSubImage3D(GLenum target,GLint level,GLint xOffset, GLint yoffset,GLSizei width, GLSizei height,GLsizei depth, GLenum format, GLenum type,const  GLvoid * data)

1D,2D,3D的区别只在于3D比1D多了depth.height,2D只比1D多了height

6、插入替换纹理

void glCopyTexSubImage1D(GLenum target, GLint level, GLint xoffset,GLsizei width,GLsizei height)

void glCopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yOffset,GLsizei width,GLsizei height)

void glCopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yOffset,GLint zOffset,GLsizei width,GLsizei height)

1D,2D,3D的区别只在于1D只有xOffset,2D有xOffset yOffset,3D比2D多了zOffset

7、使用颜色缓冲区加载数据,形成新的纹理使用

void glCopyTexImage1D(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y,GLsizei width, GLint border)

void glCopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y,GLsizei width, GLsizei height, GLint border)

x,y在颜色缓冲区中指定了开始读取纹理数据的位置:缓冲区里面的数据是源缓冲区通过glReadBuffer设置的

注意:不存在glCopyTexImage3D,因为我们无法从2D颜色缓冲区中获取体积数据

8、使用函数分配纹理对象(重要)

指定纹理对象的数量和指针,(指针指向一个无符号整形数据,由纹理对象标识符填充)

void glGenTexTures(GLsizei n,GLuint * textures);

9、绑定纹理状态(重要)

void glBindTexture(GLenum target, GLunit texture);

参数1:target:GL_TEXTURE_2D,GL_TEXTURE_1D, GL_TEXTURE_3D

参数2:需要绑定的纹理对象

10、删除绑定纹理对象(重要)

void glDeleteTexture(GLsizei n, GLuint * texture);

纹理对象以及纹理对象指针(指针指向一个无符号整形数组,由纹理对象标识符填充)

11、测试纹理对象是否有效

如果texture是一个已经分配空间的纹理对象,那么这个函数会返回GL_TURE,否则会返回GL_FALST

GLboolean glIsTexture(GLuint texture);

12、设置纹理参数

glTextureParameter(GLenum target, GLenum pname, GLFloat param);

glTextureParameter(GLenum target, GLenum pname, GLint param);

glTextureParameter(GLenum target, GLenum pname, GLFloat * param);

glTextureParameter(GLenum target, GLenum pname, GLint *param)

参数1:target,指定这些参数应用哪个纹理模式上,比如GL_TEXTURE_2D,GL_TEXTURE_1D, GL_TEXTURE_3D

参数2:pname,指定需要设置哪个纹理参数

参数3:设定特定的纹理参数的值

13、设置过滤方式(也叫取样)(重点)

1、邻近过滤 GL_NEAREST

邻近过滤是最简单最快速的过滤方法,它总是把最邻近的纹理单元取到纹理坐标中。

图3、邻近过滤

2、线性过滤GL_LINEAR

线性过滤会把这个纹理坐标周围的纹理单元加权平均值应用到这个纹理坐标中。周围坐标的纹理单元距离越近,则权值越大

图4、线性过滤

一般建议纹理缩小用邻近过滤,纹理放大用线性过滤,当然你也可以随意搭配

glTexParameter(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)

glTexParameter(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)

14、设置环绕方式

glTextParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAR_S, GL_CLAMP_TO_EDGE);

glTextParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAR_T, GL_CLAMP_TO_EDGE)

参数1: GL_TEXTURE_1D、GL_TEXTURE_2D、GL_TEXTURE_3D

参数2:GL_TEXTURE_WRAP_S、GL_TEXTURE_T、GL_TEXTURE_R,针对s,t,r坐标

S 、T、 R 坐标系对应着世界坐标系的X, Y, Z

参数3:GL_REPEAT、GL_CLAMP、GL_CLAMP_TO_EDGE、GL_CLAMP_TO_BORDER

GL_REPEAT:OpenGL 在纹理坐标超过1.0的⽅向上对纹理进⾏重复;

GL_CLAMP:所需的纹理单元取⾃纹理边界或TEXTURE_BORDER_COLOR.

GL_CLAMP_TO_EDGE环绕模式强制对范围之外的纹理坐标沿着合法的纹理单元的最后一行或者最后⼀列来进行采样。

GL_CLAMP_TO_BORDER:在纹理坐标在0.0到1.0范围之外的只使⽤边界纹理单元。边界纹理单元是作为围绕基本图像的额外的⾏和列列,并与基本纹理图像⼀起加载的

图5、环绕方式
图6、环绕方式图示

三、金字塔绘制及纹理填充



图7,金字塔模型是由6个三角形组成的(侧面4个三角形,底部两个三角形拼接)


图8金字塔实现并填充纹理思维导图(图像转载自简书用户CC老师_HelloCoder)

1、主函数中设置环境及注册函数

intmain(intargc,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));

        return1;

    }

    SetupRC();

    glutMainLoop();

    ShutdownRC();//重要,纹理绑定后需要进行解绑

    return 0;

}

2、定义全局变量

GLShaderManager shaderManager;//着色器程序

GLMatrixStack modelViewMatrix;//模型视图矩阵

GLMatrixStack projectionMatrix;

GLFrame cameraFrame;//观察者坐标

GLFrame            objectFrame;//对象坐标

GLFrustum viewFrustum;//

GLBatch            pyramidBatch;//金字塔

GLuint              textureID;//纹理变量,一般使用无符号整型

GLGeometryTransform transformPipeline;

M3DMatrix44f shadowMatrix;

3、开始设置环境

voidSetupRC()

{

    //1.初始化着色器,设置清屏颜色

    glClearColor(0.7f, 0.7f, 0.7f, 1.0f );

    shaderManager.InitializeStockShaders();

    //2.开启深度测试

    glEnable(GL_DEPTH_TEST);

    //3.分配纹理对象

    // 参数1:纹理对象个数,金字塔的5个面,共6个三角形,但是用的是同一个纹理,所以纹理对象个数为1。

//参数2:纹理对象指针,之前定义过了纹理对象,直接拿下来取地址就行了

    glGenTextures(1, &textureID);

    //4、绑定纹理状态 

   //参数1:纹理状态2D 参数2:纹理对象

    glBindTexture(GL_TEXTURE_2D, textureID);

    //将TGA文件加载为2D纹理。

    //参数1:纹理文件名称

    //参数2&参数3:需要缩小&放大的过滤器

    //参数4:纹理坐标环绕模式

    LoadTGATexture("stone.tga", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR, GL_CLAMP_TO_EDGE);

    //5.创造金字塔pyramidBatch

    MakePyramid(pyramidBatch);

    //6.调整观察者位置

    /**相机frame MoveForward(平移)

    参数1:Z,深度(屏幕到图形的Z轴距离)

     */

    cameraFrame.MoveForward(-10);

}

4、渲染场景

步骤一:设置场景

voidRenderScene(void)

{

    //1.颜色值&光源位置

    staticGLfloatvLightPos [] = {1.0f,1.0f,0.0f};

    staticGLfloatvWhite [] = {1.0f,1.0f,1.0f,1.0f};


    //2.清理缓冲区

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);


    //3.当前模型视图压栈

    modelViewMatrix.PushMatrix();


    //添加照相机矩阵

    M3DMatrix44fmCamera;

    //从camraFrame中获取一个4*4的矩阵

    cameraFrame.GetCameraMatrix(mCamera);

    //矩阵乘以矩阵堆栈顶部矩阵,相乘结果存储到堆栈的顶部 将照相机矩阵 与 当前模型矩阵相乘 压入栈顶

    modelViewMatrix.MultMatrix(mCamera);


    //创建mObjectFrame矩阵

    M3DMatrix44fmObjectFrame;

    //从objectFrame中获取矩阵,objectFrame保存的是特殊键位的变换矩阵

    objectFrame.GetMatrix(mObjectFrame);

    //矩阵乘以矩阵堆栈顶部矩阵,相乘结果存储到堆栈的顶部 将世界变换矩阵 与 当前模型矩阵相乘 压入栈顶

    modelViewMatrix.MultMatrix(mObjectFrame);


    //4.绑定纹理,因为我们的项目中只有一个纹理。如果有多个纹理。绑定纹理很重要

    glBindTexture(GL_TEXTURE_2D, textureID);


    /*5.点光源着色器

     参数1:GLT_SHADER_TEXTURE_POINT_LIGHT_DIFF(着色器标签)

     参数2:模型视图矩阵

     参数3:投影矩阵

     参数4:视点坐标系中的光源位置

     参数5:基本漫反射颜色

     参数6:图形颜色(用纹理就不需要设置颜色。设置为0)

     */

    shaderManager.UseStockShader(GLT_SHADER_TEXTURE_POINT_LIGHT_DIFF,

                                 transformPipeline.GetModelViewMatrix(),

                                 transformPipeline.GetProjectionMatrix(),

                                 vLightPos, vWhite,0);


    //pyramidBatch 绘制

    pyramidBatch.Draw();


    //模型视图出栈,恢复矩阵(push一次就要pop一次,push和pop成对存在)

    modelViewMatrix.PopMatrix();


    //6.交换缓存区

    glutSwapBuffers();

}

步骤二:绘制金字塔

voidMakePyramid(GLBatch& pyramidBatch)

{

    /*1、通过pyramidBatch组建三角形批次

      参数1:类型

      参数2:顶点数

      参数3:这个批次中将会应用1个纹理

      注意:如果不写这个参数,默认为0。

     */

    pyramidBatch.Begin(GL_TRIANGLES,18,1);


    /***前情导入


     1)设置法线

     void Normal3f(GLfloat x, GLfloat y, GLfloat z);

     Normal3f:添加一个表面法线(法线坐标 与 Vertex顶点坐标中的Y轴一致)

     表面法线是有方向的向量,代表表面或者顶点面对的方向(相反的方向)。在多数的关照模式下是必须使用。后面的课程会详细来讲法线的应用


     pyramidBatch.Normal3f(X,Y,Z);


     2)设置纹理坐标

     void MultiTexCoord2f(GLuint texture, GLclampf s, GLclampf t);

     参数1:texture,纹理层次,对于使用存储着色器来进行渲染,设置为0

     参数2:s:对应顶点坐标中的x坐标

     参数3:t:对应顶点坐标中的y

     (s,t,r,q对应顶点坐标的x,y,z,w)


     pyramidBatch.MultiTexCoord2f(0,s,t);


     3)void Vertex3f(GLfloat x, GLfloat y, GLfloat z);

      void Vertex3fv(M3DVector3f vVertex);

     向三角形批次类添加顶点数据(x,y,z);

      pyramidBatch.Vertex3f(-1.0f, -1.0f, -1.0f);



     4)获取从三点找到一个法线坐标(三点确定一个面)

     void m3dFindNormal(result,point1, point2,point3);

     参数1:结果

     参数2-4:3个顶点数据

     */


    //塔顶

    M3DVector3fvApex = {0.0f,1.0f,0.0f};

    M3DVector3fvFrontLeft = { -1.0f, -1.0f,1.0f};

    M3DVector3fvFrontRight = {1.0f, -1.0f,1.0f};

    M3DVector3fvBackLeft = { -1.0f,  -1.0f, -1.0f};

    M3DVector3fvBackRight = {1.0f,  -1.0f, -1.0f};

    M3DVector3f n;


    //金字塔底部

    //底部的四边形 = 三角形X + 三角形Y

    //三角形X = (vBackLeft,vBackRight,vFrontRight)


    //1.找到三角形X 法线

    m3dFindNormal(n, vBackLeft, vBackRight, vFrontRight);


    //vBackLeft

    pyramidBatch.Normal3fv(n);

    pyramidBatch.MultiTexCoord2f(0,0.0f,0.0f);

    pyramidBatch.Vertex3fv(vBackLeft);


    //vBackRight

    pyramidBatch.Normal3fv(n);

    pyramidBatch.MultiTexCoord2f(0,1.0f,0.0f);

    pyramidBatch.Vertex3fv(vBackRight);


    //vFrontRight

    pyramidBatch.Normal3fv(n);

    pyramidBatch.MultiTexCoord2f(0,1.0f,1.0f);

    pyramidBatch.Vertex3fv(vFrontRight);



    //三角形Y =(vFrontLeft,vBackLeft,vFrontRight)


    //1.找到三角形X 法线

    m3dFindNormal(n, vFrontLeft, vBackLeft, vFrontRight);


    //vFrontLeft

    pyramidBatch.Normal3fv(n);

    pyramidBatch.MultiTexCoord2f(0,0.0f,1.0f);

    pyramidBatch.Vertex3fv(vFrontLeft);


    //vBackLeft

    pyramidBatch.Normal3fv(n);

    pyramidBatch.MultiTexCoord2f(0,0.0f,0.0f);

    pyramidBatch.Vertex3fv(vBackLeft);


    //vFrontRight

    pyramidBatch.Normal3fv(n);

    pyramidBatch.MultiTexCoord2f(0,1.0f,1.0f);

    pyramidBatch.Vertex3fv(vFrontRight);


    // 金字塔前面

    //三角形:(Apex,vFrontLeft,vFrontRight)

    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);


    //金字塔左边

    //三角形:(vApex, vBackLeft, vFrontLeft)

    m3dFindNormal(n, vApex, vBackLeft, vFrontLeft);

    pyramidBatch.Normal3fv(n);

    pyramidBatch.MultiTexCoord2f(0,0.5f,1.0f);

    pyramidBatch.Vertex3fv(vApex);


    pyramidBatch.Normal3fv(n);

    pyramidBatch.MultiTexCoord2f(0,1.0f,0.0f);

    pyramidBatch.Vertex3fv(vBackLeft);


    pyramidBatch.Normal3fv(n);

    pyramidBatch.MultiTexCoord2f(0,0.0f,0.0f);

    pyramidBatch.Vertex3fv(vFrontLeft);


    //金字塔右边

    //三角形:(vApex, vFrontRight, vBackRight)

    m3dFindNormal(n, vApex, vFrontRight, vBackRight);

    pyramidBatch.Normal3fv(n);

    pyramidBatch.MultiTexCoord2f(0,0.5f,1.0f);

    pyramidBatch.Vertex3fv(vApex);


    pyramidBatch.Normal3fv(n);

    pyramidBatch.MultiTexCoord2f(0,1.0f,0.0f);

    pyramidBatch.Vertex3fv(vFrontRight);


    pyramidBatch.Normal3fv(n);

    pyramidBatch.MultiTexCoord2f(0,0.0f,0.0f);

    pyramidBatch.Vertex3fv(vBackRight);


    //金字塔后边

    //三角形:(vApex, vBackRight, vBackLeft)

    m3dFindNormal(n, vApex, vBackRight, vBackLeft);

    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(vBackRight);


    pyramidBatch.Normal3fv(n);

    pyramidBatch.MultiTexCoord2f(0,1.0f,0.0f);

    pyramidBatch.Vertex3fv(vBackLeft);


    //结束批次设置

    pyramidBatch.End();

}

步骤三:自定义函数 ,从TGA文件加载2D纹理。

boolLoadTGATexture(constchar*szFileName,GLenumminFilter,GLenummagFilter,GLenumwrapMode)

{

    GLbyte*pBits;

    intnWidth, nHeight, nComponents;

    GLenumeFormat;


    //1、读纹理位,读取像素

    //参数1:纹理文件名称

    //参数2:文件宽度地址

    //参数3:文件高度地址

    //参数4:文件组件地址

    //参数5:文件格式地址

    //返回值:pBits,指向图像数据的指针


    pBits =gltReadTGABits(szFileName, &nWidth, &nHeight, &nComponents, &eFormat);

    if(pBits ==NULL)

        return false;


    //2、设置纹理参数

    //参数1:纹理维度

    //参数2:为S/T坐标设置模式

    //参数3:wrapMode,环绕模式

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapMode);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapMode);


    //参数1:纹理维度

    //参数2:线性过滤

    //参数3:wrapMode,环绕模式

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter);


    //3.载入纹理

    //参数1:纹理维度

    //参数2:mip贴图层次

    //参数3:纹理单元存储的颜色成分(从读取像素图是获得)

    //参数4:加载纹理宽

    //参数5:加载纹理高

    //参数6:加载纹理的深度

    //参数7:像素数据的数据类型(GL_UNSIGNED_BYTE,每个颜色分量都是一个8位无符号整数)

    //参数8:指向纹理图像数据的指针

    glTexImage2D(GL_TEXTURE_2D,0, nComponents, nWidth, nHeight,0,

                 eFormat,GL_UNSIGNED_BYTE, pBits);

    //使用完毕释放pBits

    free(pBits);


    //4.加载Mip,纹理生成所有的Mip层

    //参数:GL_TEXTURE_1D、GL_TEXTURE_2D、GL_TEXTURE_3D

    glGenerateMipmap(GL_TEXTURE_2D);


    return true;

}

5、窗口改变监听

voidChangeSize(intw,inth)

{

    //1.设置视口

    glViewport(0,0, w, h);

    //2.创建投影矩阵

    viewFrustum.SetPerspective(35.0f, float(w) / float(h), 1.0f, 500.0f);

    //viewFrustum.GetProjectionMatrix()  获取viewFrustum投影矩阵

    //并将其加载到投影矩阵堆栈上

    projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());


    //3.设置变换管道以使用两个矩阵堆栈(变换矩阵modelViewMatrix ,投影矩阵projectionMatrix)

    //初始化GLGeometryTransform 的实例transformPipeline.通过将它的内部指针设置为模型视图矩阵堆栈 和 投影矩阵堆栈实例,来完成初始化

    //当然这个操作也可以在SetupRC 函数中完成,但是在窗口大小改变时或者窗口创建时设置它们并没有坏处。而且这样可以一次性完成矩阵和管线的设置。

    transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);

}

6、特殊键位操作

voidSpecialKeys(intkey,intx,inty)

{

    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();//视口改变,更新窗口

}

7、最后需要清除绑定的纹理

voidShutdownRC(void)

{

    glDeleteTextures(1, &textureID);

}

8、最终效果图


图8、最终效果图


图9,最终效果图


[溪浣双鲤的技术摸爬滚打之路](https://www.jianshu.com/p/3fbecd65faae)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,784评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,745评论 2 378
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,702评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,229评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,245评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,376评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,798评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,471评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,655评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,485评论 2 318
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,535评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,235评论 3 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,793评论 3 304
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,863评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,096评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,654评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,233评论 2 341

推荐阅读更多精彩内容

  • 一、纹理基础 3D图形渲染中最基本的操作就是对一个表面应用纹理。纹理可以表现只从网格的几何形状无法得到的附加细节。...
    cain_huang阅读 8,635评论 0 7
  • 1 纹理基础 纹理是一种结构化的存储形式(Textures are a structured form of st...
    RichardJieChen阅读 15,614评论 0 9
  • 一、纹理综述 物理世界中,视域内的颜色会发生快速的变化。你可以看到很多物体表面都会呈现出丰富的颜色,并且在狭小的面...
    凡几多阅读 1,020评论 1 5
  • OpenGL: 纹理 1. 原始图像数据 像素包装图像存储空间 = 图像width * 图像height * 每个...
    Neal_f阅读 1,278评论 0 2
  • http://blog.csdn.net/wangdingqiaoit/article/details/51457...
    jerryhigh阅读 5,281评论 0 8