实现金字塔纹理

在一个金字塔图形贴上纹理,示例程序代码如下:

#include "GLTools.h"
#include "GLShaderManager.h"
#include "GLMatrixStack.h"
#include "GLFrustum.h"
#include "GLGeometryTransform.h"

//在Mac 系统下,`#include<glut/glut.h>` 在Windows 和 Linux上,我们使⽤freeglut的静态库版本并且需要添加⼀个宏
#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#include <GL/glut.h>
#endif


GLShaderManager shaderManager; //着色器管理器
GLMatrixStack modelViewMatrix; //模型视图矩阵
GLMatrixStack projectionMatrix; //投影矩阵
GLFrustum viewFrustum; //视景体
GLGeometryTransform transformPipeline; //几何图形变换管道
GLFrame cameraFrame; //照相机角色帧
GLFrame objectFrame;

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

GLBatch pyramidBatch;



//ChangeSize 函数:⾃定义函数.通过glutReshaperFunc(函数名)注册为重塑函数.当屏幕⼤⼩发⽣变化/或者第⼀次创建窗⼝时,会调⽤该函数调整窗⼝⼤⼩/视⼝⼤⼩.
void ChangeSize(int w ,int h)
{
    //设置视口窗口尺寸
    glViewport(0, 0, w, h);
    
    //创建投影矩阵,
    viewFrustum.SetPerspective(35.0f, float(w)/float(h), 1.0f, 100.0f);
    //加载到投影矩阵堆栈上
    projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
    
    //设置变换管道以使用两个矩阵堆栈
    transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
}


//将TGA文件加载为2D纹理。
bool LoadTGATexture(const char *szFileName, GLenum minFilter, GLenum magFilter, GLenum wrapMode)
{
    GLbyte * pBits;
    int nWidth,nHeight,nComponents;
    GLenum eFormat;
    
    //读纹理位,读取像素
    //参数1:纹理文件名称
    //参数2:文件宽度地址
    //参数3:文件高度地址
    //参数4:文件组件地址
    //参数5:文件格式地址
    //返回值:pBits,指向图像数据的指针
    pBits = gltReadTGABits(szFileName, &nWidth, &nHeight, &nComponents, &eFormat);
    if (pBits == NULL)
    {
        return false;
    }
    
    //设置纹理参数
    //参数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:过滤方式
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter);
    
    //载入纹理
    //参数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);
    
    //加载Mip,纹理生成所有的Mip层
    //参数:GL_TEXTURE_1D、GL_TEXTURE_2D、GL_TEXTURE_3D
    glGenerateMipmap(GL_TEXTURE_2D);
    
    return true;
}


//绘制金字塔
void MakePyramid(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个顶点数据
    */
    
    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;
    
    //金字塔底部
    //底部的四边形 = 三角形X + 三角形Y
    //三角形X = (vBackLeft,vBackRight,vFrontRight)
    
    //找到三角形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)
    
    //找到三角形Y法线
    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);
    
    //Apex
    pyramidBatch.Normal3fv(n);
    pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.0f);
    pyramidBatch.Vertex3fv(vApex);
    
    //vFrontLeft
    pyramidBatch.Normal3fv(n);
    pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
    pyramidBatch.Vertex3fv(vFrontLeft);
    
    //vFrontRight
    pyramidBatch.Normal3fv(n);
    pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
    pyramidBatch.Vertex3fv(vFrontRight);
    
    
    //金字塔左边
    //三角形:(vApex, vBackLeft, vFrontLeft)
    m3dFindNormal(n, vApex, vBackLeft, vFrontLeft);
    
    //vApex
    pyramidBatch.Normal3fv(n);
    pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.0f);
    pyramidBatch.Vertex3fv(vApex);
    
    //vBackLeft
    pyramidBatch.Normal3fv(n);
    pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
    pyramidBatch.Vertex3fv(vBackLeft);
    
    //vFrontLeft
    pyramidBatch.Normal3fv(n);
    pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
    pyramidBatch.Vertex3fv(vFrontLeft);
    
    
    //金字塔右边
    //三角形:(vApex, vFrontRight, vBackRight)
    m3dFindNormal(n, vApex, vFrontRight, vBackRight);
    
    //vApex
    pyramidBatch.Normal3fv(n);
    pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.0f);
    pyramidBatch.Vertex3fv(vApex);
    
    //vFrontRight
    pyramidBatch.Normal3fv(n);
    pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
    pyramidBatch.Vertex3fv(vFrontRight);
    
    //vBackRight
    pyramidBatch.Normal3fv(n);
    pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
    pyramidBatch.Vertex3fv(vBackRight);
    
    
    //金字塔后边
    //三角形:(vApex, vBackRight, vBackLeft)
    m3dFindNormal(n, vApex, vBackRight, vBackLeft);
    
    //vApex
    pyramidBatch.Normal3fv(n);
    pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.0f);
    pyramidBatch.Vertex3fv(vApex);
    
    //vBackRight
    pyramidBatch.Normal3fv(n);
    pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
    pyramidBatch.Vertex3fv(vBackRight);
    
    //vBackLeft
    pyramidBatch.Normal3fv(n);
    pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
    pyramidBatch.Vertex3fv(vBackLeft);
    
    
    //结束批次设置
    pyramidBatch.End();
}


