在一个金字塔图形贴上纹理,示例程序代码如下:
#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;
}
运行效果如下: