前言
在openGL使用纹理贴图时:发现很多关于纹理很多参数和概念不清楚先整理一下
原始图像数据
像素包装
图像数据在内存中很少以紧密包装的形式存在。在许多硬件平台上,处于性能上的考虑,一幅图像的每一行都应该从一种特定字节对齐地址开始。绝大多数编译器会自动把变量和缓冲区放置在一个针对该架构对齐优化的地址上。
例如一个包含3个分量的rgb图像,每个分量存储在一个字节中,如果图像有199个像素,那么一行需要597个像素。如果硬件本身的体系结构是4字节排列,那么图像每一行的末尾将由额外的3个空字符进行填充达到600字节。
我们可以使用下列函数改变或者回复像素的存储方式。
void glPixelStorei(GLenum pname,Glint param);
void glPixelStoref(GLenum pname,GLfloat param);
例如,改成紧密包装(文件格式以1个字节排列,没有补齐):
glPixelStorei(GL_UNPACK_ALLGNMENT,1);
其中GL_UNPACK_ALIGNMENT指定OpenGL如何从数据缓冲区中解包图像数据。
glPixelStore参数
参数名 类型 初始值
GL_PACK_SWAP_BYTES GLboolean GL_FALSE
GL_UNPACK_SWAP_BYTES GLboolean GL_FALSE
GL_PACK_LSB_FIRST GLboolean GL_FALSE
GL_UNPACK_LSB_FIRST GLboolean GL_FALSE
GL_PACK_ROW_LENGTH GLint 0
GL_UNPACK_ROW_LENGTH GLint 0
GL_PACK_SKIP_ROWS GLint 0
GL_UNPACK_SKIP_ROWS GLint 0
GL_PACK_SKIP_PIXELS GLint 0
GL_UNPACK_SKIP_PIXELS GLint 0
GL_PACK_ALIGNMENT GLint 4
GL_UNPACK_ALIGNMENT GLint 4
GL_PACK_IMAGE_HEIGHT GLint 0
GL_UNPACK_IMAGE_HEIGHT GLint 0
GL_PACK_SKIP_IMAGES GLint 0
GL_UNPACK_SKIP_IMAGES GLint 0
像素图
每个像素需要一个以上的存储位来表示,附加位允许存储强度(亮度)。在OpenGL核心版本中,我们无法直接将一个像素图绘制到颜色缓冲区中,但是可以使用下面的函数将颜色缓冲区的内容作为像素图直接读取。
void glReadPixels(Glint x,Glint y,GLSizei width,GLSizei height,GLenum format,GLenum type,const void *pixels);
我们将x和y值指定为矩形左下角的窗口坐标,然后再指定矩形的width和height值(像素形式)。如果颜色缓冲区存储的数据与我们要求的不同,OpenGL将负责进行必要的转换。
第4个变量format指定pixels指向的数据元素的颜色布局,如下表:
OpenGL像素格式
常量 描述
GL_RGB 按照红、绿、蓝顺序排列的颜色
GL_RGBA 按照红、绿、蓝、Alpha顺序排列的颜色
GL_BGR 按照蓝、绿、红顺序排列的颜色
GL_BGRA 按照蓝、绿、红、Alpha顺序排列的颜色
GL_RED 每个像素值包含一个红色分量
GL_GREEN 每个像素值包含一个绿色分量
GL_BLUE 每个像素值包含一个蓝色分量
GL_RG 每个像素依次包含一个红色和一个绿色分量
GL_RED_INTEGER 每个像素包含一个整数形式的红色分量
GL_GREEN_INTEGER 每个像素包含一个整数形式的绿色分量
GL_BLUE_INTETER 每个像素包含一个整数形式的蓝色分量
GL_RG_INTEGER 每个玄素依次包含一个整数形式的红色和一个整数形式的绿色分量
GL_RGB_INTEGER 每个像素依次包含整数形式的红色、绿色和蓝色分量
GL_RGBA_INTEGER 每个像素一次包含整数形式的红色、绿色、蓝色和Alpha分量
GL_BGR_INTEGER 每个像素依次包含整数形式的蓝色、绿色和红色分量
GL_BGRA_INTEGER 每个像素依次包含整数形式的蓝色、绿色、红色和Alpha分量
GL_STENCIL_INDEX 每个像素只包含一个模板值
GL_DEPTH_COMPONENT 每个像素只包含一个深度值
GL_DEPTH_STENCIL 每个像素包含一个深度值和一个模板值
参数type解释参数*pixels指向的诗句,它告诉OpenGL使用缓冲区中的什么数据类型来存储颜色分量。如下表:
像素数据的数据类型
常量 描述
GL_UNSIGNED_BYTE 每种颜色分量都是一个8位无符号整数
GL_BYTE 8位有符号整数
GL_UNSIGNED_SHORT 16位无符号整数
GL_SHORT 16位有符号整数
GL_UNSIGNED_INT 32位无符号整数
GL_INT 32位有符号整数
GL_FLOAT 单精度浮点数
GL_HALF_FLOAT 半精度浮点数
GL_UNSIGNED_BYTE_3_2_2 包装的RGB值
GL_UNSIGNED_BYTE_2_3_3_REV 包装的RGB值
GL_UNSIGNED_SHORT_5_6_5 包装的RGB值
GL_UNSIGNED_SHORT_5_6_5_REV 包装的RGB值
GL_UNSIGNED_SHORT_4_4_4_4 包装的RGB值
GL_UNSIGNED_SHORT_4_4_4_4_REV 包装的RGB值
GL_UNSIGNED_SHORT_5_5_5_1 包装的RGB值
GL_UNSIGNED_SHORT_1_5_5_5_REV 包装的RGB值
GL_UNSIGNED_INT_8_8_8_8 包装的RGB值
GL_UNSIGNED_INT_8_8_8_8_REV 包装的RGB值
GL_UNSIGNED_INT_10_10_10_2 包装的RGB值
GL_UNSIGNED_INT_2_10_10_10_REV 包装的RGB值
GL_UNSIGNED_INT_24_8 包装的RGB值
GL_UNSIGNED_INT_10F_11F_REV 包装的RGB值
GL_FLOAT_24_UNSIGNED_INT_24_8_REV 包装的RGB值
glReadPixels从图形硬件中复制数据,通常通过总线传输到系统内存。在这种情况下,应用程序将被阻塞,直到内存传输完成。此外,如果我们制定一个与图形硬件的本地排列不同的像素不具,那么在数据进行重定格式时将产生额外的性能开销。
包装的像素格式
默认情况下,对于glReadPixels函数来说,读取操作在双缓冲区渲染环境下将在后台缓冲区进行,而在单缓冲区渲染环境下则在前台缓冲区进行。我们可以用下面的函数改变这些像素操作的源。
void glReadBuffer(GLenum mode);
模式参数可以取GL_FRONT、GL_BACK、GL_LEFT、GL_RIGHT、GL_FRONT_LEFT、GL_FRONT_RIGHT、GL_BACK_LEFT、GL_BACK_RIGHT或者甚至是GL_NONE中的任意一个。
保存像素
GLTools库中的gltWriteTGA函数从前台颜色缓冲区中读取颜色数据,并将这些数据存储到一个Targa文件格式的图像文件中。
GLint gltWriteTGA(const char *szFileName)
{
FILE *pFile; // 文件指针
TGAHEADER tgaHeader; // tga文件头
unsigned long lImageSize; // 图像的大小
GLbyte *pBits = NULL; // 图像数据
GLint iViewport[4]; // 视口
GLenum lastBuffer; // 保存当前读取缓冲区的设置
// 取得当前视口大小
glGetIntegerv(GL_VIEWPORT, iViewport);
// 获得图像大小,因为tga的图像数据是紧密包装的,所以用视口的宽高乘以3个字节
lImageSize = iViewport[2] * 3 * iViewport[3];
// 分配内存用于存储读取出来的图像数据
pBits = (GLbyte *)malloc(lImageSize);
if(pBits == NULL)
return 0;
// 设置为逐个像素的方式读取
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glPixelStorei(GL_PACK_ROW_LENGTH, 0);
glPixelStorei(GL_PACK_SKIP_ROWS, 0);
glPixelStorei(GL_PACK_SKIP_PIXELS, 0);
// 保存当前的设置,后面再恢复它
glGetIntegerv(GL_READ_BUFFER, (GLint *)&lastBuffer);
glReadBuffer(GL_FRONT);
glReadPixels(0, 0, iViewport[2], iViewport[3], GL_BGR_EXT, GL_UNSIGNED_BYTE, pBits);
glReadBuffer(lastBuffer);
// 初始化tag文件头的格式
tgaHeader.identsize = 0;
tgaHeader.colorMapType = 0;
tgaHeader.imageType = 2;
tgaHeader.colorMapStart = 0;
tgaHeader.colorMapLength = 0;
tgaHeader.colorMapBits = 0;
tgaHeader.xstart = 0;
tgaHeader.ystart = 0;
tgaHeader.width = iViewport[2];
tgaHeader.height = iViewport[3];
tgaHeader.bits = 24;
tgaHeader.descriptor = 0;
// 苹果操作需要,进行大小端的转换
#ifdef __APPLE__
LITTLE_ENDIAN_WORD(&tgaHeader.colorMapStart);
LITTLE_ENDIAN_WORD(&tgaHeader.colorMapLength);
LITTLE_ENDIAN_WORD(&tgaHeader.xstart);
LITTLE_ENDIAN_WORD(&tgaHeader.ystart);
LITTLE_ENDIAN_WORD(&tgaHeader.width);
LITTLE_ENDIAN_WORD(&tgaHeader.height);
#endif
// 打开文件
pFile = fopen(szFileName, "wb");
if(pFile == NULL)
{
free(pBits);
return 0;
}
// 写文件头
fwrite(&tgaHeader, sizeof(TGAHEADER), 1, pFile);
// 写图像数据
fwrite(pBits, lImageSize, 1, pFile);
// 释放临时分配的内存空间
free(pBits);
fclose(pFile);
// 成功了
return 1;
}
读取像素
gltReadTGABits从磁盘中载入Targa文件。
GLbyte *gltReadTGABits(const char *szFileName, GLint *iWidth, GLint *iHeight, GLint *iComponents, GLenum *eFormat)
{
FILE *pFile; // 文件指针
TGAHEADER tgaHeader; // TGA 文件头
unsigned long lImageSize; // 图像的大小,用字节表示
short sDepth; // 像素深度
GLbyte *pBits = NULL; // 指向位的指针
// 默认/失败值
*iWidth = 0;
*iHeight = 0;
*eFormat = GL_BGR_EXT;
*iComponents = GL_RGB;
// 尝试打开文件
pFile = fopen(szFileName, "rb");
if(pFile == NULL)
return NULL;
// 读入文件(二进制)
fread(&tgaHeader, 18/* sizeof(TGAHEADER)*/, 1, pFile);
// 为大小字节存储顺序问题而进行字节交换
#ifdef __APPLE__
LITTLE_ENDIAN_WORD(&tgaHeader.colorMapStart);
LITTLE_ENDIAN_WORD(&tgaHeader.colorMapLength);
LITTLE_ENDIAN_WORD(&tgaHeader.xstart);
LITTLE_ENDIAN_WORD(&tgaHeader.ystart);
LITTLE_ENDIAN_WORD(&tgaHeader.width);
LITTLE_ENDIAN_WORD(&tgaHeader.height);
#endif
// 获取纹理的宽度、高度和深度
*iWidth = tgaHeader.width;
*iHeight = tgaHeader.height;
sDepth = tgaHeader.bits / 8;
// 这里进行一些有效性检验,非常简单,我们只要懂得或者说关心8位、24位或32位
//的targa
if(tgaHeader.bits != 8 && tgaHeader.bits != 24 && tgaHeader.bits != 32)
return NULL;
// 计算图像缓冲区的大小
lImageSize = tgaHeader.width * tgaHeader.height * sDepth;
// 进行内存定位并进行成功检验
pBits = (GLbyte*)malloc(lImageSize * sizeof(GLbyte));
if(pBits == NULL)
return NULL;
// 读入位
// 检查读取错误。这项操作应该发现RLE或者其他我们不想识别的奇怪格式
if(fread(pBits, lImageSize, 1, pFile) != 1)
{
free(pBits);
return NULL;
}
// 设置希望的OpenGL格式
switch(sDepth)
{
case 3: // 最可能的情况
*eFormat = GL_BGR;
*iComponents = GL_RGB;
break;
case 4:
*eFormat = GL_BGRA_EXT;
*iComponents = GL_RGBA8;
break;
case 1:
*eFormat = GL_LUMINANCE;
*iComponents = GL_LUMINANCE8;
break;
default: // RGB
// 如果是在iPhone上TGA为BGR,并且iPhone不支持没有Alpha的BGR,
//但是它支持RGB,所以只要将红色和蓝色调整一下就能满足要求。
//但是为了加快iPhone的载入速度,请保存带有Alpha的TGA。
#ifdef OPENGL_ES
for(int i=0;i<lImageSize;i+=3){
GLbyte temp=pBits[i];
pBits[i]=pBits[i+2];
pBits[i+2]=temp;
}
#endif
};
// 文件操作完成
fclose(pFile);
// 返回指向图像数据的指针
return pBits;
}
载入纹理
有3个OpenGL函数最经常用来从存储器缓冲区中载入(比如说,从一个磁盘文件中读取)纹理数据。
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。我们也可以指定代理纹理(Proxy texture),方法是指定GL_PROXY_TEXTURE_1D、GL_PROXY_TEXTURE_2D或GL_PROXY_TEXTURE_3D,并使用glGetTexPatameter函数提取代理查询的结果。
Level参数指定了这些函数所加在的mip贴图层次。
internalformat参数告诉OpenGL我们希望在每个纹理单元中存储多少颜色成分,并在可能的情况下说明这些成分的存储大小,以及是否希望对纹理进行压缩。
最常用的纹理内部格式
常量 含义
GL_ALPHA 按照alpha值存储纹理单元
GL_LUMINANCE 按照亮度值存储纹理单元
GL_LUMINANCE_ALPHA 按照亮度值和alpha值存储纹理单元
GL_RGB 按照红、绿、蓝成分存储纹理单元
GL_RGBA 按照红、绿、蓝和alpha成分存储纹理单元
使用颜色缓冲区
一维和二维的纹理也可以从颜色缓冲区加在数据。我们可以从颜色缓冲区读取一幅图像,并通过下面这两个函数将它化为一个新的纹理使用。
void glCopyTexImage1D(GLenum target,Glint level,GLenum internalformat,Glint x,Glint y,GLsizei width,Glint border);
void glCopyTexImage2D(GLenum target,Glint level,GLenum internalformat,Glint x,Glint y,GLsizei width,GLsizei height,Glint border);
这两个函数的操作类似于glTexImage,但在这里x和y在颜色缓冲区中指定了开始读取纹理数据的位置。源缓冲区是通过glReadBuffer函数设置的。并不存在glCopyTexImage3D。
更新纹理
替换一个纹理图像常常要比直接使用glTexImage重新加载一个新纹理快得多:
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 format,GLenum type,const GLvoid *data);
下面函数允许沃恩从颜色缓冲区读取纹理,并插入或替换原来纹理的一部分。
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,Glint 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);
纹理对象
纹理对象允许我们一次加载一个以上的纹理状态(包括纹理图像),以及在它们之间快速切换。纹理状态是由当前绑定的纹理对象维护的,而纹理对象是由一个无符号整数标识的:
按所需加载纹理数量初始化纹理对象:
void glGenTextures(GLsizei n,GLuint *textures);
为了绑定纹理状态,我们可以调用下面这个函数:
void glBindTexture(GLenum target,GLuint texture);
为了删除纹理对象,可以调用下面这个函数:
void glDeleteTextures(GLsizei n,GLuint *textures);
我们可以使用下面这个函数对纹理对象名(或句柄)进行测试,判断它们是否有效:
GLboolean glIsTexture(GLuint texture);
如果这个证书是一个以前已经分配的纹理对象名,则返回GL_TRUE。
纹理应用
纹理坐标
典型情况下,纹理坐标是作为0.0到1.0范围内的浮点值指定的。纹理坐标命名为s、t、r和q,支持从一维到三维纹理坐标。
纹理参数
很多参数的应用都会影响渲染的规则和纹理贴图的行为,这些参数通过下列函数的变量来进行设置:
void glTexParameterf(GLenum target,GLenum pname,GLfloat param);
void glTexParamereri(GLenum target,GLenum pname,Glint param);
void glTexParameterfv(GLenum target,GLenum pname,GLfloat *params);
void glTexParameteriv(GLenum target,GLenum pname,Glint *params);
第一个参数target指定这些参数将要应用到哪个纹理模式上,它可以是GL_TEXTURE_1D、GL_TEXTURE_2D和GL_TEXTURE_3D.第二个参数pname指定了需要设置哪个纹理参数,而最后一个参数param或params用于设置特定的纹理参数的值。
基本过滤
根据一个拉伸或收缩的纹理贴图计算颜色片段的过程称为纹理过滤。使用OpenGL的纹理参数函数,可以同事设置放大和缩小过滤器。这两种过滤器的参数名分别是GL_TEXTURE_MAG_FILTER和GL_TEXTURE_MIN_FILTER。就目前来说,我们可以为他们从两种基本的纹理过滤器GL_NEAREST和GL_LINEAR中进行选择,它们分别对应于最邻近过滤和线性过滤。
我们可以使用下面这两个函数,为放大和缩小过滤器设置纹理过滤器:
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
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_MIN_FILTER,GL_LINEAR);
纹理环绕
我们可以调glTexParameteri函数(并分别使用GL_TEXTURE_WRAP_S、GL_TEXTURE_WRAP_T或GL_TEXTURE_WRAP_R作为参数)、为每个坐标分别设置环绕模式。然后,我们可以把环绕模式设置为下面几个值之一:GL_REPEAT、GL_CLAMT、GL_CLAMP_TO_EDGE或GL_CLAMP_TO_BORDER。
综合运用
加载纹理
GLuint textureID;
glGenTextures(1,&textureID);
glBindTexture(GL_TEXTURE_2D,textureID);
loadTGATexture(“stone.tga”,GL_LINEAR,GL_LINEAR,GL_CLAMP_TO_EDGE);
其中,加载纹理的loadTGATexture定义如下:
bool LoadTGATexture(const char *szFileName, GLenum minFilter, GLenum magFilter, GLenum wrapMode)
{
GLbyte *pBits;
int nWidth, nHeight, nComponents;
GLenum eFormat;
// 读取材质比特
pBits = gltReadTGABits(szFileName, &nWidth, &nHeight, &nComponents, &eFormat);
if (pBits == NULL)
return false;
//设置放大和缩小过滤
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);
//改成紧密包装
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
//载入纹理数据
glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGB, nWidth, nHeight, 0,
eFormat, GL_UNSIGNED_BYTE, pBits);
free(pBits);
if (minFilter == GL_LINEAR_MIPMAP_LINEAR ||
minFilter == GL_LINEAR_MIPMAP_NEAREST ||
minFilter == GL_NEAREST_MIPMAP_LINEAR ||
minFilter == GL_NEAREST_MIPMAP_NEAREST)
//生成Mip层
glGenerateMipmap(GL_TEXTURE_2D);
return true;
}
指定纹理坐标
添加顶点:
pyramidBatch.Normal3f(0.0f, -1.0f, 0.0f);
pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
pyramidBatch.Vertex3f(-1.0f, -1.0f, -1.0f);
为一个面计算表面法线,然后再在所有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);
实际渲染:
glBindTexture(GL_TEXTURE_2D, textureID);
shaderManager.UseStockShader(GLT_SHADER_TEXTURE_POINT_LIGHT_DIFF,
transformPipeline.GetModelViewMatrix(),
transformPipeline.GetProjectionMatrix(),
vLightPos, vWhite, 0);
pyramidBatch.Draw();
Mip贴图
Mip贴图纹理由一些列纹理图像组成,每个图像大小在每个轴的方向上都缩小一半。
如果想指定只加载0层至第4层,应如下操作:
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_BASE_LEVEL,0);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAX_LEVEL,4);
Mip贴图过滤
经过Mip贴图的纹理过滤
常量 描述
GL_NEAREST 在Mip基层上执行最邻近过滤
GL_LINEAR 在Mip基层上执行线性过滤
GL_NEAREST_MIPMAP_NEAREST 选择最邻近Mip层,并执行最邻近过滤
GL_NEAREST_MIPMAP_LINEAR 在Mip层之间执行线性插补,并执行最邻近过滤
GL_LINEAR_MIPMAP_NEAREST 选择最邻近Mip层,并执行线性过滤
GL_LINEAR_MIPMAP_LINEAR 在Mip层之间执行线性插补,并执行线性过滤,又称三线性Mip贴图
生成Mip层
void glGenerateMipmap(GLenum target);
目标参数可以是GL_TEXTURE_1D、GL_TEXTURE_2D、GL_TEXTURE_3D、GL_TEXTURE_CUBE_MAP、GL_TEXTURE_1D_ARRAY或GL_TEXTURE_2D_ARRAY
各项异性过滤
首先,必须确认这种扩展是得到支持的:
if(gltIsExtSupported(“GL_EXT_texture_filter_anisotropic”));
然后,查询得到支持的各向异性过滤的最大数量:
GLfloat fLargest;
……
……
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT,&fLargest);
最后,设置想要应用的各向异性过滤的数量:
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAX_ANISOTROPY_EXT,fLargest);
纹理压缩
压缩纹理
纹理压缩是通过在glTexImage函数中把internalFormat参数设置为下表的任意值实现的:
通用压缩纹理格式
压缩格式 基本内部格式
GL_COMPRESSED_RGB GL_RGB
GL_COMPRESSED_RGBA GL_RGBA
GL_COMPRESSED_SRGB GL_RGB
GL_COMPRESSED_SRGB_ALPHA GL_RGBA
GL_COMPRESSED_RED GL_RED
GL_COMPRESSED_RG GL_RG(Red Green)
可以用如下方法来判断这个纹理是否被成功压缩:
Glint compFlag;
……
glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_COMPRESSED,&compFlag);
glGetTexLevelParameter函数提取的压缩纹理格式
参数 返回
GL_TEXTURE_COMPRESSED 如果纹理被压缩,返回1,否则返回0
GL_TEXTURE_COMPRESSED_IMAGE_SIZE 压缩后的纹理的大小(以字节为单位)
GL_TEXTURE_INTERNAL_FORMAT 所使用的压缩格式
GL_NUM_COMPRESSED_TEXTURE_FORMATS 受支持的研所为例格式的数量
GL_COMPRESSED_TEXTURE_FORMATS 一个包含了一些常量值的数组,每个常量值对应于一种受支持的压缩纹理格式
GL_TEXTURE_COMPRESSION_HINT 纹理压缩提示的值(GL/NICEST/GL_FASTEST)
我们可以使用glHint指定希望OpenGL根据最快速度还是最佳质量算法来选择压缩格式:
glHint(GL_TEXTURE_COMPRESSION_HINT,GL_FASTEST);
glHint(GL_TEXTURE_COMPRESSION_HINT,GL_NICEST);
glHint(GL_TEXTURE_COMPRESSION_HINT,GL_DONT_CARE);
GL_EXT_texture_conpression_s3tc的压缩格式
格式 描述
GL_COMPRESSED_RGB_S3TC_DXT1 RGB数据被压缩alpha值时钟是1.0
GL_COMPRESSED_RGBA_S3TC_DXT1 RGB数据被压缩,alpha值是1.0或0.0
GL_COMPRESSED_RGBA_S3TC_DXT2 RGB数据被压缩,alpha值用4位存储
GL_COMPRESSED_RGBA_S3TC_DXT3 RGB数据被压缩,alpha值是一些8位置的加权平均值
加载压缩纹理
为了加载预先经过压缩的纹理数据,可以使用下列函数之一:
void glCompressedTexImage1D(GLenum target,Glint level,GLenum internalFormat,GLsizei width,Glint border,GLsizei imageSize,void *data);
void glCompressedTexImage2D(GLenum target,Glint level,GLenum internalFormat,GLsizei width,GLsizei height,Glint border,GLsizei imageSize,void *data);
void glCompressedTexImage3D(GLenum target,Glint level,GLenum internalFormat,GLsizei width,GLsizei height,GLsizei depth,Glint border,GLsizei imageSize,GLvoid *data);
转载请注明出处:http://blog.csdn.net/ylbs110/article/details/51793970
示例
为了显示纹理的使用,本示例在学习笔记3(http://blog.csdn.net/ylbs110/article/details/51760021)的示例上直接添加了纹理代码,为了让纹理显示清晰,颜色做了少许改变。
注意:UseStockShader的第一个参数要改为GLT_SHADER_TEXTURE_POINT_LIGHT_DIFF才能成功使用纹理
#include "glew/glew.h"
#include "glfw/glfw3.h"
#include "glm/glm.hpp"
#include "glm/gtc/matrix_transform.hpp"
#include "glm/gtc/type_ptr.hpp"
#include "camera.h"
#include "filesystem.h"
#include "mesh.h"
#include "model.h"
#include "root_directory.h"
#include "shader.h"
#include "stb_image.h"
#include <iostream>
using namespace std;
static const unsigned int SCR_WIDTH = 1920;
static const unsigned int SCR_HEIGHT = 1080;
Camera camera(glm::vec3(0.0f, 0.0f, 3.0f));
float lastX = (float)SCR_WIDTH / 2.0f;
float lastY = (float)SCR_HEIGHT / 2.0f;
bool firstMouse = true;
float deltaTime = 0.0f;
float lastFrame = 0.0f;
void frameBuffer_size_callback(GLFWwindow *window, int width, int height);
void mouse_move_callback(GLFWwindow *window, double xPos, double yPos);
void mouse_scroll_callback(GLFWwindow *window, double xOffset, double yOffset);
void processInput(GLFWwindow *windows);
unsigned int loadTexture(const char* path);
void renderScene(const Shader &shader);
void renderCube();
void renderQuad();
// meshes
unsigned int planeVAO;
int main()
{
int glfwStatus = glfwInit();
if (glfwStatus != GLFW_TRUE)
{
cout << "GLFW initialize failed!" << endl;
glfwTerminate();
return -1;
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
GLFWwindow *window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "3.1.1.shadow_mapping_depth", nullptr, nullptr);
if (nullptr == window)
{
cout << "GLFW create window failed!" << endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, frameBuffer_size_callback);
glfwSetCursorPosCallback(window, mouse_move_callback);
glfwSetScrollCallback(window, mouse_scroll_callback);
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
int glewStatus = glewInit();
if (GLEW_OK != glewStatus)
{
cout << "GLEW initialize failed!" << endl;
return -1;
}
glEnable(GL_DEPTH_TEST);
Shader simpleDepthShader("3.1.1.shadow_mapping_depth.vs", "3.1.1.shadow_mapping_depth.fs");
Shader debugDepthQuad("3.1.1.debug_quad.vs", "3.1.1.debug_quad_depth.fs");
// set up vertex data (and buffer(s)) and configure vertex attributes
// ------------------------------------------------------------------
float planeVertices[] = {
// positions // normals // texcoords
25.0f, -0.5f, 25.0f, 0.0f, 1.0f, 0.0f, 25.0f, 0.0f,
-25.0f, -0.5f, 25.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f,
-25.0f, -0.5f, -25.0f, 0.0f, 1.0f, 0.0f, 0.0f, 25.0f,
25.0f, -0.5f, 25.0f, 0.0f, 1.0f, 0.0f, 25.0f, 0.0f,
-25.0f, -0.5f, -25.0f, 0.0f, 1.0f, 0.0f, 0.0f, 25.0f,
25.0f, -0.5f, -25.0f, 0.0f, 1.0f, 0.0f, 25.0f, 10.0f
};
//plane VAO
unsigned int planeVBO;
glGenVertexArrays(1, &planeVAO);
glBindVertexArray(planeVAO);
glGenBuffers(1, &planeVBO);
size_t planeVerticesSize = sizeof(planeVertices);
unsigned int vertexNum = sizeof(planeVertices) / sizeof(planeVertices[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(planeVertices), planeVertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (GLvoid*)(0));
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (GLvoid*)(3 * sizeof(float)));
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (GLvoid*)(6 * sizeof(float)));
glBindVertexArray(0);
// load textures
// -------------
unsigned int woodTexture = loadTexture(FileSystem::getPath("").c_str());
// configure depth map FBO
// -----------------------
const unsigned int SHADOW_WIDTH = 1024, SHADOW_HEIGHT = 1024;
unsigned int depthMapFBO;
glGenFramebuffers(1, &depthMapFBO);
glBindFramebuffer(GL_FRAMEBUFFER, depthMapFBO); //为什么不绑定frameBufferFBO
// create depth texture
unsigned int depthMap;
glGenTextures(1, &depthMap);
glBindTexture(GL_TEXTURE_2D, depthMap);
/******* 功能:指定一个二维纹理图像,纹理将指定纹理图像的一部分映射到纹理化为活动的每个图形基元上。
/******* 当前片段着色器或顶点着色器使用内置纹理查找函数时,纹理处于活动状态
/******* 参数1:target:指定活动纹理单元的目标纹理。必须是GL_TEXTURE_2D,
/******* GL_TEXTURE_CUBE_MAP_POSITIVE_X,GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
/******* GL_TEXTURE_CUBE_MAP_POSITIVE_Y,GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
/******* GL_TEXTURE_CUBE_MAP_POSITIVE_Z,或GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
/******* 参数2:level:指定详细程度编号。级别0是基本图像级别。级别n是第n个缩略图缩小图像。
/******* 参数3:internalformat指定纹理的内部格式。必须是下列符号常量之一:GL_ALPHA,GL_LUMINANCE,GL_LUMINANCE_ALPHA,GL_RGB,GL_RGBA
/******* 参数4:width:指定纹理图像的宽度。所有实现都支持宽度至少为64 texels的2D纹理图像和宽度至少为16 texels的立方体贴图纹理图像。
/******* 参数5:height:指定纹理图像的高度所有实现都支持至少64像素高的2D纹理图像和至少16像素高的立方体贴图纹理图像。
/******* 参数6:border指定边框的宽度。必须为0。
/******* 参数7:format指定纹理数据的格式。必须匹配internalformat。下面的符号值被接受:GL_ALPHA,GL_RGB,GL_RGBA,GL_LUMINANCE,和GL_LUMINANCE_ALPHA
/******* 参数8:指定纹理数据的数据类型。下面的符号值被接受:GL_UNSIGNED_BYTE,GL_UNSIGNED_SHORT_5_6_5,GL_UNSIGNED_SHORT_4_4_4_4,和GL_UNSIGNED_SHORT_5_5_5_1
/******* 参数9:data指定一个指向内存中图像数据的指针
*******/
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, SHADOW_WIDTH, SHADOW_HEIGHT, 0, GL_DEPTH_COMPONENT, GL_FALSE, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
// attach depth texture as FBO's depth buffer
glBindFramebuffer(GL_FRAMEBUFFER, depthMapFBO);
/******* 功能:将纹理图像附加到帧缓冲对象 *******/
/******* 参数1:指定帧缓冲目标。 符号常量必须是GL_FRAMEBUFFER *******/
/******* 参数2:指定应附加纹理图像的附着点。 必须是以下符号常量之一:GL_COLOR_ATTACHMENT0,GL_DEPTH_ATTACHMENT或GL_STENCIL_ATTACHMENT。 *******/
/******* 参数3:指定纹理目标。 必须是以下符号常量之一:GL_TEXTURE_2D,GL_TEXTURE_CUBE_MAP_POSITIVE_X,
/******* GL_TEXTURE_CUBE_MAP_NEGATIVE_X,GL_TEXTURE_CUBE_MAP_POSITIVE_Y,GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
/******* GL_TEXTURE_CUBE_MAP_POSITIVE_Z或GL_TEXTURE_CUBE_MAP_NEGATIVE_Z。
/******* 参数4:指定要附加图像的纹理对象。
/******* 参数5:指定要附加的纹理图像的mipmap级别,该级别必须为0。*******/
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthMap, 0);
/******* 功能:指定在当前帧缓冲区的哪个颜色缓冲区进行绘制,并GL_FRONT: 单缓存的默认值
/*******GL_FRONT_RIGHT:
/*******GL_NONE:不写入颜色缓冲区,如果存在片段着色器则不启用该着色器
/*******GL_FRONT_LEFT:
/*******GL_FRONT_AND_BACK:
/*******GL_RIGHT:
/*******GL_AUXi: i表示第几个辅助缓存.
/*******GL_LEFT:
/*******GL_BACK_RIGHT:
/*******GL_BACK: 双缓存的默认值
/*******GL_BA
对于帧缓冲区对象而言,可以取GL_COLOR_ATTACHMENT$m$ and GL_NONE,m为0~GL_MAX_COLOR_ATTACHMENTS
注意: 启用多个缓存用于写操作时, 只要其中一个缓存存在, 就不会发生错误. 如果指定的缓存都不存在, 就发生错误.CK_LEFT:
*******/
glDrawBuffer(GL_NONE);
/******* 功能:该函数主要是确定颜色缓冲区的来源(不会影响到深度、模板等缓冲区的读取),
这里的设置将会影响到glReadPixels, glCopyTexImage1D, glCopyTexImage2D, glCopyTexSubImage1D, glCopyTexSubImage2D, glCopyTexSubImage3D的读取结果。
*******/
glReadBuffer(GL_NONE);
/******* 功能:在每个纹理目标中,0被保留用以代表默认纹理。
当一张纹理被第一次绑定时,它假定成为指定的目标类型。例如,一张纹理若第一次被绑定到GL_TEXTURE_1D上,就变成了一张一维纹理;
若第一次被绑定到GL_TEXTURE_2D上,就变成了一张二维纹理。
由于OpenGL是状态机,当使用glBindTexture绑定一张纹理后,
如果不再绑定新的纹理,则OpenGL之后的操作都会对应此纹理,当一个纹理与目标绑定时,该目标之前的绑定关系将自动被打破。
纹理名称与相应的纹理内容位于当前GL rendering上下文的共享对象空间中。
*******/
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// shader configuration
// --------------------
debugDepthQuad.use();
debugDepthQuad.setInt("depthMap", 0);
// lighting info
// -------------
glm::vec3 lightPos(glm::vec3(-2.0f, 4.0f, -1.0f));
while (!glfwWindowShouldClose(window))
{
float currentFrame = glfwGetTime();
deltaTime = currentFrame - lastFrame;
lastFrame = currentFrame;
processInput(window);
glClearColor(0.1f, 0.1f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// 1. render depth of scene to texture (from light's perspective)
// --------------------------------------------------------------
glm::mat4 lightProjection, lightView;
glm::mat4 lightSpaceMatrix;
float near_plane = 1.0f, far_plane = 7.5f;
lightProjection = glm::ortho(-10.0f, 10.0f, -10.0f, 10.0f, near_plane, far_plane);
lightView = glm::lookAt(lightPos, glm::vec3(0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
lightSpaceMatrix = lightProjection * lightView;
// render scene from light's point of view
simpleDepthShader.use();
simpleDepthShader.setMat4("lightSpaceMatrix", lightSpaceMatrix);
glViewport(0, 0, SHADOW_WIDTH, SHADOW_HEIGHT);
glBindFramebuffer(GL_FRAMEBUFFER, depthMapFBO);
glClear(GL_DEPTH_BUFFER_BIT);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, woodTexture);
renderScene(simpleDepthShader);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// reset viewport
glViewport(0, 0, SCR_WIDTH, SCR_HEIGHT);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// render Depth map to quad for visual debugging
// ---------------------------------------------
debugDepthQuad.use();
debugDepthQuad.setFloat("near_plane", near_plane);
debugDepthQuad.setFloat("far_plane", far_plane);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, depthMap);
renderQuad();
// glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
// -------------------------------------------------------------------------------
glfwSwapBuffers(window);
glfwPollEvents();
}
// optional: de-allocate all resources once they've outlived their purpose:
// ------------------------------------------------------------------------
glDeleteVertexArrays(1, &planeVAO);
glDeleteBuffers(1, &planeVBO);
glfwTerminate();
return 0;
}
// renders the 3D scene
// --------------------
void renderScene(const Shader &shader)
{
//floor
glm::mat4 model = glm::mat4(1.0f);
shader.setMat4("model", model);
glBindVertexArray(planeVAO);
glDrawArrays(GL_TRIANGLES, 0, 6);
//第一个cube
model = glm::mat4(1.0f);
model = glm::translate(model, glm::vec3(0.0f, 1.5f, 0.0f));
model = glm::scale(model, glm::vec3(0.5f));
shader.setMat4("model", model);
renderCube(); //第一个cube
//第二个cube
model = glm::mat4(1.0f);
model = glm::translate(model, glm::vec3(2.0f, 0.0f, 1.0f));
model = glm::scale(model, glm::vec3(2.0f));
shader.setMat4("model", model);
renderCube(); //第二个cube
//第三个cube
model = glm::mat4(1.0f);
model = glm::translate(model, glm::vec3(-1.0f, 0.0f, 2.0f));
model = glm::scale(model, glm::vec3(0.25f));
shader.setMat4("model", model);
renderCube();
}
// renderCube() renders a 1x1 3D cube in NDC.
// -------------------------------------------------
unsigned int cubeVAO = 0;
unsigned int cubeVBO = 0;
void renderCube()
{
// initialize (if necessary)
if (cubeVAO == 0)
{
float vertices[] = {
// back face
-1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, // bottom-left
1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, // top-right
1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, // bottom-right
1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, // top-right
-1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, // bottom-left
-1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, // top-left
// front face
-1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // bottom-left
1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, // bottom-right
1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, // top-right
1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, // top-right
-1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, // top-left
-1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // bottom-left
// left face
-1.0f, 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top-right
-1.0f, 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // top-left
-1.0f, -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // bottom-left
-1.0f, -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // bottom-left
-1.0f, -1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, // bottom-right
-1.0f, 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top-right
// right face
1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top-left
1.0f, -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // bottom-right
1.0f, 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // top-right
1.0f, -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // bottom-right
1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top-left
1.0f, -1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, // bottom-left
// bottom face
-1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, // top-right
1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, // top-left
1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, // bottom-left
1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, // bottom-left
-1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, // bottom-right
-1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, // top-right
// top face
-1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, // top-left
1.0f, 1.0f , 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // bottom-right
1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, // top-right
1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // bottom-right
-1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, // top-left
-1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f // bottom-left
};
glGenVertexArrays(1, &cubeVAO);
glGenBuffers(1, &cubeVBO);
glBindVertexArray(cubeVAO);
glBindBuffer(GL_ARRAY_BUFFER, cubeVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// link vertex attributes
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (GLvoid*)(0));
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (GLvoid*)(3 * sizeof(float)));
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (GLvoid*)(6 * sizeof(float)));
//glBindFramebuffer(GL_FRAMEBUFFER, cubeVBO);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glEnableVertexAttribArray(0);
}
// render Cube
glBindVertexArray(cubeVAO);
glDrawArrays(GL_TRIANGLES, 0, 36);
glBindVertexArray(0);
}
// renderQuad() renders a 1x1 XY quad in NDC
// -----------------------------------------
unsigned int quadVAO = 0;
unsigned int quadVBO = 0;
void renderQuad()
{
if (quadVAO == 0)
{
float quadVertices[] = {
// positions // texture Coords
-1.0f, 1.0f, 0.0f, 0.0f, 1.0f,
-1.0f, -1.0f, 0.0f, 0.0f, 0.0f,
1.0f, 1.0f, 0.0f, 1.0f, 1.0f,
1.0f, -1.0f, 0.0f, 1.0f, 0.0f,
};
// setup plane VAO
glGenVertexArrays(1, &quadVAO);
glGenBuffers(1, &quadVBO);
glBindVertexArray(quadVAO);
glBindBuffer(GL_ARRAY_BUFFER, quadVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), quadVertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(0));
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(float)));
}
glBindVertexArray(quadVAO);
//glDrawArrays(GL_TRIANGLES, 0, 24);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glBindVertexArray(0);
}
// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
// ---------------------------------------------------------------------------------------------------------
void processInput(GLFWwindow *windows)
{
if (glfwGetKey(windows, GLFW_KEY_ESCAPE) == GLFW_PRESS)
{
glfwSetWindowShouldClose(windows, GL_TRUE);
}
if (glfwGetKey(windows, GLFW_KEY_W) == GLFW_PRESS)
{
camera.ProcessKeyboard(FORWARD, deltaTime);
}
if (glfwGetKey(windows, GLFW_KEY_A) == GLFW_PRESS)
{
camera.ProcessKeyboard(BACKWARD, deltaTime);
}
if (glfwGetKey(windows, GLFW_KEY_A) == GLFW_PRESS)
{
camera.ProcessKeyboard(LEFT, deltaTime);
}
if (glfwGetKey(windows, GLFW_KEY_D) == GLFW_PRESS)
{
camera.ProcessKeyboard(windows, deltaTime);
}
}
// glfw: whenever the window size changed (by OS or user resize) this callback function executes
// ---------------------------------------------------------------------------------------------
void frameBuffer_size_callback(GLFWwindow *window, int width, int height)
{
// make sure the viewport matches the new window dimensions; note that width and
// height will be significantly larger than specified on retina displays.
glViewport(0, 0, width, height);
}
// glfw: whenever the mouse moves, this callback is called
// -------------------------------------------------------
void mouse_move_callback(GLFWwindow *window, double xPos, double yPos)
{
if (firstMouse)
{
lastX = xPos;
lastY = yPos;
firstMouse = false;
}
double xOffset = xPos - lastX;
double yOffset = lastY - yPos;
lastX = xPos;
lastY = yPos;
camera.ProcessMouseMovement(xOffset, yOffset);
}
// glfw: whenever the mouse scroll wheel scrolls, this callback is called
// ----------------------------------------------------------------------
void mouse_scroll_callback(GLFWwindow *window, double xOffset, double yOffset)
{
camera.ProcessMouseScroll(yOffset);
}
// utility function for loading a 2D texture from file
// ---------------------------------------------------
unsigned int loadTexture(const char* path)
{
unsigned int textureId;
glGenTextures(1, &textureId);
int width, height, nrComponents;
unsigned char *data = stbi_load(path, &width, &height, &nrComponents, 0);
if (data)
{
/*OpenGL像素格式
常量 描述
GL_RGB 按照红、绿、蓝顺序排列的颜色
GL_RGBA 按照红、绿、蓝、Alpha顺序排列的颜色
GL_BGR 按照蓝、绿、红顺序排列的颜色
GL_BGRA 按照蓝、绿、红、Alpha顺序排列的颜色
GL_RED 每个像素值包含一个红色分量
GL_GREEN 每个像素值包含一个绿色分量
GL_BLUE 每个像素值包含一个蓝色分量
GL_RG 每个像素依次包含一个红色和一个绿色分量
GL_RED_INTEGER 每个像素包含一个整数形式的红色分量
GL_GREEN_INTEGER 每个像素包含一个整数形式的绿色分量
GL_BLUE_INTETER 每个像素包含一个整数形式的蓝色分量
GL_RG_INTEGER 每个玄素依次包含一个整数形式的红色和一个整数形式的绿色分量
GL_RGB_INTEGER 每个像素依次包含整数形式的红色、绿色和蓝色分量
GL_RGBA_INTEGER 每个像素一次包含整数形式的红色、绿色、蓝色和Alpha分量
GL_BGR_INTEGER 每个像素依次包含整数形式的蓝色、绿色和红色分量
GL_BGRA_INTEGER 每个像素依次包含整数形式的蓝色、绿色、红色和Alpha分量
GL_STENCIL_INDEX 每个像素只包含一个模板值
GL_DEPTH_COMPONENT 每个像素只包含一个深度值
GL_DEPTH_STENCIL 每个像素包含一个深度值和一个模板值*/
GLenum format;
if (nrComponents == 1)
{
format = GL_RED;
}
else if (nrComponents == 3)
{
format = GL_RGB;
}
else if (nrComponents == 4)
{
format = GL_RGBA;
}
glBindTexture(GL_TEXTURE_2D, textureId);
//glActiveTexture(GL_TEXTURE0);
/******* 功能:指定一个二维纹理图像,纹理将指定纹理图像的一部分映射到纹理化为活动的每个图形基元上。
/******* 当前片段着色器或顶点着色器使用内置纹理查找函数时,纹理处于活动状态
/******* 参数1:target:指定活动纹理单元的目标纹理。必须是GL_TEXTURE_2D,
/******* GL_TEXTURE_CUBE_MAP_POSITIVE_X,GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
/******* GL_TEXTURE_CUBE_MAP_POSITIVE_Y,GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
/******* GL_TEXTURE_CUBE_MAP_POSITIVE_Z,或GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
/******* 参数2:level:指定详细程度编号。级别0是基本图像级别。级别n是第n个缩略图缩小图像。
/******* 参数3:internalformat指定纹理的内部格式。必须是下列符号常量之一:GL_ALPHA,GL_LUMINANCE,GL_LUMINANCE_ALPHA,GL_RGB,GL_RGBA
/******* 参数4:width:指定纹理图像的宽度。所有实现都支持宽度至少为64 texels的2D纹理图像和宽度至少为16 texels的立方体贴图纹理图像。
/******* 参数5:height:指定纹理图像的高度所有实现都支持至少64像素高的2D纹理图像和至少16像素高的立方体贴图纹理图像。
/******* 参数6:border指定边框的宽度。必须为0。
/******* 参数7:format指定纹理数据的格式。必须匹配internalformat。下面的符号值被接受:GL_ALPHA,GL_RGB,GL_RGBA,GL_LUMINANCE,和GL_LUMINANCE_ALPHA
/******* 参数8:指定纹理数据的数据类型。下面的符号值被接受:GL_UNSIGNED_BYTE,GL_UNSIGNED_SHORT_5_6_5,GL_UNSIGNED_SHORT_4_4_4_4,和GL_UNSIGNED_SHORT_5_5_5_1
/******* 参数9:data指定一个指向内存中图像数据的指针
*******/
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
/******* 功能:为纹理对象生成一组完整的mipmap
/******* 参数1:target 指定将生成mipmap的纹理对象绑定到的活动纹理单元的纹理目标。 必须是以下符号常量之一:GL_TEXTURE_2D或GL_TEXTURE_CUBE_MAP。
*******/
glGenerateMipmap(GL_TEXTURE_2D);
// for this tutorial: use GL_CLAMP_TO_EDGE to prevent semi-transparent borders. Due to interpolation it takes texels from next repeat
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, format == GL_RGBA ? GL_CLAMP_TO_EDGE : GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, format == GL_RGBA ? GL_CLAMP_TO_EDGE : GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
stbi_image_free(data);
}
else
{
cout << "Texture failed to load at path" << path << endl;
stbi_image_free(data);
}
return textureId;
}
(1)3.1.1.debug_quad.vs 顶点着色器程序
#version 450 core
layout (location = 0) in vec3 aPos;
//layout (location = 1) in vec3 aNormal;
layout (location = 1) in vec2 aTexCoords;
out vec2 TexCoords;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
TexCoords = aTexCoords;
// gl_Position = vec4(aPos, 1.0f);
gl_Position = projection * view * vec4(aPos, 1.0f);
}
再来一张贴图
(2)3.1.1.debug_quad_depth.fs 片元着色器程序
#version 450 core
out vec4 FragColor;
in vec2 TexCoords;
uniform sampler2D depthMap;
uniform float near_plane;
uniform float far_plane;
// required when using a perspective projection matrix
float LinearizeDepth(float depth)
{
float z = depth * 2.0f - 1.0f; // Back to NDC
return (2.0f * near_plane * far_plane) / (far_plane + near_plane - z *(far_plane - near_plane));
}
void main()
{
float depthValue = texture(depthMap, TexCoords).r;
// FragColor = vec4(vec3(LinearizeDepth(depthValue) / far_plane), 1.0f); // perspective
FragColor = vec4(vec3(depthValue), 1.0f); // orthographic
}
在来一张贴图
(3)3.1.1.shadow_mapping_depth.vs 顶点着色器程序
#version 450 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoords;
out vec2 TexCoords;
uniform mat4 lightSpaceMatrix;
uniform mat4 model;
uniform sampler2D floorTexture;
void main()
{
TexCoords = aTexCoords;
gl_Position = lightSpaceMatrix * model * vec4(aPos, 1.0f);
}
再来一张贴图
(4)3.1.1.shadow_mapping_depth.fs 片元着色器程序
#version 450 core
out vec2 TexCoords;
uniform sampler2D floorTexture;
void main()
{
//什么要注释?因为不需要片元着色器处理
// gl_FragDepth = gl_FragCoord.z;
// vec3 color = texture(floorTexture, TexCoords).rgb;
// gl_FragColor = vec4(color, 1.0f);
gl_FragDepth = gl_FragCoord.z;
}
再来一张贴图
运行结果:深黑色部分是他上面浅色立方体的阴影贴图
参考