![](https://img.haomeiwen.com/i6072651/f1bd5a123e4aa2cb.png)
金字塔图形坐标解析:
金字塔底部四边形 = 三角形X + 三角形Y
![](https://img.haomeiwen.com/i3278444/3ff581c1c8f53b15.png)
- 各个顶点坐标
vBackLeft (-1.0,-1.0,-1.0)
vBackRight (1.0,-1.0,-1.0)
vFrontLeft (-1.0,-1.0,1.0)
vFrontRight(1.0,-1.0,1.0)
vApex (0,1.0,0)
- 三⻆形X的坐标如下:
vBackLeft(-1.0,-1.0,-1.0)
vBackRight(1.0,-1.0,-1.0)
vFrontRight(1.0,-1.0,1.0)- 三⻆形Y的坐标如下:
vFrontLeft(-1.0,-1.0,1.0)
vBackLeft(-1.0,-1.0,-1.0)
vFrontRight(1.0,-1.0,1.0)- 三⻆形X的2D纹理坐标如下(第一个0是纹理,后面的表示坐标(s,t)):
vBackLeft(0,0.0,0.0)
vBackRight(0,1.0,0.0)
vFrontRight(0,1.0,1.0)- 三⻆形Y的2D纹理坐标如下:
vFrontLeft(0,0.0,1.0)
vBackLeft(0,0.0,0.0)
vFrontRight(0,1.0,1.0)
金字塔实现并填充纹理思维导图
![](https://img.haomeiwen.com/i2023991/75931040bf1ea762.png)
1、主函数中设置环境及注册函数
int main(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、开始设置环境
void SetupRC()
{
//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、渲染场景
步骤一:设置场景
void RenderScene(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();
}
步骤二:绘制金字塔
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个顶点数据
*/
//塔顶
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纹理。
bool LoadTGATexture(const char *szFileName,GLenum minFilter,GLenum magFilter,GLenum wrapMode)
{
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、窗口改变监听
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);
//viewFrustum.GetProjectionMatrix() 获取viewFrustum投影矩阵
//并将其加载到投影矩阵堆栈上
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
//3.设置变换管道以使用两个矩阵堆栈(变换矩阵modelViewMatrix ,投影矩阵projectionMatrix)
//初始化GLGeometryTransform 的实例transformPipeline.通过将它的内部指针设置为模型视图矩阵堆栈 和 投影矩阵堆栈实例,来完成初始化
//当然这个操作也可以在SetupRC 函数中完成,但是在窗口大小改变时或者窗口创建时设置它们并没有坏处。而且这样可以一次性完成矩阵和管线的设置。
transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
}
6、特殊键位操作
void SpecialKeys(int key,int x,int y)
{
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、最后需要清除绑定的纹理
void ShutdownRC(void)
{
glDeleteTextures(1, &textureID);
}
运行代码,得到带纹理的金字塔(通过上下左右键操作可选择金字塔)
![](https://img.haomeiwen.com/i2023991/cf3ddebd5b489a76.png)
![](https://img.haomeiwen.com/i2023991/9bad09f0d2f13098.png)
网友评论