void SetupRC()
{
    glClearColor(0.7f, 0.7f, 0.7f, 1.0f );
    shaderManager.InitializeStockShaders();
    
    //开启深度测试
    glEnable(GL_DEPTH_TEST);
    
    //分配纹理对象 参数1:纹理对象个数,参数2:纹理对象指针
    glGenTextures(1, &textureID);
    //绑定纹理状态 参数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);
    
    //创造金字塔pyramidBatch
    MakePyramid(pyramidBatch);
    
    /*
     相机frame MoveForward(平移)
     参数1:Z,深度(屏幕到图形的Z轴距离)
     */
    cameraFrame.MoveForward(-10);
    
}

//删除纹理对象
void ShutdownRC(void)
{
    glDeleteTextures(1, &textureID);
}

//上下左右键位控制移动
void SpecialKeys(int key, int x, int y)
{
    
    //旋转度数
    float angular = float(m3dDegToRad(5.0f));
    

    if (key == GLUT_KEY_UP)
    {
        objectFrame.RotateWorld(-angular, 1.0f, 0.0f, 0.0f);
    }
    
    if (key == GLUT_KEY_DOWN)
    {
        objectFrame.RotateWorld(angular, 1.0f, 0.0f, 0.0f);
    }
    
    if (key == GLUT_KEY_LEFT)
    {
        objectFrame.RotateWorld(-angular, 0.0f, 1.0f, 0.0f);
    }
    
    if (key == GLUT_KEY_RIGHT)
    {
        objectFrame.RotateWorld(angular, 0.0f, 1.0f, 0.0f);
    }
    
    glutPostRedisplay();
}

//RenderScene 函数:⾃定义函数.通过glutDisplayFunc(函数名)注册为显示渲染函数.当屏幕发⽣变化/或者开发者主动渲染会调⽤此函数,⽤来实现数据->渲染过程
void RenderScene(void)
{
    
    //1.颜色值&光源位置
    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;
    //从camraFrame中获取一个4*4的矩阵
    cameraFrame.GetCameraMatrix(mCamera);
    //矩阵乘以矩阵堆栈顶部矩阵,相乘结果存储到堆栈的顶部 将照相机矩阵 与 当前模型矩阵相乘 压入栈顶
    modelViewMatrix.MultMatrix(mCamera);
    
    //创建mObjectFrame矩阵
    M3DMatrix44f mObjectFrame;
    //从objectFrame中获取矩阵,objectFrame保存的是特殊键位的变换矩阵
    objectFrame.GetMatrix(mObjectFrame);
    //矩阵乘以矩阵堆栈顶部矩阵,相乘结果存储到堆栈的顶部 将世界变换矩阵 与 当前模型矩阵相乘 压入栈顶
    modelViewMatrix.MultMatrix(mObjectFrame);
  
    //绑定纹理
    glBindTexture(GL_TEXTURE_2D, textureID);


    /*
     点光源着色器
     参数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();
    
    //模型视图出栈
    modelViewMatrix.PopMatrix();
    
    //交换缓存区
    glutSwapBuffers();
    
    
}


//main 函数: 程序⼊⼝.OpenGL 是⾯向过程编程.所以你会发现利⽤OpenGL处理图形/图像都是链式形式.以及基于OpenGL封装的图像处理框架也是链式编程
int main(int argc, char* argv[])
{
    gltSetWorkingDirectory(argv[0]);
    glutInit(&argc, argv);
    //申请一个颜色缓存区、双缓存区、深度缓存区、模板缓存区
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
    //设置窗口的尺寸
    glutInitWindowSize(800, 800);
    //设置窗口的名称
    glutCreateWindow("金字塔纹理贴图");
    //注册回调函数(改变尺寸)
    glutReshapeFunc(ChangeSize);
    //注册显示函数
    glutDisplayFunc(RenderScene);
    
    glutSpecialFunc(SpecialKeys);
    
    GLenum err = glewInit();
    if (GLEW_OK != err)
    {
        fprintf(stderr, "Error: %s\n", glewGetErrorString(err));
        return 1;
    }
    
    SetupRC();
   
    //runloop运行循环
    glutMainLoop();
    
    ShutdownRC();
    
    return 0;
}

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