With the knowledge covered in previous articles, let’s implement a simple case
First, the effect drawing
Second, the analysis
The pyramid is made up of 4 triangles plus the bottom square, which is made up of X and Y triangles. So the model has five vertices: (in the center of an object as the origin coordinates) VBackLeft (1.0, 1.0, 1.0) VBackRight (1.0, 1.0, 1.0) vFrontLeft VFrontRight (1.0, 1.0, 1.0) (1.0, 1.0, 1.0) ,1.0 vApex (0, 0)
VBackLeft (0,0) VBackRight (1,0) vFrontLeft (0,1) VFrontRight (1,1) vApex (0.5,1)
3. Code logic flow chart
Four, the source code
#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;
//用来初始化纹理,将TGA文件加载为2D纹理。
bool LoadTGATexture(const char *szFileName, GLenum minFilter, GLenum magFilter, GLenum wrapMode)
{
//定义一个指针
GLbyte *pBits;
//定义文件的 宽、高、组件
int nWidth,nHeight,nComponents;
//定义文件格式
GLenum eFormat;
//1、读纹理的像素
/*
参数1:纹理文件名称
参数2:文件宽度地址
参数3:文件高度地址
参数4:文件组件地址
参数5:文件格式地址
返回值:pBits,指向图像数据的指针
*/
pBits = gltReadTGABits(szFileName, &nWidth, &nHeight, &nComponents, &eFormat);
if (pBits == NULL) {
return false;
}
//2、设置纹理参数
//S、T方向的环绕方式
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);
//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);
//4、使用完,释放指针
free(pBits);
return true;
}
//用来绘制金字塔
void MakePyramid(GLBatch& pyramidBatch)
{
//1、顶点坐标
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 };
//2、begin 开始设置
/*
1、类型
2、定点数
3、这个批次中会用到几个纹理,不写就是0
*/
pyramidBatch.Begin(GL_TRIANGLES, 18, 1);
//======================金字塔底部======================
//底部的四边形 = 三角形X + 三角形Y
//三角形X = (vBackLeft,vBackRight,vFrontRight)
//1、vBackLeft
//导入纹理坐标
/*
参数1:texture,纹理层次,对于使用存储着色器来进行渲染,设置为0
参数2:s 对应顶点坐标中的x坐标
参数3:t 对应顶点坐标中的y坐标
*/
pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
//导入顶点坐标
pyramidBatch.Vertex3fv(vBackLeft);
//2、vBackRight
pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
pyramidBatch.Vertex3fv(vBackRight);
//3、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);
//======================金字塔前面======================
//三角形:(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);
//3、end 结束设置
pyramidBatch.End();
}
//初始化纹理和金字塔
void SetupRC()
{
//1、设置背景色
glClearColor(0.77, 0.77, 0.77, 1.0);
//2、初始化着色器
shaderManager.InitializeStockShaders();
//3、深度测试
glEnable(GL_DEPTH_TEST);
//4、分配纹理对象
glGenTextures(1, &textureID);
//5、绑定纹理状态
glBindTexture(GL_TEXTURE_2D, textureID);
//6、加载纹理数据并且设置纹理数据
//纹理文件名称、缩小时候的过滤方式、放大时候的过滤方式、环绕方式
LoadTGATexture("brick.tga", GL_NEAREST, GL_LINEAR, GL_CLAMP_TO_EDGE);
//7、初始化金字塔
MakePyramid(pyramidBatch);
//8、设置观察者位置
cameraFrame.MoveForward(-10);
}
//清理,删除纹理对象
void ShutdownRC(void)
{
glDeleteTextures(1, &textureID);
}
void RenderScene(void)
{
//1、清理缓冲区
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//2、压栈
modelViewMatrix.PushMatrix();
//观察者矩阵
M3DMatrix44f mCamera;
cameraFrame.GetCameraMatrix(mCamera);
modelViewMatrix.MultMatrix(mCamera);
//物体矩阵
M3DMatrix44f mObjectFrame;
objectFrame.GetMatrix(mObjectFrame);
modelViewMatrix.MultMatrix(mObjectFrame);
//3、以防万一,在这里绑定纹理
glBindTexture(GL_TEXTURE_2D, textureID);
//4、使用着色器 纹理替换矩阵着色器
/*
参数1:GLT_SHADER_TEXTURE_REPLACE(着色器标签)
参数2:模型视图投影矩阵
参数3:纹理层
*/
shaderManager.UseStockShader(GLT_SHADER_TEXTURE_REPLACE,transformPipeline.GetModelViewProjectionMatrix(),0);
//5、绘制
pyramidBatch.Draw();
//6、出栈
modelViewMatrix.PopMatrix();
//7、交换缓冲区
glutSwapBuffers();
}
void SpecialKeys(int key, int x, int y)
{
//这里让物体在世界坐标系中进行旋转
if(key == GLUT_KEY_UP){
objectFrame.RotateWorld(m3dDegToRad(-5.0f), 1, 0, 0);
}
if(key == GLUT_KEY_DOWN){
objectFrame.RotateWorld(m3dDegToRad(5.0f), 1, 0, 0);
}
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);
}
//提交,调用RenderScene重新渲染
glutPostRedisplay();
}
void ChangeSize(int w, int h)
{
//1
glViewport(0, 0, w, h);
//2、创建投影矩阵
viewFrustum.SetPerspective(35.0f, float(w)/float(h), 1.0f, 500.0f);
//3、将投影矩阵加载到投影矩阵堆栈上
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
//4、设置变换管道来使用MV矩阵堆栈和P矩阵堆栈
transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
}
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("纹理金字塔");
GLenum err = glewInit();
if (GLEW_OK != err) {
fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
return 1;
}
glutReshapeFunc(ChangeSize);
glutSpecialFunc(SpecialKeys);
glutDisplayFunc(RenderScene);
SetupRC();
glutMainLoop();
ShutdownRC();
return 0;
}
Copy the code
Five, matters needing attention
- Instead of having a method that is too bloated, extract two wrapper functions for a cleaner look
- Note the order in which the texture API is used in the LoadTGATexture method
- Pay attention to the mapping of vertices to texture coordinates in the MakePyramid method
- Note that the RenderScene method is bound again after the stack to prevent the texture from being used elsewhere. And then remember out of the stack