OpenGL初探

第十八节—纹理金字塔

2020-09-03  本文已影响0人  L_Ares

本文为L_Ares个人写作,包括图片皆为个人亲自操作,以任何形式转载请表明原文出处。

首先介绍几个函数,下面要用到的。

1. 根据三个点寻找法线:

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

参数:

(1).result:找到的法线结果。
(2).(3).(4).point:用来找法线的三个点。这三个点是要有顺序的,正面是逆时针排列,背面是顺时针排列。

2. 设置法线:

void Normal3fv(M3DVector3f vNormal);

参数:

vNormal:法线坐标数组

3. 设置纹理坐标:

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

参数:

(1).texture:纹理的mimap涂层数,也就是你要设置哪个纹理的纹理坐标,填入纹理的顺序数,我们这节只有一个纹理,所以这个纹理就是第1个,所以写0。

(2).s: 纹理的横坐标对应的是多少。

(2).t:纹理的纵坐标对应的是多少。

4. 设置顶点坐标:

void Vertex3fv(M3DVector3f vVertex);

参数:

vVertex:顶点坐标数组

代码如下:


//
//  main.cpp
//  09纹理金字塔
//
//  Created by EasonLi on 2020/9/2.
//  Copyright © 2020 EasonLi. All rights reserved.
//

#include <stdio.h>

#pragma mark - 引用类

//工具类GLTools,GLTool中大多数类似于C语言的工具类都在其中
#include "GLTools.h"
//着色器管理器
#include "GLShaderManager.h"
//参考帧
#include "GLFrame.h"
//矩阵投影工具类
#include "GLFrustum.h"
//容器类。三角形批次类
#include "GLBatch.h"
//矩阵堆栈类。
#include "GLMatrixStack.h"
//管道类。管理mvp矩阵的
#include "GLGeometryTransform.h"
//3D数学类
#include "math3d.h"

//根据系统引入GLUT
#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#include <GL/glut.h>
#endif

#pragma mark - 公共变量

//着色器管理器
GLShaderManager shaderManager;
//三角形批次类
GLBatch pyramidBatch;
//观察者参考帧
GLFrame cameraFrame;
//物体参考帧
GLFrame objFrame;
//模型视图矩阵堆栈
GLMatrixStack mvMatrixStack;
//投影矩阵堆栈
GLMatrixStack projMatrixStack;
//矩阵投影工具对象
GLFrustum viewFrustum;
//变换管道
GLGeometryTransform transformPipeline;
//纹理变量,一般用无符号整型
GLuint textureID;


#pragma mark - 函数
//初建或重塑窗口
void ChangeSize (int w , int h)
{
    
    //设置视口大小
    if (h == 0) {
        h = 1;
    }
    glViewport(0, 0, w, h);
    
    //设置投影方式并创建投影矩阵
    viewFrustum.SetPerspective(36.f, float(w)/float(h), 1.f, 500.f);
    
    //将投影视图矩阵放入投影矩阵堆栈
    projMatrixStack.LoadMatrix(viewFrustum.GetProjectionMatrix());
    
    //设置变换管道
    transformPipeline.SetMatrixStacks(mvMatrixStack, projMatrixStack);
    
}

/**
 将TGA文件加载成2D纹理
 里面包括了读取、加载、设置纹理
 参数
 (1)fileName:要加载的TGA文件的文件名
 (2)minFilter:缩小过滤模式
 (3)magFilter:方大过滤模式
 (4)wrapMode:环绕方式
 */
bool LoadTGATexture(const char *fileName , GLenum minFilter , GLenum magFilter , GLenum wrapMode)
{
    
    //先定义一下要用到的变量,用来存储读取纹理之后拿到的数据信息
    //因为读取纹理tga文件的函数返回的是字节类型
    GLbyte *mByte;
    //定义获取到的宽、高、组成和颜色成分变量
    int nWidth,nHeight,nComponent;
    GLenum nFormat;
    
    //1.读取纹理。将TGA文件读取成像素
    //参数
    //(1) 要读取的tga的文件名,必要时可以添加路径进去
    //注:从这里开始,后面的参数都是要的指针,所以给的是地址
    //(2) tga文件的宽度
    //(3) tga文件的高度
    //(4) tga文件的颜色成分组成
    //(5) tga文件的格式
    mByte = gltReadTGABits(fileName, &nWidth, &nHeight, &nComponent, &nFormat);
    if (mByte == NULL) {
        return false;
    }
    
    //2.设置纹理
    //设置纹理的环绕方式
    //横向s轴的环绕方式
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapMode);
    //纵向t轴的环绕方式
    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);
    
    
    //3.载入纹理
    //参数
    //(1)纹理维度
    //(2)mip贴图的层次
    //(3)每个纹理单元中存储多少的颜色成分
    //(4)加载纹理的宽度
    //(5)加载纹理的高度
    //(6)允许为纹理添加一个边框,就是给一个边界的限度,一般设置成0就行了
    //(7)载入纹理要用的像素模式
    //(8)像素数据的数据类型
    //(9)指向纹理图像数据的指针。
    glTexImage2D(GL_TEXTURE_2D, 0, nComponent, nWidth, nHeight, 0, nFormat, GL_UNSIGNED_BYTE, mByte);
    
    //使用完成之后要释放
    free(mByte);
    
    //4.加载Mip纹理所生成的所有Mip层
    //参数选择:GL_TEXTURE_1D、GL_TEXTURE_2D、GL_TEXTURE_3D
    glGenerateMipmap(GL_TEXTURE_2D);
    
    return true;
    
}

