纹理只是一种能够应用到场景中的三角形上的图像数据,它通过经过过滤的纹理单元(texel,相当于纹理的像素)填充到实心区域。
原始图像数据
像素包装
- 图形数据在内存中很少以紧密包装的形式存在,为了性能,一副图像的每一行都应该从一个特定字节对齐地址开始(牺牲了空间),绝大多数编译器会自动把变量和缓冲区放置在一个针对该架构对齐优化的地址上
- Windows中的RMP文件格式的像素数据使用4字节排列;然而Targa(TGA)文件格式则是1个字节排列的(这样不会浪费空间)
改变或恢复像素的储存方式:
void glPixelStorei(GLenum pname,GLint param);
void glPixelStoref(GLenum pname,GLfloat param);
如图我们想要改成紧密包装像素数据,应该调用glPixelStorei(GL_UNPACK_ALIGNMENT,1);//GL_UNPACK_ALIGNMENT制定ruhr从数据缓冲区中解包图像数据
像素图
- 像素图在内存布局上与位图非常相似,但是每个像素都需要一个以上的储存位来表示。每个像素的附加位允许储存强度(intensity,有时被陈伟亮度,即luminance的值)或者颜色分量值。
- 可以使用下面的函数将颜色缓冲区的内容作为像素图直接读取.
void glReadPixels(GLint x,GLint y,GLSizei width,GLSizei height,
GLenum format,GLenum type,const void *pixels);
/*
x,y制定为举行左下角的窗口坐标,然后制定矩形的width和height值(像素形式)。如果颜色缓冲区储存的数据与我们要求的不同,OpenGL将负责进行必要的转换。
*pixels必须是合法的
format制定piels指向的数据元素的颜色布局
type解释参数*pixels指向的数据,他告诉OpenGL使用缓存区中的什么数据类型来存储颜色分量
*/
glReadPixels从图形硬件中复制数据,通常通过总线传输到系统内存
包装的像素格式
包装的像素格式将颜色数据压缩到了尽可能少的储存位中,每个颜色通道的位数显示在常量中
对于glReadPixels函数来说,读取操作在双缓冲区的渲染环境下载后台缓冲区进行,在单缓冲区渲染环境下则在前台缓冲区进行
可以用void glReadBuffer(GLenum mode);
改变这些像素操作的源
保存像素
用glWriteTGA函数来讲屏幕图像保存为一个Targa文件
读取像素
读取Targa文件以备OpenGL使用的函数
载入纹理
有三个OpenGL函数最经常用来从存储器缓冲区中载入(比如说,从一个磁盘文件中读取)纹理数据:
void glTexImage1D((GLenum target, GLint level, GLenum internalformat,GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type,void* data);
void glTexImage2D((GLenum target, GLint level, GLenum internalformat,GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type,void* data);
void glTexImage3D((GLenum target, GLint level, GLenum internalformat,GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type,void* data)
target 指定目标纹理,这个值必须是GL_TEXTURE_2D。
level 执行细节级别。0是最基本的图像级别,n表示第N级贴图细化级别。
internalformat 指定纹理中的颜色组件。可选的值有GL_ALPHA,GL_RGB,GL_RGBA,GL_LUMINANCE, GL_LUMINANCE_ALPHA 等几种。
width 指定纹理图像的宽度,必须是2的n次方。纹理图片至少要支持64个材质元素的宽度
height 指定纹理图像的高度,必须是2的m次方。纹理图片至少要支持64个材质元素的高度
border 指定边框的宽度。必须为0。
format 像素数据的颜色格式, 不需要和internalformatt取值必须相同。可选的值参考internalformat。
type 指定像素数据的数据类型。可以使用的值有GL_UNSIGNED_BYTE,GL_UNSIGNED_SHORT_5_6_5,GL_UNSIGNED_SHORT_4_4_4_4,GL_UNSIGNED_SHORT_5_5_5_1
最后三个参数format, type, data和用于把图像放入颜色缓冲区的glDrawPixels函数的对应参数相同
使用颜色缓冲区
一维和二维纹理也可以从颜色缓冲区加载数据,我们可以从颜色缓冲区读取一幅图像,并通过下面这两个函数将它作为一个新的纹理使用。
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,
GLint border);
更新纹理
替换纹理通常比重新加载一个新纹理快得多,用于完成这个任务的是glTexSubImage()
。
glCopyTexSubImage
函数允许从颜色缓冲区读取纹理,并插入或替换原来纹理的一部分。
颜色缓冲区是2D的,不存在一种对应方法来讲一副2D彩色图像作为一个3D纹理的来源,但我们可以使用glCopyTexSubImage3D函数,在一个三位问李重使用颜色缓冲区的数据来设置它的一个纹理单元平面
纹理对象
在纹理之间进行切换或者重新加载不同的纹理图像开销很大。纹理对象支持我们一次加载一个以上的纹理状态,以及在它们之间的快速切换。
void glGenTextures(GLsizei n,GLunit *textures)
分配一些纹理对象
void glBindTextures(GLenum n,GLunit texture)
绑定一种纹理状态
void glDeleteTextures(GLsizei n,GLunit *textures)
删除纹理对象
GLboolean glIsTexture(GLuint texture)
对纹理对象名进行测试,以判断它们是否有效
纹理应用
加载纹理只是在几何图形上应用纹理的第一部。最低限度我们必须同时提供纹理坐标,并设置纹理坐标环绕模式和纹理过滤。最后,我们可以选择对纹理进行Mip贴图,以提高纹理贴图性能和/或视觉质量
纹理坐标
纹理坐标是x和y轴上0到1之间的范围(注意我们使用的是2D纹理图片)。使用纹理坐标获取纹理颜色叫做采样(Sampling)。纹理坐标起始于(0,0)也就是纹理图片的左下角,终结于纹理图片的右上角(1,1)。下面的图片展示了我们是如何把纹理坐标映射到三角形上的。
我们为三角形准备了3个纹理坐标点。如上图所示,我们希望三角形的左下角对应纹理的左下角,因此我们把三角左下角的顶点的纹理坐标设置为(0,0);三角形的上顶点对应于图片的中间所以我们把它的纹理坐标设置为(0.5,1.0);同理右下方的顶点设置为(1.0,0)。我们只要传递这三个纹理坐标给顶点着色器就行了,接着片段着色器会为每个片段生成纹理坐标的插值。
纹理坐标看起来就像这样:
GLfloat texCoords[] = {
0.0f, 0.0f, // 左下角
1.0f, 0.0f, // 右下角
0.5f, 1.0f // 顶部位置
};
如图,一维、二维、三维纹理坐标(s、t、r、q与x、y、z、w相类似)(q为缩放因子)
纹理参数
glTexParameterf(GLenum target,GLenum pname,GLfloat param);
glTexParameteri(GLenum target,GLenum pname,GLint param);
glTexParameterfv(GLenum target,GLenum pname,GLfloat * params);
glTexParameteriv(GLenum target,GLenum pname,GLint *params);
基本过滤
纹理过滤:根据一个拉伸或收缩的纹理贴图计算颜色偏短的过程称为纹理过滤
最近邻过滤是我们能够选择的最简单、最快速的过滤方法,其最显著的特征就是当纹理被拉伸到特别大的时候所出现的大片斑驳状像素。
//最近邻过滤用于GL_TEXTURE_2D,为放大和缩小过滤器设置纹理过滤器
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAX_FILTER,GL_NEAREST)
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST)
线性过滤:更接近真实,没有人工操作的痕迹
//简单设置线性过滤
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAX_FILTER,GL_LINEAR)
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR)
纹理环绕
纹理采样有几种不同的插值方式。我们需要自己告诉OpenGL在纹理中采用哪种采样方式。
- 纹理坐标通常的范围是从(0, 0)到(1, 1),如果我们把纹理坐标设置为范围以外会发生什么?OpenGL默认的行为是重复这个纹理图像(我们简单地忽略浮点纹理坐标的整数部分),但OpenGL提供了更多的选择:
环绕方式:
- GL_REPEAT 纹理的默认行为,重复纹理图像
- GL_MIRRORED_REPEAT和GL_REPEAT一样,除了重复的图片是镜像放置的
- GL_CLAMP_TO_EDGE纹理坐标会在0到1之间,超出的部分会重复纹理坐标的边缘,就是边缘被拉伸
- GL_CLAMP_TO_BORDER超出的部分是用户指定的边缘的颜色
当纹理坐标超出默认范围时,每个值都有不同的视觉效果输出。我们来看看这些纹理图像的例子:
前面提到的选项都可以使用glTexParameter
函数单独设置每个坐标轴s、t(如果是使用3D纹理那么还有一个r)它们和x、y(z)是相等的:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
第一个参数指定了纹理目标;我们使用的是2D纹理,因此纹理目标是GL_TEXTURE_2D。
第二个参数需要我们去告知我们希望去设置哪个纹理轴。我们打算设置的是WRAP选项,并且指定S和T轴。最后一个参数需要我们传递放置方式,在这个例子里我们在当前激活纹理上应用GL_MIRRORED_REPEAT。
如果我们选择GL_CLAMP_TO_BORDER选项,我们还要指定一个边缘的颜色。这次使用glTexParameter
函数的fv后缀形式,加上GL_TEXTURE_BORDER_COLOR作为选项,这个函数需要我们传递一个边缘颜色的float数组作为颜色值:
float borderColor[] = { 1.0f, 1.0f, 0.0f, 1.0f };
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
综合应用
// Pyramid.cpp
// OpenGL SuperBible, Chapter 5
// Demonstrates Texture mapping a pyramid
// Program by Richard S. Wright Jr.
#include <GLTools.h> // OpenGL toolkit
#include <GLMatrixStack.h>
#include <GLFrame.h>
#include <GLFrustum.h>
#include <GLBatch.h>
#include <GLGeometryTransform.h>
#include <math.h>
#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#include <GL/glut.h>
#endif
/////////////////////////////////////////////////////////////////////////////////
// An assortment of needed classes
GLShaderManager shaderManager;
GLMatrixStack modelViewMatrix;
GLMatrixStack projectionMatrix;//投影矩阵堆栈
GLFrame cameraFrame;//全局照相机实例
GLFrame objectFrame;
GLFrustum viewFrustum;//投影矩阵
GLBatch pyramidBatch;
GLuint textureID;
GLGeometryTransform transformPipeline;// 变换管线
M3DMatrix44f shadowMatrix;
void MakePyramid(GLBatch& pyramidBatch)
{
//6个三角形18个顶点,1代表在这个批次中将应用一个纹理
pyramidBatch.Begin(GL_TRIANGLES, 18, 1);
// Bottom of pyramid
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, 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);
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 }; // Vector of three floats(x, y, z)
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;
// Front of Pyramid
//glTool函数库包含了一个函数,专门用于根据一个多边形上的3个点计算一条法线向量:
//该方法的第一个参数用于存储求得的法线向量,还要另外向它传递3个向量,表示取自多边形或三角形上的点
//(以逆时针环绕方向指定)。注意,该方法返回的法线向量并不一定是单位长度的
m3dFindNormal(n, vApex, vFrontLeft, vFrontRight);
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.0f);
pyramidBatch.Vertex3fv(vApex); // Apex
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
pyramidBatch.Vertex3fv(vFrontLeft); // Front left corner
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
pyramidBatch.Vertex3fv(vFrontRight); // Front right corner
m3dFindNormal(n, vApex, vBackLeft, vFrontLeft);
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.0f);
pyramidBatch.Vertex3fv(vApex); // Apex
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
pyramidBatch.Vertex3fv(vBackLeft); // Back left corner
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
pyramidBatch.Vertex3fv(vFrontLeft); // Front left corner
m3dFindNormal(n, vApex, vFrontRight, vBackRight);
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.0f);
pyramidBatch.Vertex3fv(vApex); // Apex
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
pyramidBatch.Vertex3fv(vFrontRight); // Front right corner
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
pyramidBatch.Vertex3fv(vBackRight); // Back right cornder
m3dFindNormal(n, vApex, vBackRight, vBackLeft);
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.0f);
pyramidBatch.Vertex3fv(vApex); // Apex
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
pyramidBatch.Vertex3fv(vBackRight); // Back right cornder
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
pyramidBatch.Vertex3fv(vBackLeft); // Back left corner
pyramidBatch.End();
}
// Load a TGA as a 2D Texture. Completely initialize the state
bool LoadTGATexture(const char *szFileName, GLenum minFilter, GLenum magFilter, GLenum wrapMode)
{
GLbyte *pBits;
int nWidth, nHeight, nComponents;
GLenum eFormat;
// Read the texture bits
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, nComponents, 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)
glGenerateMipmap(GL_TEXTURE_2D);
return true;
}
///////////////////////////////////////////////////////////////////////////////
// This function does any needed initialization on the rendering context.
// This is the first opportunity to do any OpenGL related tasks.
void SetupRC()
{
// Black background
glClearColor(0.7f, 0.7f, 0.7f, 1.0f);
//调用之前使用
shaderManager.InitializeStockShaders();
glEnable(GL_DEPTH_TEST);
glGenTextures(1, &textureID);//分配一些纹理对象
glBindTexture(GL_TEXTURE_2D, textureID);//绑定一种纹理状态
LoadTGATexture("stone.tga", GL_LINEAR, GL_LINEAR, GL_CLAMP_TO_EDGE);
MakePyramid(pyramidBatch);
cameraFrame.MoveForward(-7.0f);
}
///////////////////////////////////////////////////////////////////////////////
// Cleanup... such as deleting texture objects
void ShutdownRC(void)
{
glDeleteTextures(1, &textureID);
}
///////////////////////////////////////////////////////////////////////////////
// Called to draw scene
void RenderScene(void)
{
static GLfloat vLightPos[] = { 1.0f, 1.0f, 0.0f };
static GLfloat vWhite[] = { 1.0f, 1.0f, 1.0f, 1.0f };
// Clear the window with current clearing color
//glClear()函数的作用是用当前缓冲区清除值,
//也就是glClearColor或者glClearDepth、glClearIndex、glClearStencil、glClearAccum等函数所指定的值来清除指定的缓冲区,
//也可以使用glDrawBuffer一次清除多个颜色缓存。
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
//保存当前模型视图矩阵
modelViewMatrix.PushMatrix();
M3DMatrix44f mCamera;
cameraFrame.GetCameraMatrix(mCamera);
modelViewMatrix.MultMatrix(mCamera);
M3DMatrix44f mObjectFrame;
objectFrame.GetMatrix(mObjectFrame);
modelViewMatrix.MultMatrix(mObjectFrame);
glBindTexture(GL_TEXTURE_2D, textureID);//绑定纹理对象
//绘制背景,点光源
shaderManager.UseStockShader(GLT_SHADER_TEXTURE_POINT_LIGHT_DIFF,
transformPipeline.GetModelViewMatrix(),
transformPipeline.GetProjectionMatrix(),
vLightPos, vWhite, 0);
pyramidBatch.Draw();
modelViewMatrix.PopMatrix();
// Flush drawing commands
glutSwapBuffers();
}
// Respond to arrow keys by moving the camera frame of reference
void SpecialKeys(int key, int x, int y)
{
if (key == GLUT_KEY_UP)
//void RotateWorld(float fAngle, float x, float y, float z)
//在世界坐标系中旋转
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();
//glutPostRedisplay 标记当前窗口需要重新绘制。
//通过glutMainLoop下一次循环时,窗口显示将被回调以重新显示窗口的正常面板。
}
///////////////////////////////////////////////////////////////////////////////
// Window has changed size, or has just been created. In either case, we need
// to use the window dimensions to set the viewport and the projection matrix.
void ChangeSize(int w, int h)
{
glViewport(0, 0, w, h);
//创建投影矩阵并把它载入到投影矩阵堆栈中
viewFrustum.SetPerspective(35.0f, float(w) / float(h), 1.0f, 500.0f);
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
//设置变换管线以使用两个矩阵堆栈
transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
}
///////////////////////////////////////////////////////////////////////////////
// Main entry point for GLUT based programs
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);//OpenGL渲染代码
GLenum err = glewInit();
if (GLEW_OK != err) {
fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
return 1;
}
SetupRC();
glutMainLoop();
ShutdownRC();
return 0;
}
Mip贴图
Mip贴图是一种强大的纹理技巧,不仅可以提高渲染性能,而且可以改善场景的显示质量。Mipmap中每一个层级的小图都是主图的一个特定比例的缩小细节的复制品。虽然在某些必要的视角,主图仍然会被使用,来渲染完整的细节。但是当贴图被缩小或者只需要从远距离观看时,mipmap就会转换到适当的层级。。
Mip贴图过滤
活动的Mip贴图
//汉字注释见书p151
// Tunnel.cpp
// Demonstrates mipmapping and using texture objects
// OpenGL SuperBible
// Richard S. Wright Jr.
#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; // Shader Manager
GLMatrixStack modelViewMatrix; // Modelview Matrix
GLMatrixStack projectionMatrix; // Projection Matrix
GLFrustum viewFrustum; // View Frustum
GLGeometryTransform transformPipeline; // Geometry Transform Pipeline
GLBatch floorBatch;
GLBatch ceilingBatch;
GLBatch leftWallBatch;
GLBatch rightWallBatch;
GLfloat viewZ = -65.0f;
// Texture objects
#define TEXTURE_BRICK 0
#define TEXTURE_FLOOR 1
#define TEXTURE_CEILING 2
#define TEXTURE_COUNT 3
GLuint textures[TEXTURE_COUNT];
const char *szTextureFiles[TEXTURE_COUNT] = { "brick.tga", "floor.tga", "ceiling.tga" };
///////////////////////////////////////////////////////////////////////////////
// Change texture filter for each texture object
void ProcessMenu(int value)
{
GLfloat fLargest;
GLint iLoop;
for(iLoop = 0; iLoop < TEXTURE_COUNT; iLoop++)
{
glBindTexture(GL_TEXTURE_2D, textures[iLoop]);
switch(value)
{
case 0:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
break;
case 1:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
break;
case 2:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
break;
case 3:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR);
break;
case 4:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
break;
case 5:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
break;
case 6:
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &fLargest);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, fLargest);
break;
case 7:
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0f);
break;
}
}
// Trigger Redraw
glutPostRedisplay();
}
//////////////////////////////////////////////////////////////////
// This function does any needed initialization on the rendering
// context. Here it sets up and initializes the texture objects.
void SetupRC()
{
GLbyte *pBytes;
GLint iWidth, iHeight, iComponents;
GLenum eFormat;
GLint iLoop;
// Black background
glClearColor(0.0f, 0.0f, 0.0f,1.0f);
shaderManager.InitializeStockShaders();
// Load textures
glGenTextures(TEXTURE_COUNT, textures);
for(iLoop = 0; iLoop < TEXTURE_COUNT; iLoop++)
{
// Bind to next texture object
glBindTexture(GL_TEXTURE_2D, textures[iLoop]);
// Load texture, set filter and wrap modes
pBytes = gltReadTGABits(szTextureFiles[iLoop],&iWidth, &iHeight,
&iComponents, &eFormat);
// Load texture, set filter and wrap modes
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_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, iComponents, iWidth, iHeight, 0, eFormat, GL_UNSIGNED_BYTE, pBytes);
glGenerateMipmap(GL_TEXTURE_2D);
// Don't need original texture data any more
free(pBytes);
}
// Build Geometry
GLfloat z;
floorBatch.Begin(GL_TRIANGLE_STRIP, 28, 1);
for(z = 60.0f; z >= 0.0f; z -=10.0f)
{
floorBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
floorBatch.Vertex3f(-10.0f, -10.0f, z);
floorBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
floorBatch.Vertex3f(10.0f, -10.0f, z);
floorBatch.MultiTexCoord2f(0, 0.0f, 1.0f);
floorBatch.Vertex3f(-10.0f, -10.0f, z - 10.0f);
floorBatch.MultiTexCoord2f(0, 1.0f, 1.0f);
floorBatch.Vertex3f(10.0f, -10.0f, z - 10.0f);
}
floorBatch.End();
ceilingBatch.Begin(GL_TRIANGLE_STRIP, 28, 1);
for(z = 60.0f; z >= 0.0f; z -=10.0f)
{
ceilingBatch.MultiTexCoord2f(0, 0.0f, 1.0f);
ceilingBatch.Vertex3f(-10.0f, 10.0f, z - 10.0f);
ceilingBatch.MultiTexCoord2f(0, 1.0f, 1.0f);
ceilingBatch.Vertex3f(10.0f, 10.0f, z - 10.0f);
ceilingBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
ceilingBatch.Vertex3f(-10.0f, 10.0f, z);
ceilingBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
ceilingBatch.Vertex3f(10.0f, 10.0f, z);
}
ceilingBatch.End();
leftWallBatch.Begin(GL_TRIANGLE_STRIP, 28, 1);
for(z = 60.0f; z >= 0.0f; z -=10.0f)
{
leftWallBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
leftWallBatch.Vertex3f(-10.0f, -10.0f, z);
leftWallBatch.MultiTexCoord2f(0, 0.0f, 1.0f);
leftWallBatch.Vertex3f(-10.0f, 10.0f, z);
leftWallBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
leftWallBatch.Vertex3f(-10.0f, -10.0f, z - 10.0f);
leftWallBatch.MultiTexCoord2f(0, 1.0f, 1.0f);
leftWallBatch.Vertex3f(-10.0f, 10.0f, z - 10.0f);
}
leftWallBatch.End();
rightWallBatch.Begin(GL_TRIANGLE_STRIP, 28, 1);
for(z = 60.0f; z >= 0.0f; z -=10.0f)
{
rightWallBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
rightWallBatch.Vertex3f(10.0f, -10.0f, z);
rightWallBatch.MultiTexCoord2f(0, 0.0f, 1.0f);
rightWallBatch.Vertex3f(10.0f, 10.0f, z);
rightWallBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
rightWallBatch.Vertex3f(10.0f, -10.0f, z - 10.0f);
rightWallBatch.MultiTexCoord2f(0, 1.0f, 1.0f);
rightWallBatch.Vertex3f(10.0f, 10.0f, z - 10.0f);
}
rightWallBatch.End();
}
///////////////////////////////////////////////////
// Shutdown the rendering context. Just deletes the
// texture objects
void ShutdownRC(void)
{
glDeleteTextures(TEXTURE_COUNT, textures);
}
///////////////////////////////////////////////////
// Respond to arrow keys, move the viewpoint back
// and forth
void SpecialKeys(int key, int x, int y)
{
if(key == GLUT_KEY_UP)
viewZ += 0.5f;
if(key == GLUT_KEY_DOWN)
viewZ -= 0.5f;
// Refresh the Window
glutPostRedisplay();
}
/////////////////////////////////////////////////////////////////////
// Change viewing volume and viewport. Called when window is resized
void ChangeSize(int w, int h)
{
GLfloat fAspect;
// Prevent a divide by zero
if(h == 0)
h = 1;
// Set Viewport to window dimensions
glViewport(0, 0, w, h);
fAspect = (GLfloat)w/(GLfloat)h;
// Produce the perspective projection
viewFrustum.SetPerspective(80.0f,fAspect,1.0,120.0);
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
}
///////////////////////////////////////////////////////
// Called to draw scene
void RenderScene(void)
{
// Clear the window with current clearing color
glClear(GL_COLOR_BUFFER_BIT);
modelViewMatrix.PushMatrix();
modelViewMatrix.Translate(0.0f, 0.0f, viewZ);
shaderManager.UseStockShader(GLT_SHADER_TEXTURE_REPLACE, transformPipeline.GetModelViewProjectionMatrix(), 0);
glBindTexture(GL_TEXTURE_2D, textures[TEXTURE_FLOOR]);
floorBatch.Draw();
glBindTexture(GL_TEXTURE_2D, textures[TEXTURE_CEILING]);
ceilingBatch.Draw();
glBindTexture(GL_TEXTURE_2D, textures[TEXTURE_BRICK]);
leftWallBatch.Draw();
rightWallBatch.Draw();
modelViewMatrix.PopMatrix();
// Buffer swap
glutSwapBuffers();
}
//////////////////////////////////////////////////////
// Program entry point
int main(int argc, char *argv[])
{
gltSetWorkingDirectory(argv[0]);
// Standard initialization stuff
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
glutInitWindowSize(800, 600);
glutCreateWindow("Anisotropic Tunnel");
glutReshapeFunc(ChangeSize);
glutSpecialFunc(SpecialKeys);
glutDisplayFunc(RenderScene);
// Add menu entries to change filter
glutCreateMenu(ProcessMenu);
glutAddMenuEntry("GL_NEAREST",0);
glutAddMenuEntry("GL_LINEAR",1);
glutAddMenuEntry("GL_NEAREST_MIPMAP_NEAREST",2);
glutAddMenuEntry("GL_NEAREST_MIPMAP_LINEAR", 3);
glutAddMenuEntry("GL_LINEAR_MIPMAP_NEAREST", 4);
glutAddMenuEntry("GL_LINEAR_MIPMAP_LINEAR", 5);
glutAddMenuEntry("Anisotropic Filter", 6);
glutAddMenuEntry("Anisotropic Off", 7);
glutAttachMenu(GLUT_RIGHT_BUTTON);
GLenum err = glewInit();
if (GLEW_OK != err) {
fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
return 1;
}
// Startup, loop, shutdown
SetupRC();
glutMainLoop();
ShutdownRC();
return 0;
}
各向异性过滤
如果我们在纹理过滤时考虑了观察角度,那么这种方法就叫做各向异性过滤
纹理压缩
纹理压缩(Texture compression)是一种专为在三维计算机图形渲染系统中存储纹理而使用的图像压缩技术。与普通图像压缩算法的不同之处在于,纹理压缩算法为纹素的随机存取做了优化。
压缩纹理
加载压缩纹理
最后一个示例
// SphereWorld.cpp
// OpenGL SuperBible
// New and improved (performance) sphere world
// Program by Richard S. Wright Jr.
#include <GLTools.h>
#include <GLShaderManager.h>
#include <GLFrustum.h>
#include <GLBatch.h>
#include <GLMatrixStack.h>
#include <GLGeometryTransform.h>
#include <StopWatch.h>
#include <math.h>
#include <stdio.h>
#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#include <GL/glut.h>
#endif
#define NUM_SPHERES 50
GLFrame spheres[NUM_SPHERES];
GLShaderManager shaderManager; // Shader Manager
GLMatrixStack modelViewMatrix; // Modelview Matrix
GLMatrixStack projectionMatrix; // Projection Matrix
GLFrustum viewFrustum; // View Frustum
GLGeometryTransform transformPipeline; // Geometry Transform Pipeline
GLFrame cameraFrame; // Camera frame
GLTriangleBatch torusBatch;
GLTriangleBatch sphereBatch;
GLBatch floorBatch;
GLuint uiTextures[3];
void DrawSongAndDance(GLfloat yRot) // Called to draw dancing objects
{
static GLfloat vWhite[] = { 1.0f, 1.0f, 1.0f, 1.0f };
static GLfloat vLightPos[] = { 0.0f, 3.0f, 0.0f, 1.0f };
// Get the light position in eye space
M3DVector4f vLightTransformed;
M3DMatrix44f mCamera;
modelViewMatrix.GetMatrix(mCamera);
m3dTransformVector4(vLightTransformed, vLightPos, mCamera);
// Draw the light source
modelViewMatrix.PushMatrix();
modelViewMatrix.Translatev(vLightPos);
shaderManager.UseStockShader(GLT_SHADER_FLAT,
transformPipeline.GetModelViewProjectionMatrix(),
vWhite);
sphereBatch.Draw();
modelViewMatrix.PopMatrix();
glBindTexture(GL_TEXTURE_2D, uiTextures[2]);
for(int i = 0; i < NUM_SPHERES; i++) {
modelViewMatrix.PushMatrix();
modelViewMatrix.MultMatrix(spheres[i]);
shaderManager.UseStockShader(GLT_SHADER_TEXTURE_POINT_LIGHT_DIFF,
modelViewMatrix.GetMatrix(),
transformPipeline.GetProjectionMatrix(),
vLightTransformed,
vWhite,
0);
sphereBatch.Draw();
modelViewMatrix.PopMatrix();
}
// Song and dance
modelViewMatrix.Translate(0.0f, 0.2f, -2.5f);
modelViewMatrix.PushMatrix(); // Saves the translated origin
modelViewMatrix.Rotate(yRot, 0.0f, 1.0f, 0.0f);
// Draw stuff relative to the camera
glBindTexture(GL_TEXTURE_2D, uiTextures[1]);
shaderManager.UseStockShader(GLT_SHADER_TEXTURE_POINT_LIGHT_DIFF,
modelViewMatrix.GetMatrix(),
transformPipeline.GetProjectionMatrix(),
vLightTransformed,
vWhite,
0);
torusBatch.Draw();
modelViewMatrix.PopMatrix(); // Erased the rotate
modelViewMatrix.Rotate(yRot * -2.0f, 0.0f, 1.0f, 0.0f);
modelViewMatrix.Translate(0.8f, 0.0f, 0.0f);
glBindTexture(GL_TEXTURE_2D, uiTextures[2]);
shaderManager.UseStockShader(GLT_SHADER_TEXTURE_POINT_LIGHT_DIFF,
modelViewMatrix.GetMatrix(),
transformPipeline.GetProjectionMatrix(),
vLightTransformed,
vWhite,
0);
sphereBatch.Draw();
}
bool LoadTGATexture(const char *szFileName, GLenum minFilter, GLenum magFilter, GLenum wrapMode)
{
GLbyte *pBits;
int nWidth, nHeight, nComponents;
GLenum eFormat;
// Read the texture bits
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)
glGenerateMipmap(GL_TEXTURE_2D);
return true;
}
//////////////////////////////////////////////////////////////////
// This function does any needed initialization on the rendering
// context.
void SetupRC()
{
// Make sure OpenGL entry points are set
glewInit();
// Initialze Shader Manager
shaderManager.InitializeStockShaders();
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
// This makes a torus
gltMakeTorus(torusBatch, 0.4f, 0.15f, 40, 20);
// This makes a sphere
gltMakeSphere(sphereBatch, 0.1f, 26, 13);
// Make the solid ground
GLfloat texSize = 10.0f;
floorBatch.Begin(GL_TRIANGLE_FAN, 4, 1);
floorBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
floorBatch.Vertex3f(-20.0f, -0.41f, 20.0f);
floorBatch.MultiTexCoord2f(0, texSize, 0.0f);
floorBatch.Vertex3f(20.0f, -0.41f, 20.0f);
floorBatch.MultiTexCoord2f(0, texSize, texSize);
floorBatch.Vertex3f(20.0f, -0.41f, -20.0f);
floorBatch.MultiTexCoord2f(0, 0.0f, texSize);
floorBatch.Vertex3f(-20.0f, -0.41f, -20.0f);
floorBatch.End();
// Make 3 texture objects
glGenTextures(3, uiTextures);
// Load the Marble
glBindTexture(GL_TEXTURE_2D, uiTextures[0]);
LoadTGATexture("marble.tga", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR, GL_REPEAT);
// Load Mars
glBindTexture(GL_TEXTURE_2D, uiTextures[1]);
LoadTGATexture("marslike.tga", GL_LINEAR_MIPMAP_LINEAR,
GL_LINEAR, GL_CLAMP_TO_EDGE);
// Load Moon
glBindTexture(GL_TEXTURE_2D, uiTextures[2]);
LoadTGATexture("moonlike.tga", GL_LINEAR_MIPMAP_LINEAR,
GL_LINEAR, GL_CLAMP_TO_EDGE);
// Randomly place the spheres
for(int i = 0; i < NUM_SPHERES; i++) {
GLfloat x = ((GLfloat)((rand() % 400) - 200) * 0.1f);
GLfloat z = ((GLfloat)((rand() % 400) - 200) * 0.1f);
spheres[i].SetOrigin(x, 0.0f, z);
}
}
////////////////////////////////////////////////////////////////////////
// Do shutdown for the rendering context
void ShutdownRC(void)
{
glDeleteTextures(3, uiTextures);
}
// Called to draw scene
void RenderScene(void)
{
static CStopWatch rotTimer;
float yRot = rotTimer.GetElapsedSeconds() * 60.0f;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
modelViewMatrix.PushMatrix();
M3DMatrix44f mCamera;
cameraFrame.GetCameraMatrix(mCamera);
modelViewMatrix.MultMatrix(mCamera);
// Draw the world upside down
modelViewMatrix.PushMatrix();
modelViewMatrix.Scale(1.0f, -1.0f, 1.0f); // Flips the Y Axis
modelViewMatrix.Translate(0.0f, 0.8f, 0.0f); // Scootch the world down a bit...
glFrontFace(GL_CW);
DrawSongAndDance(yRot);
glFrontFace(GL_CCW);
modelViewMatrix.PopMatrix();
// Draw the solid ground
glEnable(GL_BLEND);
glBindTexture(GL_TEXTURE_2D, uiTextures[0]);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
static GLfloat vFloorColor[] = { 1.0f, 1.0f, 1.0f, 0.75f};
shaderManager.UseStockShader(GLT_SHADER_TEXTURE_MODULATE,
transformPipeline.GetModelViewProjectionMatrix(),
vFloorColor,
0);
floorBatch.Draw();
glDisable(GL_BLEND);
DrawSongAndDance(yRot);
modelViewMatrix.PopMatrix();
// Do the buffer Swap
glutSwapBuffers();
// Do it again
glutPostRedisplay();
}
// Respond to arrow keys by moving the camera frame of reference
void SpecialKeys(int key, int x, int y)
{
float linear = 0.1f;
float angular = float(m3dDegToRad(5.0f));
if(key == GLUT_KEY_UP)
cameraFrame.MoveForward(linear);
if(key == GLUT_KEY_DOWN)
cameraFrame.MoveForward(-linear);
if(key == GLUT_KEY_LEFT)
cameraFrame.RotateWorld(angular, 0.0f, 1.0f, 0.0f);
if(key == GLUT_KEY_RIGHT)
cameraFrame.RotateWorld(-angular, 0.0f, 1.0f, 0.0f);
}
void ChangeSize(int nWidth, int nHeight)
{
glViewport(0, 0, nWidth, nHeight);
transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
viewFrustum.SetPerspective(35.0f, float(nWidth)/float(nHeight), 1.0f, 100.0f);
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
modelViewMatrix.LoadIdentity();
}
int main(int argc, char* argv[])
{
gltSetWorkingDirectory(argv[0]);
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(800,600);
glutCreateWindow("OpenGL SphereWorld");
glutReshapeFunc(ChangeSize);
glutDisplayFunc(RenderScene);
glutSpecialFunc(SpecialKeys);
SetupRC();
glutMainLoop();
ShutdownRC();
return 0;
}