绘制一个带纹理的金字塔模型,通过本案例来加深对纹理的理解,案例执行结果如下效果图:
Jietu20200819-160303.gif
搭建框架
#include "GLTools.h"
#include "GLShaderManager.h"
#include "GLFrustum.h"
#include "GLBatch.h"
#include "GLFrame.h"
#include "GLMatrixStack.h"
#include "GLGeometryTransform.h"
#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#include <GL/glut.h>
#endif
GLShaderManager shaderManager;
GLMatrixStack modelViewMatrix;
GLMatrixStack projectionMatrix;
GLFrame cameraFrame;
GLFrame objectFrame;
GLFrustum viewFrustum;
GLBatch pyramidBatch;
//纹理变量,一般使用无符号整型
GLuint textureID;
GLGeometryTransform transformPipeline;
M3DMatrix44f shadowMatrix;
void RenderScene(){
}
void SetupRC(){
}
void SpecialKeys(int key, int x, int y){
}
void ChangeSize(int w, int h){
glViewport(0, 0, w, h);
viewFrustum.SetPerspective(35, float(w)/float(h), 1, 100);
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
cameraFrame.MoveForward(-10.0f);
}
int main(int argc, char * argv[]) {
gltSetWorkingDirectory(argv[0]);
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DEPTH | GLUT_RGBA | GLUT_DOUBLE | GLUT_STENCIL);
glutInitWindowSize(800, 600);
glutCreateWindow("test");
glutDisplayFunc(RenderScene);
glutReshapeFunc(ChangeSize);
glutSpecialFunc(SpecialKeys);
GLenum err = glewInit();
if(GLEW_OK != err){
return 1;
}
SetupRC();
glutMainLoop();
ShutdownRC();
return 0;
}
SetupRC函数
在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文件加载纹理。又细分为以下几个步骤:
6.1 读取纹素
//参数1:纹理文件名称
//参数2:返回纹理宽度
//参数3:返回纹理高度
//参数4:返回纹理组件
//参数5:返回纹理格式
//返回值:pBits,指向图像数据的指针
int nWidth, nHeight, nComponents;
GLenum eFormat;
GLbyte* pBits = gltReadTGABits(szFileName, &nWidth, &nHeight, &nComponents, &eFormat);
6.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: 缩小/放大过滤方式.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter);
6.3 载入纹理
//参数1:纹理维度
//参数2:mip贴图层次
//参数3:纹理单元存储的颜色成分(从读取像素图是获得)
//参数4:加载纹理宽
//参数5:加载纹理高
//参数6:加载纹理的深度
//参数7:加载纹理的格式
//参数8:像素数据的数据类型(GL_UNSIGNED_BYTE,每个颜色分量都是一个8位无符号整数)
//参数9:指向纹理图像数据的指针
glTexImage2D(GL_TEXTURE_2D, 0, nComponents, nWidth, nHeight, 0, eFormat, GL_UNSIGNED_BYTE, pBits);
- 使用三角形批次类创建金字塔
由于金字塔需要附着纹理,所以需要在金字塔创建的时候,需要分别设置顶点坐标与纹理坐标。
在坐标系中绘制金字塔,坐标原点位置金字塔的中心。一个金字塔共有5个顶点,5个顶点组成6个三角形。5个顶点的坐标如下:
vApex(0.0, 1.0, 0.0)
vBackLeft(-1.0, -1.0, -1.0)
vBackRight(1.0. -1.0, -1.0)
vFrontRight(1.0, -1.0, 1.0)
vFrontLeft(-1.0, -1.0, 1.0)
image.png
金字塔纹理坐标如下:
image.png
- 设置纹理坐标使用三角形批次类的MultiTexCoord2f函数。
//参数1:图层,即纹理的level,一般为0
//参数2:纹理的坐标s,类似于顶点坐标x
//参数3:纹理的坐标t,类似于顶点坐标y
pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
- 设置顶点坐标使用三角形批次类的Vertex3fv函数。
//参数:顶点坐标x,y,z
pyramidBatch.Vertex3fv(vApex);
设置金字塔的前面、左面、右面、后面三角形的顶点坐标与纹理坐标:
// 金字塔前面
//三角形:(Apex,vFrontLeft,vFrontRight)
pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.0f);
pyramidBatch.Vertex3fv(vApex);
pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
pyramidBatch.Vertex3fv(vFrontLeft);
pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
pyramidBatch.Vertex3fv(vFrontRight);
//金字塔左边
//三角形:(vApex, vBackLeft, vFrontLeft)
pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.0f);
pyramidBatch.Vertex3fv(vApex);
pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
pyramidBatch.Vertex3fv(vBackLeft);
pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
pyramidBatch.Vertex3fv(vFrontLeft);
//金字塔右边
//三角形:(vApex, vFrontRight, vBackRight)
pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.0f);
pyramidBatch.Vertex3fv(vApex);
pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
pyramidBatch.Vertex3fv(vFrontRight);
pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
pyramidBatch.Vertex3fv(vBackRight);
//金字塔后边
//三角形:(vApex, vBackRight, vBackLeft)
pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.0f);
pyramidBatch.Vertex3fv(vApex);
pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
pyramidBatch.Vertex3fv(vBackRight);
pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
pyramidBatch.Vertex3fv(vBackLeft);
接下来就是设置金字塔底部三角形X、Y的顶点坐标和纹理坐标了。这里需要发挥一下空间想象。
在3D空间里面,我们有6个方向:上下左右前后,分别是观察者观察物体所在的方向,三角形X、Y处于金字塔底部,观察者应该从下面观察这个金字塔。
image.png
根据这个图,我们可以知道顶点坐标与纹理坐标的对应关系:
//三角形X = (vBackLeft,vBackRight,vFrontRight)
//vBackLeft
pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
pyramidBatch.Vertex3fv(vBackLeft);
//vBackRight
pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
pyramidBatch.Vertex3fv(vBackRight);
//vFrontRight
pyramidBatch.MultiTexCoord2f(0, 1.0f, 1.0f);
pyramidBatch.Vertex3fv(vFrontRight);
//三角形Y =(vFrontLeft,vBackLeft,vFrontRight)
//vFrontLeft
pyramidBatch.MultiTexCoord2f(0, 0.0f, 1.0f);
pyramidBatch.Vertex3fv(vFrontLeft);
//vBackLeft
pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
pyramidBatch.Vertex3fv(vBackLeft);
//vFrontRight
pyramidBatch.MultiTexCoord2f(0, 1.0f, 1.0f);
pyramidBatch.Vertex3fv(vFrontRight);
至此,SetupRC函数算是完成了。
RenderScene函数
在SetupRC函数里面已经得到了顶点坐标,也关联了对应的纹理坐标,那在RenderScene函数的操作就相对较简单。
- 清理缓冲区
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
- 模型视图矩阵压入堆栈
modelViewMatrix.PushMatrix();
- 由于在ChangeSize函数里面移动了相机的位置,此时应该要将模型视图矩阵堆栈的栈顶乘以观察者矩阵。
modelViewMatrix.PushMatrix();
M3DMatrix44f mCamera;
cameraFrame.GetCameraMatrix(mCamera);
modelViewMatrix.MultMatrix(mCamera);
- 在SpecialKeys函数里面将会对物体进行旋转,此时需要将模型视图矩阵堆栈的栈顶再乘以物体矩阵。
M3DMatrix44f mObject;
objectFrame.GetMatrix(mObject);
modelViewMatrix.MultMatrix(mObject);
- 重新绑定纹理,这个案例中只有一个纹理,此处若不重新绑定也不会出问题,出于严谨性,还是重新绑定一下。
glBindTexture(GL_TEXTURE_2D, textureID);
- 使用纹理替换矩阵着色器进行绘制
shaderManager.UseStockShader(GLT_SHADER_TEXTURE_REPLACE, transformPipeline.GetModelViewProjectionMatrix(), 0);
pyramidBatch.Draw();
- 绘制完成之后,模型视图矩阵出栈,恢复原样。
modelViewMatrix.PopMatrix();
- 交换缓冲区
glutSwapBuffers();
ShutdownRC函数
出于代码的完整性,在程序退出前删除纹理对象。
void ShutdownRC(void){
glDeleteTextures(1, &textureID);
}
至此,案例2就完成了。
总结本次案例重点:
- 加载纹理的流程。
- 顶点坐标与纹理坐标的关联。
网友评论