OpenGL(十四)- 压缩纹理
压缩纹理
为了利用OpenGL对压缩纹理的支持,纹理数据一开始并不需要进行压缩。
我们可以在加载一幅纹理图像时请求OpenGL对它进行压缩,这是通过在glTexImage函数中设置internalFormat参数实现的。可选参数如下:
通过这种方式进行图像压缩增加了纹理加载的开销,但却能够通过更有效地使用纹理存储空间来增加纹理性能。
如果由于某些原因而无法对纹理进行压缩,OpenGL就会使用上表所列出的基本内部格式,并加载未经压缩的纹理。
当我们试图按照这种方式加载并压缩一个纹理时,可以使用glGetTexLevelParameteriv函数(以GL_TEXTURE_COMPRESSED为参数)来判断这个纹理是否被成功压缩。
GLint compFlag;……glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_COMPRESSED,&compFlag);glGetTexLevelParameteriv函数可以接受几个新的参数名,它们都和压缩纹理有关。
当纹理进行压缩之后,OpenGL会选择最适当的纹理压缩格式。
我们可以使用glHint指定希望OpenGL根据最快速度还是最佳质量算法来选择压缩格式。
glHint(GL_TEXTURE_COMPRESSION_HINT,GL_FASTEST);glHint(GL_TEXTURE_COMPRESSION_HINT,GL_NICEST);glHint(GL_TEXTURE_COMPRESSION_HINT,GL_DONT_CARE);加载压缩纹理
用glGetCompressedTexImage函数(相当于未压缩纹理的glTexImage函数)提取经过压缩的数据并把它保存到磁盘中。在后续的纹理加载中,可以使用原始压缩数据,从而极大地提高纹理的加载速度。
为了加载预先经过压缩的纹理数据,可以使用下列函数:
voidglCompressedTexImage1D(GLenum target,GLint level,GLenum internalFormat,GLsizei width,GLint border,GLsizei imageSize,void*data);voidglCompressedTexImage2D(GLenum target,GLint level,GLenum internalFormat,GLsizei width,GLsizei height,GLint border,GLsizei imageSize,void*data);voidglCompressedTexImage3D(GLenum target,GLint level,GLenum internalFormat,GLsizei width,GLsizei height,GLsizei depth,GLint border,Glsizei imageSize,GLvoid*data);纹理压缩是一种非常流行的纹理特性。较小的纹理所占据的空间更小、通过网络传输的速度更快、从磁盘加载的速度更快、复制到图形内存也更加快速,并且允许更多的纹理加载到硬件中,而且使纹理的启用更加快速!
但是,不要忘了,和生活中的很多事情一样,天下没有免费的午餐。
使用纹理压缩也要付出一些代价。例如,GL_EXT_texture_compression_s3tc方法是通过从每个纹理单元提取颜色数据进行工作的。
对于有些纹理,这样可能会导致图像质量的损失(尤其是那些包含了平滑颜色过渡的纹理)。
在其他一些时候,具有大量细节的纹理在视觉上几乎与未压缩的源纹理相同,颜色的变化几乎无法察觉。
纹理压缩方法的选择(或选择不使用纹理压缩)可能极大地依赖于底层图像的本质。
示例
在此前分享的SphereWorld示例基础上扩展,代码如下:
#include"sphereworld5.h"#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>#ifndefFREEGLUT_STATIC#defineFREEGLUT_STATIC#endif#include<GL/glut.h>#defineNUM_SPHERES50namespacesphereworld5{GLFrame spheres[NUM_SPHERES];GLShaderManager shaderManager;GLMatrixStack modelViewMatrix;GLMatrixStack projectionMatrix;GLFrustum viewFrustum;GLGeometryTransform transformPipeline;GLFrame cameraFrame;GLTriangleBatch torusBatch;GLTriangleBatch sphereBatch;GLBatch floorBatch;GLuint uiTextures[3];voidDrawSongAndDance(floatyRot){staticGLfloat vWhite[]={1.0f,1.0f,1.0f,1.0f};staticGLfloat vLightPos[]={0.0f,3.0f,0.0f,1.0f};// 获取眼空间中的光源位置M3DVector4f vLightTransformed;M3DMatrix44f mCamera;modelViewMatrix.GetMatrix(mCamera);m3dTransformVector4(vLightTransformed,vLightPos,mCamera);// 绘制光源modelViewMatrix.PushMatrix();modelViewMatrix.Translatev(vLightPos);shaderManager.UseStockShader(GLT_SHADER_FLAT,transformPipeline.GetModelViewProjectionMatrix(),vWhite);sphereBatch.Draw();modelViewMatrix.PopMatrix();glBindTexture(GL_TEXTURE_2D,uiTextures[2]);for(inti=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();}modelViewMatrix.Translate(0.0f,0.2f,-2.5f);modelViewMatrix.PushMatrix();modelViewMatrix.Rotate(yRot,0.0f,1.0f,0.0f);// 绘制与相机相关的内容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();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();}boolLoadTGATexture(constchar*szFileName,unsignedintminFilter,unsignedintmagFilter,unsignedintwrapMode){GLbyte*pBits;intnWidth,nHeight,nComponents;GLenum eFormat;pBits=gltReadTGABits(szFileName,&nWidth,&nHeight,&nComponents,&eFormat);if(pBits==NULL)returnfalse;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);returntrue;}voidSetupRC(){glewInit();shaderManager.InitializeStockShaders();glEnable(GL_DEPTH_TEST);glEnable(GL_CULL_FACE);glClearColor(0.0f,0.0f,0.0f,1.0f);gltMakeTorus(torusBatch,0.4f,0.15f,40,20);gltMakeSphere(sphereBatch,0.1f,26,13);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();glGenTextures(3,uiTextures);glBindTexture(GL_TEXTURE_2D,uiTextures[0]);LoadTGATexture("./src/chapter_5/src/SphereWorld/marble.tga",GL_LINEAR_MIPMAP_LINEAR,GL_LINEAR,GL_REPEAT);glBindTexture(GL_TEXTURE_2D,uiTextures[1]);LoadTGATexture("./src/chapter_5/src/SphereWorld/marslike.tga",GL_LINEAR_MIPMAP_LINEAR,GL_LINEAR,GL_CLAMP_TO_EDGE);glBindTexture(GL_TEXTURE_2D,uiTextures[2]);LoadTGATexture("./src/chapter_5/src/SphereWorld/moonlike.tga",GL_LINEAR_MIPMAP_LINEAR,GL_LINEAR,GL_CLAMP_TO_EDGE);for(inti=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);}}voidShutdownRC(){glDeleteTextures(3,uiTextures);}voidRenderScene(){staticCStopWatch rotTimer;floatyRot=rotTimer.GetElapsedSeconds()*60.0f;glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);modelViewMatrix.PushMatrix();M3DMatrix44f mCamera;cameraFrame.GetCameraMatrix(mCamera);modelViewMatrix.MultMatrix(mCamera);modelViewMatrix.PushMatrix();modelViewMatrix.Scale(1.0f,-1.0f,1.0f);modelViewMatrix.Translate(0.0f,0.8f,0.0f);glFrontFace(GL_CW);DrawSongAndDance(yRot);glFrontFace(GL_CCW);modelViewMatrix.PopMatrix();glEnable(GL_BLEND);glBindTexture(GL_TEXTURE_2D,uiTextures[0]);glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);staticGLfloat 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();glutSwapBuffers();glutPostRedisplay();}voidSpecialKeys(intkey,intx,inty){floatlinear=0.1f;floatangular=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);}voidChangeSize(intw,inth){glViewport(0,0,w,h);transformPipeline.SetMatrixStacks(modelViewMatrix,projectionMatrix);viewFrustum.SetPerspective(35.0f,float(w)/float(h),1.0f,100.0f);projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());modelViewMatrix.LoadIdentity();}intsphereworld5_test(intargc,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();return0;}}效果预览:
