原始图像数据
- .像素包装
- 图像存储空间 = 图像的⾼高度 * 图像宽度 * 每个像素的字节数
认识函数
- 像素存储方式
//改变像素存储⽅式
void glPixelStorei(GLenum pname,GLint param);
//恢复像素存储⽅式
void glPixelStoref(GLenum pname,GLfloat param);
//举例:
//参数1:GL_UNPACK_ALIGNMENT 指定OpenGL 如何从数据缓存区中解包图像 数据
//参数2:表示参数GL_UNPACK_ALIGNMENT 设置的值
//GL_UNPACK_ALIGNMENT 指内存中每个像素行起点的排列请求,允许设置为1 (byte排列)、2(排列为偶数byte的⾏)、4(字word排列列)、8(行从双字节边界开始)
glPixelStorei(GL_UNPACK_ALIGNMENT,1);
- 从颜⾊缓存区内容作为像素图直接读取
/*
//参数1:x,矩形左下角的窗⼝坐标
//参数2:y,矩形左下角的窗口坐标
//参数3:width,矩形的宽,以像素为单位
//参数4:height,矩形的高,以像素为单位
//参数5:format,OpenGL 的像素格式,参考 表6-1
//参数6:type,解释参数pixels指向的数据,告诉OpenGL 使⽤缓存区中的什么 数据类型来存储颜⾊分量,像素数据的数据类型,参考 表6-2
//参数7:pixels,指向图形数据的指针
*/
void glReadPixels(GLint x,GLint y,GLSizei width,GLSizei
height, GLenum format, GLenum type,const void * pixels);
glReadBuffer(mode);—> 指定读取的缓存
glWriteBuffer(mode);—> 指定写⼊入的缓存
- 载入纹理
/**
* target:`GL_TEXTURE_1D`、`GL_TEXTURE_2D`、`GL_TEXTURE_3D`。
* Level:指定所加载的mip贴图层次。⼀般我们都把这个参数设置为0。
* internalformat:每个纹理单元中存储多少颜色成分。
* width、height、depth参数:指加载纹理的宽度、高度、深度。
==注意!== 这些值必须是2的整数次方。(这是因为OpenGL 旧版本上的遗留留下的一个要求。当然现在已经可以支持不是2的整数次方。但是开发者们还是习惯使用以2的整数次方去设置这些参数。)
* border参数:允许为纹理理贴图指定一个边界宽度。
* format、type、data参数:与我们在讲glDrawPixels 函数对于的参数相同
*/
void glTexImage1D(GLenum target,GLint level,GLint
internalformat,GLsizei width,GLint border,GLenum
format,GLenum type,void *data);
void glTexImage2D(GLenum target,GLint level,GLint
internalformat,GLsizei width,GLsizei height,GLint
border,GLenum format,GLenum type,void * data);
void glTexImage3D(GLenum target,GLint level,GLint internalformat,GLSizei width,GLsizei height,GLsizei depth,GLint border,GLenum format,GLenum type,void *data);
- 更新纹理: 可以从颜色缓存区读取一段纹理来插入或替换,插入纹理也一样
/**
* target:`GL_TEXTURE_1D`、`GL_TEXTURE_2D`、`GL_TEXTURE_3D`。
* Level:指定所加载的mip贴图层次。⼀般我们都把这个参数设置为0。
* internalformat:每个纹理单元中存储多少颜色成分。
* width、height、depth参数:指加载纹理的宽度、高度、深度。
==注意!== 这些值必须是2的整数次方。(这是因为OpenGL 旧版本上的遗留留下的一个要求。当然现在已经可以支持不是2的整数次方。但是开发者们还是习惯使用以2的整数次方去设置这些参数。)
* border参数:允许为纹理理贴图指定一个边界宽度。
* format、type、data参数:与我们在讲glDrawPixels 函数对于的参数相同
*/
void glTexSubImage1D(GLenum target,GLint level,GLint xOffset,GLsizei width,GLenum
format,GLenum type,const GLvoid *data);
void glTexSubImage2D(GLenum target,GLint level,GLint xOffset,GLint yOffset,GLsizei
width,GLsizei height,GLenum format,GLenum type,const GLvoid *data);
void glTexSubImage3D(GLenum target,GLint level,GLint xOffset,GLint yOffset,GLint
zOffset,GLsizei width,GLsizei height,GLsizei depth,Glenum type,const GLvoid * data);
- 插⼊替换纹理
/**
* target:`GL_TEXTURE_1D`、`GL_TEXTURE_2D`、`GL_TEXTURE_3D`。
* Level:指定所加载的mip贴图层次。⼀般我们都把这个参数设置为0。
* internalformat:每个纹理单元中存储多少颜色成分。
* width、height、depth参数:指加载纹理的宽度、高度、深度。
==注意!== 这些值必须是2的整数次方。(这是因为OpenGL 旧版本上的遗留留下的一个要求。当然现在已经可以支持不是2的整数次方。但是开发者们还是习惯使用以2的整数次方去设置这些参数。)
* border参数:允许为纹理理贴图指定一个边界宽度。
* format、type、data参数:与我们在讲glDrawPixels 函数对于的参数相同
*/
void glCopyTexSubImage1D(GLenum target,GLint level,GLint xoffset,GLint x,GLint y,GLsizei
width);
void glCopyTexSubImage2D(GLenum target,GLint level,GLint xoffset,GLint yOffset,GLint x,GL
y,GLsizei width,GLsizei height);
void glCopyTexSubImage3D(GLenum target,GLint level,GLint xoffset,GLint yOffset,GLint
zOffset,GLint x,GLint y,GLsizei width,GLsizei height);
- 使⽤颜色缓存区加载数据,形成新的纹理使⽤
/**
x,y 在颜⾊缓存区中指定了了开始读取纹理数据的位置; 缓存区里的数据,是源缓存区通过glReadBuffer设置的。
注意:不存在glCopyTextImage3D ,因为我们⽆法从2D 颜⾊缓存区中获取体积 数据。
*/
void glCopyTexImage1D(GLenum target,GLint level,GLenum
internalformt,GLint x,GLint y,GLsizei width,GLint border);
void glCopyTexImage2D(GLenum target,GLint level,GLenum
internalformt,GLint x,GLint y,GLsizei width,GLsizei
height,GLint border);
- 纹理对象的设置和使用
//步骤一:
// 使⽤函数分配纹理对象
//指定纹理对象的数量 和 指针(指针指向⼀个无符号整形数组,由纹理对象标识符填充)。
void glGenTextures(GLsizei n,GLuint * textTures);
//步骤二:
//绑定纹理状态
//参数 target:GL_TEXTURE_1D、GL_TEXTURE_2D、GL_TEXTURE_3D //参数texture:需要绑定的纹理对象
void glBindTexture(GLenum target,GLunit texture);
//步骤三:
//删除绑定纹理对象
//纹理对象 以及 纹理对象指针(指针指向一个无符号整形数组,由纹理对象标识符填充)。
void glDeleteTextures(GLsizei n,GLuint *textures);
//步骤四:(可选)
//测试纹理对象是否有效
//如果texture是⼀个已经分配空间的纹理对象,那么这个函数会返回GL_TRUE,否则会返回GL_FALSE。
GLboolean glIsTexture(GLuint texture);
- 设置纹理参数
/**通用函数
参数1:target,指定这些参数将要应用在哪个纹理模式上,比如GL_TEXTURE_1D、GL_TEXTURE_2D、GL_TEXTURE_3D。
参数2:pname,指定需要设置哪个纹理参数,相当于设置哪个属性,例如人的身高、体重等属性
参数3:param,设定特定的纹理参数的值
*/
glTexParameterf(GLenum target,GLenum pname,GLFloat param);
glTexParameteri(GLenum target,GLenum pname,GLint param);
glTexParameterfv(GLenum target,GLenum pname,GLFloat *param);
glTexParameteriv(GLenum target,GLenum pname,GLint *param);
-
像素数据类型,表示着在每一个分量占多少个单位
- 设置过滤⽅式
- 过滤方式分两种
-
一种是邻近过滤(Nearest Neighbor Filtering):左上角那个纹理像素的中心距离纹理坐标最近,所以它会被选择为样本颜色:
- 一种是线性过滤(linear Filtering):它会基于纹理坐标附近的纹理像素,计算出一个插值,近似出这些纹理像素之间的颜色。
- 2种纹理过滤⽅式比较
- 过滤方式的使用
//纹理缩小时,使⽤邻近过滤
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST)
//纹理放大时,使用线性过滤
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR)
- 建议是纹理放大时用线性过滤,缩小时用邻近过滤,但是仅仅是建议,也可以如下面那样使用
/**
可以根据当前需求来设置用邻近过滤或者线性过滤
*/
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_HEAREST);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_HEAREST);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
-
设置环绕⽅式
- 环绕方式的函数使用
/**
参数1:GL_TEXTURE_1D、GL_TEXTURE_2D、GL_TEXTURE_3D
参数2:GL_TEXTURE_WRAP_S、GL_TEXTURE_T、GL_TEXTURE_R,针对s,t,r坐标 (s,t,r分别表示x,y,z 坐标)
参数3:GL_REPEAT、GL_CLAMP、GL_CLAMP_TO_EDGE、GL_CLAMP_TO_BORDER
GL_REPEAT:OpenGL 在纹理坐标超过1.0的⽅向上对纹理理进⾏行行重复; GL_CLAMP:所需的纹理单元取自纹理边界或TEXTURE_BORDER_COLOR. GL_CLAMP_TO_EDGE 环绕模式强制对范围之外的纹理坐标沿着合法的纹理单元的最后⼀行或者最后⼀列来进行采样。
GL_CLAMP_TO_BORDER:在纹理坐标在0.0到1.0范围之外的只使用边界纹理单元。边界纹理单元是作为围绕基本图像的额外的⾏和列,并与基本纹理图像⼀起加载的。
*/
glTextParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAR_S,GL_CLAMP_TO_EDGE);
glTextParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAR_T,GL_CLAMP_TO_EDGE);
纹理坐标
为了能够把纹理映射(Map)到三角形上,我们需要指定三角形的每个顶点各自对应纹理的哪个部分。这样每个顶点就会关联着一个纹理坐标(Texture Coordinate),用来标明该从纹理图像的哪个部分采样(译注:采集片段颜色)。之后在图形的其它片段上进行片段插值(Fragment Interpolation)。
纹理坐标在x和y轴上,范围为0到1之间(注意我们使用的是2D纹理图像)。使用纹理坐标获取纹理颜色叫做采样(Sampling)。纹理坐标起始于(0, 0),也就是纹理图片的左下角,终始于(1, 1),即纹理图片的右上角。下面的图片展示了我们是如何把纹理坐标映射到三角形上的。
我们为三角形指定了3个纹理坐标点。如上图所示,我们希望三角形的左下角对应纹理的左下角,因此我们把三角形左下角顶点的纹理坐标设置为(0, 0);三角形的上顶点对应于图片的上中位置所以我们把它的纹理坐标设置为(0.5, 1.0);同理右下方的顶点设置为(1, 0)。我们只要给顶点着色器传递这三个纹理坐标就行了,接下来它们会被传片段着色器中,它会为每个片段进行纹理坐标的插值。
纹理坐标看起来就像这样:
GLfloat texCoords[] = {
0.0f, 0.0f, // 左下角
1.0f, 0.0f, // 右下角
0.5f, 1.0f // 上中
};
对纹理采样的解释非常宽松,它可以采用几种不同的插值方式。所以我们需要自己告诉OpenGL该怎样对纹理采样。
那么3D纹理坐标是怎样的呢?
-
3D纹理坐标图形
图中问号坐标位置是什么呢?答案是(1,1,0),为什么呢?请参考上面的2D纹理坐标三角形了解
案例金字塔图形解析
纹理坐标请参考下面
纹理坐标可以倒转过来(意思就是(0,0) 与(0.1)互换位置,即是下图打勾的连接方式),但是不可交叉连接,像下图那样的交叉是不允许的
案例示例
#include "GLTools.h"
#include "GLShaderManager.h"
#include "GLFrustum.h"
#include "GLBatch.h"
#include "GLFrame.h"
#include "GLMatrixStack.h"
#include "GLGeometryTransform.h"
#ifdef __APPLE__
#include
#else
#define FREEGLUT_STATIC
#include
#endif
GLShaderManager shaderManager;
GLMatrixStack modelViewMatrix;
GLMatrixStack projectionMatrix;
GLFrame cameraFrame;
GLFrame objectFrame;
GLFrustum viewFrustum;
GLBatch pyramidBatch;
//纹理变量,一般使用无符号整型
GLuint textureID;
GLGeometryTransform transformPipeline;
M3DMatrix44f shadowMatrix;
//绘制金字塔
void MakePyramid(GLBatch& pyramidBatch)
{
/*1、通过pyramidBatch组建三角形批次
参数1:类型
参数2:顶点数(金字塔是18个顶点)
参数3:这个批次中将会应用1个纹理
注意:如果不写这个参数,默认为0。
*/
pyramidBatch.Begin(GL_TRIANGLES, 18, 1);
//金字塔底部
//底部的四边形 = 三角形X + 三角形Y
//三角形X
/*设置法线
Normal3f:添加一个表面法线(法线坐标 与 Vertex顶点坐标中的Y轴一致)
表面法线是有方向的向量,代表表面或者顶点面对的方向(相反的方向)。在多数的关照模式下是必须使用。
*/
pyramidBatch.Normal3f(0.0f, -1.0f, 0.0f);
/**设置纹理坐标
MultiTexCoord2f(GLuint texture,GLclampf s,GLclamp t);
参数1:texture,纹理层次,对于使用存储着色器来进行渲染,设置为0
参数2:(s,t,r,q对应顶点坐标的x,y,z,w)s:对应顶点坐标中的x坐标
参数3:t:对应顶点坐标中的y
*/
pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f); //可以看坐标图就明白了
//vBlackLeft点
pyramidBatch.Vertex3f(-1.0f, -1.0f, -1.0f);
pyramidBatch.Normal3f(0.0f, -1.0f, 0.0f);
pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
//vBlackRight点
pyramidBatch.Vertex3f(1.0f, -1.0f, -1.0f);
pyramidBatch.Normal3f(0.0f, -1.0f, 0.0f);
pyramidBatch.MultiTexCoord2f(0, 1.0f, 1.0f);
//vFrontRight点
pyramidBatch.Vertex3f(1.0f, -1.0f, 1.0f);
//三角形B
pyramidBatch.Normal3f(0.0f, -1.0f, 0.0f);
pyramidBatch.MultiTexCoord2f(0, 0.0f, 1.0f);
pyramidBatch.Vertex3f(-1.0f, -1.0f, 1.0f);
pyramidBatch.Normal3f(0.0f, -1.0f, 0.0f);
pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
pyramidBatch.Vertex3f(-1.0f, -1.0f, -1.0f);
pyramidBatch.Normal3f(0.0f, -1.0f, 0.0f);
pyramidBatch.MultiTexCoord2f(0, 1.0f, 1.0f);
pyramidBatch.Vertex3f(1.0f, -1.0f, 1.0f);
//塔顶
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;
// 金字塔前面
//三角形:(Apex,vFrontLeft,vFrontRight)
//纹理坐标设置,参考PPT图6-4图
/** 获取从三点找到一个法线坐标(三点确定一个面)
void m3dFindNormal(result,point1, point2,point3);
参数1:结果
参数2-4:3个顶点数据
*/
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;
int nWidth, nHeight, nComponents;
GLenum eFormat;
//1、读纹理位置,读取像素
/**
参数一:纹理文件名称
参数二:文件宽度地址
参数三:文件高度地址
参数四:文件组建地址
参数五:文件格式地址
返回值: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:GL_UNPACK_ALIGNMENT,指定OpenGL如何从数据缓存区中解包图像数据
//参数2:针对GL_UNPACK_ALIGNMENT 设置的值
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
//载入纹理
//参数1:纹理维度
//参数2:mip贴图层次,为0,因为只有一层(从0开始)
//参数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);
//使用完毕释放pBits
free(pBits);
//只有minFilter 等于以下四种模式,才可以生成Mip贴图
//GL_NEAREST_MIPMAP_NEAREST具有非常好的性能,并且闪烁现象非常弱
//GL_LINEAR_MIPMAP_NEAREST常常用于对游戏进行加速,它使用了高质量的线性过滤器
//GL_LINEAR_MIPMAP_LINEAR 和GL_NEAREST_MIPMAP_LINEAR 过滤器在Mip层之间执行了一些额外的插值,以消除他们之间的过滤痕迹。
//GL_LINEAR_MIPMAP_LINEAR 三线性Mip贴图。纹理过滤的黄金准则,具有最高的精度。
if(minFilter == GL_LINEAR_MIPMAP_LINEAR ||
minFilter == GL_LINEAR_MIPMAP_NEAREST ||
minFilter == GL_NEAREST_MIPMAP_LINEAR ||
minFilter == GL_NEAREST_MIPMAP_NEAREST)
//加载Mip,纹理生成所有的Mip层
//参数:GL_TEXTURE_1D、GL_TEXTURE_2D、GL_TEXTURE_3D
glGenerateMipmap(GL_TEXTURE_2D);
return true;
}
//初始化数据,设置基础属性
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:纹理坐标环绕模式 GL_CLAMP_TO_EDGE :超出的坐标为用户指定的边缘颜色。
*/
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 RenderScene(void)
{
//颜色值 光照效果颜色,图形颜色
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();
}
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();
}
void ChangeSize(int w, int h)
{
glViewport(0, 0, w, h);
//创建投影矩阵
viewFrustum.SetPerspective(35.0f, float(w)/float(h), 1.0f, 500.0f);
//viewFrustum.GetProjectionMatrix() 获取viewFrustum投影矩阵
//并将其加载到投影矩阵堆栈上
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
// 设置变换管道以使用两个矩阵堆栈(变换矩阵modelViewMatrix ,投影矩阵projectionMatrix)
//初始化GLGeometryTransform 的实例transformPipeline.通过将它的内部指针设置为模型视图矩阵堆栈 和 投影矩阵堆栈实例,来完成初始化
//当然这个操作也可以在SetupRC 函数中完成,但是在窗口大小改变时或者窗口创建时设置它们并没有坏处。而且这样可以一次性完成矩阵和管线的设置。
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, 600);
glutCreateWindow("Pyramid");
glutReshapeFunc(ChangeSize);
glutSpecialFunc(SpecialKeys);
glutDisplayFunc(RenderScene);
GLenum err = glewInit();
if (GLEW_OK != err) {
fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
return 1;
}
SetupRC();
glutMainLoop();
ShutdownRC();
return 0;
}