//设置金字塔顶点及纹理数据等
void MakePyramid(GLBatch &pyramidBatch)
{
    //通过三角形批次类设置金字塔基本绘制信息
    //参数:
    //(1)绘制方式
    //(2)顶点数量,金字塔是6个三角形组成的,4个侧面每个都是1个三角形,底面矩形是2个三角形,所以是6个三角形18个顶点
    //(3)要用几个纹理。如果这个参数不设置的话,默认是0的,也就是不用纹理。
    pyramidBatch.Begin(GL_TRIANGLES, 18 , 1);
    
    //设置顶点坐标
    //金字塔顶点
    //Front是靠近观察者的顶点,Back是远离观察者的顶点
    M3DVector3f vApex = {0.f,1.f,0.f};
    M3DVector3f vFrontLeft = {-1.f,-1.f,1.f};
    M3DVector3f vFrontRight = {1.f,-1.f,1.f};
    M3DVector3f vBackLeft = {-1.f,-1.f,-1.f};
    M3DVector3f vBackRight = {1.f,-1.f,-1.f};
    
    //临时的法线变量
    M3DVector3f vNormal;
    
    
    
    //金字塔底部是两个三角形构成,分别命名三角形A和三角形B
/********************************************设置三角形A******************************************/
    //这里注意,法线在获取的时候,顶点的顺序是有方向性的,正面是逆时针顺序,背面是顺时针顺序,不然会出现黑色,没有纹理
    //顶点:vFrontLeft,vBackLeft,vBackRight
    //根据三个点,找到三角形A的法线,也可以自己算法线,无所谓的
    //参数:
    //(1)result:找到的法线。存储到刚才定义的临时法线变量里面
    //(2)(3)(4):用来找法线的三个点
    m3dFindNormal(vNormal, vFrontLeft, vBackLeft, vBackRight);
    
    //利用找到的三角形A的法线,立即设置三角形A的三个顶点的纹理
    //vBackLeft
    //设置法线
    pyramidBatch.Normal3fv(vNormal);
    //设置纹理坐标
    pyramidBatch.MultiTexCoord2f(0, 0.f, 0.f);
    //设置顶点坐标
    pyramidBatch.Vertex3fv(vFrontLeft);
    
    //vBackRight
    //设置法线
    pyramidBatch.Normal3fv(vNormal);
    //设置纹理坐标
    pyramidBatch.MultiTexCoord2f(0, 0.f, 1.f);
    //设置顶点坐标
    pyramidBatch.Vertex3fv(vBackLeft);
    
    //vFrontRight
    //设置法线
    pyramidBatch.Normal3fv(vNormal);
    //设置纹理坐标
    //参数:
    //(1)texture:第几个纹理!!!而不是把纹理放上去,是纹理的叠加的时候,这个纹理是第几个。要用哪个
    //(2)s:纹理的横坐标
    //(3)t:纹理的总坐标
    pyramidBatch.MultiTexCoord2f(0, 1.f, 1.f);
    //设置顶点坐标
    pyramidBatch.Vertex3fv(vBackRight);
    
    
    
/********************************************设置三角形B******************************************/
    //三角形B的三个点:vBackRight,vFrontRight,vFrontLeft
    //找到三角形B的法线
    m3dFindNormal(vNormal, vBackRight, vFrontRight, vFrontLeft);
    
    //设置三角形B的三个顶点纹理
    //vFrontLeft
    //设置法线
    pyramidBatch.Normal3fv(vNormal);
    //设置纹理坐标
    pyramidBatch.MultiTexCoord2f(0, 1.f, 1.f);
    //设置顶点坐标
    pyramidBatch.Vertex3fv(vBackRight);
    
    //vBackLeft
    //设置法线
    pyramidBatch.Normal3fv(vNormal);
    //设置纹理坐标
    pyramidBatch.MultiTexCoord2f(0, 1.f, 0.f);
    //设置顶点坐标
    pyramidBatch.Vertex3fv(vFrontRight);
    
    //vFrontRight
    //设置法线
    pyramidBatch.Normal3fv(vNormal);
    //设置纹理坐标
    pyramidBatch.MultiTexCoord2f(0, 0.f, 0.f);
    //设置顶点坐标
    pyramidBatch.Vertex3fv(vFrontLeft);
    
    
    
/********************************************设置正面三角形****************************************/
    //顶点:vApex,vFrontLeft,vFrontRight
    //找到正面三角形的法线
    m3dFindNormal(vNormal, vApex, vFrontLeft, vFrontRight);
   
    //设置正面三角形的顶点纹理
    //vApex
    //设置法线
    pyramidBatch.Normal3fv(vNormal);
    //设置纹理坐标
    pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.f);
    //设置顶点坐标
    pyramidBatch.Vertex3fv(vApex);
    
    //vFrontLeft
    //设置法线
    pyramidBatch.Normal3fv(vNormal);
    //设置纹理坐标
    pyramidBatch.MultiTexCoord2f(0, 0.f, 0.f);
    //设置顶点坐标
    pyramidBatch.Vertex3fv(vFrontLeft);
    
    //vFrontRight
    //设置法线
    pyramidBatch.Normal3fv(vNormal);
    //设置纹理坐标
    pyramidBatch.MultiTexCoord2f(0, 1.f, 0.f);
    //设置顶点坐标
    pyramidBatch.Vertex3fv(vFrontRight);


    
/********************************************设置左面三角形****************************************/
    //顶点:vApex,vFrontLeft,vBackLeft
    //找到左面三角形的法线
    m3dFindNormal(vNormal, vApex, vBackLeft, vFrontLeft);
    
    //设置左面三角形顶点纹理
    //vApex
    //设置法线
    pyramidBatch.Normal3fv(vNormal);
    //设置纹理坐标
    pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.f);
    //设置顶点坐标
    pyramidBatch.Vertex3fv(vApex);
    
    //vFrontLeft
    //设置法线
    pyramidBatch.Normal3fv(vNormal);
    //设置纹理坐标
    pyramidBatch.MultiTexCoord2f(0, 1.f, 0.f);
    //设置顶点坐标
    pyramidBatch.Vertex3fv(vFrontLeft);
    
    //vBackLeft
    //设置法线
    pyramidBatch.Normal3fv(vNormal);
    //设置纹理坐标
    pyramidBatch.MultiTexCoord2f(0, 0.f, 0.f);
    //设置顶点
    pyramidBatch.Vertex3fv(vBackLeft);
    
    
    
/********************************************设置右面三角形****************************************/
    //顶点:vApex,vFrontRight,vBackRight
    //找到右面三角形的法线
    m3dFindNormal(vNormal, vApex, vFrontRight, vBackRight);
    
    //设置右面三角形顶点纹理
    //vApex
    //设置法线
    pyramidBatch.Normal3fv(vNormal);
    //设置纹理坐标
    pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.f);
    //设置顶点坐标
    pyramidBatch.Vertex3fv(vApex);
    
    //vFrontRight
    //设置法线
    pyramidBatch.Normal3fv(vNormal);
    //设置纹理坐标
    pyramidBatch.MultiTexCoord2f(0, 1.f, 0.f);
    //设置顶点坐标
    pyramidBatch.Vertex3fv(vFrontRight);
    
    //vBackRight
    //设置法线
    pyramidBatch.Normal3fv(vNormal);
    //设置纹理坐标
    pyramidBatch.MultiTexCoord2f(0, 0.f, 0.f);
    //设置顶点坐标
    pyramidBatch.Vertex3fv(vBackRight);
    
    
    
/********************************************设置后面三角形****************************************/
    //顶点:vApex,vBackLeft,vBackRight
    //找到后面三角形的法线
    m3dFindNormal(vNormal, vApex, vBackRight, vBackLeft);
    //设置后面三角形顶点纹理
    //vApex
    //设置法线
    pyramidBatch.Normal3fv(vNormal);
    //设置纹理坐标
    pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.f);
    //设置顶点坐标
    pyramidBatch.Vertex3fv(vApex);

    //vBackLeft
    //设置法线
    pyramidBatch.Normal3fv(vNormal);
    //设置纹理坐标
    pyramidBatch.MultiTexCoord2f(0, 1.f, 0.f);
    //设置顶点坐标
    pyramidBatch.Vertex3fv(vBackLeft);

    //vBackRight
    //设置法线
    pyramidBatch.Normal3fv(vNormal);
    //设置纹理坐标
    pyramidBatch.MultiTexCoord2f(0, 0.f, 0.f);
    //设置顶点坐标
    pyramidBatch.Vertex3fv(vBackRight);
    
/***********************************************************************************************/
    
    //结束三角形批次类的设置
    pyramidBatch.End();
    
    
}

//设置渲染环境
void SetUpRC ()
{
    
    //设置清屏颜色
    glClearColor(0.3f, 0.3f, 0.3f, 1.f);
    
    //初始化着色器管理器
    shaderManager.InitializeStockShaders();
    
    //开启深度测试
    glEnable(GL_DEPTH_TEST);
    
    //观察者参考帧偏移一定的位置,不要和原点重合
    cameraFrame.MoveForward(-10.f);
    
    //分配纹理对象
    //参数:(1)要使用几个纹理 (2)纹理对象的指针
    glGenTextures(1, &textureID);
    
    //绑定纹理状态
    //参数:(1)纹理的状态 (2)纹理对象
    glBindTexture(GL_TEXTURE_2D, textureID);
    
    //读取、加载、设置纹理参数
    //这是一个自己写的函数,主要是把这三步合成一步
    //就是将TGA文件加载成2D纹理
    LoadTGATexture("stone.tga", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR, GL_CLAMP_TO_EDGE);
    
    //设置金字塔顶点及纹理数据等
    MakePyramid(pyramidBatch);
    
}

//清理
void ShutDownRC ()
{
    
    //
    glDeleteTextures(1, &textureID);
    
}

//渲染
void RenderScene ()
{
    
    //清理缓冲区
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    
    //光源位置
    static GLfloat vLightPos[] = {1.f,1.f,0.f};
    
    //颜色
    static GLfloat vColor[] = {1.f,1.f,1.f,1.f};
    
    //压栈
    mvMatrixStack.PushMatrix();
    
    //获取观察者矩阵
    M3DMatrix44f mCamera;
    cameraFrame.GetCameraMatrix(mCamera);
    mvMatrixStack.MultMatrix(mCamera);
    
    //获取物体矩阵
    M3DMatrix44f mObj;
    objFrame.GetMatrix(mObj);
    mvMatrixStack.MultMatrix(mObj);
    
    //绑定纹理。因为这里我们只有一个纹理,是可以不用再绑定的,但是多个纹理的时候,绑定一下保证纹理不错
    glBindTexture(GL_TEXTURE_2D, textureID);
    
    //设置着色器
    shaderManager.UseStockShader(GLT_SHADER_TEXTURE_POINT_LIGHT_DIFF,transformPipeline.GetModelViewMatrix(),transformPipeline.GetProjectionMatrix(),vLightPos,vColor,0);
    
    //绘制
    pyramidBatch.Draw();
    
    //出栈
    mvMatrixStack.PopMatrix();
    
    //交换缓冲区
    glutSwapBuffers();
    
    
}

//特殊键位
void SpecialKeys (int key , int x ,int y)
{
    switch (key) {
            
        case GLUT_KEY_UP:
            objFrame.RotateWorld(m3dDegToRad(-5.0f), 1.0f, 0.0f, 0.0f);
            break;
            
        case GLUT_KEY_DOWN:
            objFrame.RotateWorld(m3dDegToRad(5.0f), 1.0f, 0.0f, 0.0f);
            break;
            
        case GLUT_KEY_RIGHT:
            objFrame.RotateWorld(m3dDegToRad(5.0f), 0.0f, 1.0f, 0.0f);
            break;
            
        case GLUT_KEY_LEFT:
            objFrame.RotateWorld(m3dDegToRad(-5.0f), 0.0f, 1.0f, 0.0f);
            break;
            
    }
    glutPostRedisplay();
    
}

#pragma mark - main

int main (int argc , char *argv[])
{
    
    //设置工作目录和项目目录一致在/Resource下,glut已经设置,手动安全
    gltSetWorkingDirectory(argv[0]);
    
    //初始化glut
    glutInit(&argc, argv);
    
    //初始化显示模式
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
    
    //设置窗口大小
    glutInitWindowSize(600, 600);
    
    //创建窗口并命名
    glutCreateWindow("纹理金字塔");
    
    //注册重塑函数
    glutReshapeFunc(ChangeSize);
    
    //注册渲染函数
    glutDisplayFunc(RenderScene);
    
    //注册特殊键位
    glutSpecialFunc(SpecialKeys);
    
    //初始化glew库
    GLenum status = glewInit();
    if (status != GLEW_OK) {
        printf("glew init error : %s \n",glewGetErrorString(status));
        return 1;
    }
    
    //设置渲染环境
    SetUpRC();
    
    //设置本地消息循环机制
    glutMainLoop();
    
    ShutDownRC();
    
    return 0;
    
}


执行结果如图1.1:

1.1.png
上一篇下一篇

猜你喜欢

热点阅读