想必很多人都见过DDS这种文件,它是一个“图片文件”,如果你安装了某些看图软件,你可以直接双击打开它来进行预览。

那么,这种DDS文件和我们常见的TGA/PNG之类的文件有何不同呢?

DDS和TGA/PNG/JPG之类的“图片文件” 一样,支持“压缩”,减少磁盘空间占用(把文件变小)。

通常我们要加载一个TGA或者PNG文件到OpenGL的时候,都要先把文件数据还原成RGB格式的像素数据,然后用glTexImage2D把像素数据传到显存。这个过程相当于“解压”,这通常非常消耗CPU资源,速度较慢。

但是DDS的压缩数据不需要“解压”就能直接传到显存,而且传到显存之后也不会解压,这极大减少了显存的使用量,并且提高了纹理加载速度,有绝对的优势。我们只需要读取好压缩数据,然后使用glCompressedTexImage2D(代替glTexImage2D)就可以直接把压缩数据传到显存,完成加载。

DDS可以保存许多种格式的像素数据,这里只讲最常用的3种(DXT1、DXT3、DXT5)。

* 当然DDS文件也能存储不压缩的像素数据。

为了在OpenGL中使用DDS压缩纹理(下文简称压缩纹理),我们需要一下2个OpenGL扩展:

GL_ARB_texture_compression

提供函数 “glCompressedTexImage2D”

GL_EXT_texture_compression_s3tc

提供以下格式的压缩纹理支持:
GL_COMPRESSED_RGB_S3TC_DXT1_EXT
GL_COMPRESSED_RGBA_S3TC_DXT3_EXT
GL_COMPRESSED_RGBA_S3TC_DXT5_EXT

完整的加载过程代码:

#include <stdio.h>
#include <gl/glut.h>
#include <gl/glext.h> // Minimum and maximum macros
#define max(a,b) (((a) > (b)) ? (a) : (b))
#define min(a,b) (((a) < (b)) ? (a) : (b)) PFNGLCOMPRESSEDTEXIMAGE2DARBPROC glCompressedTexImage2DARB = NULL; #pragma region DDS #define DDPF_ALPHAPIXELS 0x000001
#define DDPF_ALPHA 0x000002
#define DDPF_FOURCC 0x000004
#define DDPF_RGB 0x000040
#define DDPF_YUV 0x000200
#define DDPF_LUMINANCE 0x020000 #define D3DFMT_DXT1 (('D'<<0)|('X'<<8)|('T'<<16)|('1'<<24))
#define D3DFMT_DXT3 (('D'<<0)|('X'<<8)|('T'<<16)|('3'<<24))
#define D3DFMT_DXT5 (('D'<<0)|('X'<<8)|('T'<<16)|('5'<<24)) typedef struct
{
DWORD dwSize;
DWORD dwFlags;
DWORD dwFourCC;
DWORD dwRGBBitCount;
DWORD dwRBitMask;
DWORD dwGBitMask;
DWORD dwBBitMask;
DWORD dwABitMask;
} DDS_PIXELFORMAT; #define DDSD_CAPS 0x000001
#define DDSD_HEIGHT 0x000002
#define DDSD_WIDTH 0x000004
#define DDSD_PITCH 0x000008
#define DDSD_PIXELFORMAT 0x001000
#define DDSD_MIPMAPCOUNT 0x020000
#define DDSD_LINEARSIZE 0x080000
#define DDSD_DEPTH 0x800000 typedef struct
{
DWORD dwSize;
DWORD dwFlags;
DWORD dwHeight;
DWORD dwWidth;
DWORD dwPitchOrLinearSize;
DWORD dwDepth;
DWORD dwMipMapCount;
DWORD dwReserved1[];
DDS_PIXELFORMAT ddspf;
DWORD dwCaps;
DWORD dwCaps2;
DWORD dwCaps3;
DWORD dwCaps4;
DWORD dwReserved2;
} DDS_HEADER; typedef struct
{
DWORD dwMagic;
DDS_HEADER Header;
} DDS_FILEHEADER; // For a compressed texture, the size of each mipmap level image is typically one-fourth the size of the previous, with a minimum of 8 (DXT1) or 16 (DXT2-5) bytes (for
// square textures). Use the following formula to calculate the size of each level for a non-square texture:
#define SIZE_OF_DXT1(width, height) ( max(1, ( (width + 3) >> 2 ) ) * max(1, ( (height + 3) >> 2 ) ) * 8 )
#define SIZE_OF_DXT2(width, height) ( max(1, ( (width + 3) >> 2 ) ) * max(1, ( (height + 3) >> 2 ) ) * 16 ) #pragma endregion GLuint gl_load_dds(GLvoid *pBuffer)
{
DDS_FILEHEADER *header;
DWORD compressFormat;
GLuint texnum;
GLvoid *data;
GLsizei imageSize; header = (DDS_FILEHEADER *)pBuffer; if (header->dwMagic != 0x20534444) {
printf("bad dds file\n");
return ;
} if (header->Header.dwSize != ) {
printf("bad header size\n");
return ;
} if (!(header->Header.dwFlags & DDSD_LINEARSIZE)) {
printf("bad file type\n");
return ;
} if (!(header->Header.ddspf.dwFlags & DDPF_FOURCC)) {
printf("bad pixel format\n");
return ;
} compressFormat = header->Header.ddspf.dwFourCC; if (compressFormat != D3DFMT_DXT1 &&
compressFormat != D3DFMT_DXT3 &&
compressFormat != D3DFMT_DXT5) {
printf("bad compress format\n");
return ;
} data = (GLvoid *)(header + ); // header data skipped glGenTextures(, &texnum);
glBindTexture(GL_TEXTURE_2D, texnum); switch (compressFormat)
{
case D3DFMT_DXT1:
imageSize = SIZE_OF_DXT1(header->Header.dwWidth, header->Header.dwHeight);
glCompressedTexImage2DARB(GL_TEXTURE_2D, , GL_COMPRESSED_RGB_S3TC_DXT1_EXT, header->Header.dwWidth, header->Header.dwHeight, , imageSize, data);
break;
case D3DFMT_DXT3:
imageSize = SIZE_OF_DXT2(header->Header.dwWidth, header->Header.dwHeight);
glCompressedTexImage2DARB(GL_TEXTURE_2D, , GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, header->Header.dwWidth, header->Header.dwHeight, , imageSize, data);
break;
case D3DFMT_DXT5:
imageSize = SIZE_OF_DXT2(header->Header.dwWidth, header->Header.dwHeight);
glCompressedTexImage2DARB(GL_TEXTURE_2D, , GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, header->Header.dwWidth, header->Header.dwHeight, , imageSize, data);
break;
} glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, ); return texnum;
} GLuint g_texnum; void load_textures(void)
{
FILE *fp;
int size;
void *data; fp = fopen("028_dxt5.dds", "rb");
if (!fp) {
return;
} fseek(fp, , SEEK_END);
size = ftell(fp);
fseek(fp, , SEEK_SET); data = malloc(size);
if (!data) {
fclose(fp);
return;
} if (fread(data, size, , fp) != ) {
free(data);
fclose(fp);
return;
} fclose(fp); // Load DDS to GL texture
g_texnum = gl_load_dds(data); free(data);
} void init(void)
{
// GL_ARB_texture_compression
// GL_EXT_texture_compression_s3tc
glCompressedTexImage2DARB = (PFNGLCOMPRESSEDTEXIMAGE2DARBPROC)wglGetProcAddress("glCompressedTexImage2DARB"); load_textures();
glClearColor(, , , );
} void display(void)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); RECT rc;
rc.left = ;
rc.top = ;
rc.right = rc.left + ;
rc.bottom = rc.top + ; glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, g_texnum); glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glColor4f(, , , ); glBegin(GL_QUADS);
glTexCoord2f(, );
glVertex2f(rc.left, rc.top);
glTexCoord2f(, );
glVertex2f(rc.right, rc.top);
glTexCoord2f(, );
glVertex2f(rc.right, rc.bottom);
glTexCoord2f(, );
glVertex2f(rc.left, rc.bottom);
glEnd(); glutSwapBuffers();
glutPostRedisplay();
} void reshape(int width, int height)
{
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(, width, height, );
glMatrixMode(GL_MODELVIEW); glViewport(, , width, height);
} int main(int argc, char **argv)
{
glutInitWindowPosition(, );
glutInitWindowSize(++, ++);
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
glutCreateWindow("OpenGL DDS");
init();
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutMainLoop(); return ;
}

关于如何制作一个DDS文件,可以使用Nvidia提供的DXT工具,下载地址:

https://developer.nvidia.com/legacy-texture-tools
http://pan.baidu.com/s/1pKKRL3P

以下是文件大小对比:

以下是图像质量对比:

原图(TGA,无压缩):

DXT1(压缩比:1/8,无Alpha通道,但可以单色透明):

DXT3(压缩包:1/4,Alpha通道还原较差):

DXT5(压缩比:1/4,Alpha通道还原较好):

参考:

http://msdn.microsoft.com/en-us/library/windows/desktop/bb943990(v=vs.85).aspx
http://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_texture_compression.txt
http://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_texture_compression_s3tc.txt

OpenGL 加载DDS文件(压缩纹理)的更多相关文章

  1. openGL加载obj文件+绘制大脑表层+高亮染色

    绘制大脑表层并高亮染色的工作是以openGL加载obj文件为基础的,这里是我们用到的原始程序:只能加载一个obj文件的demo. 然而,一个完整的大脑表层是由很多分区组成的,因此我们的程序需要支持两个 ...

  2. 如何在SCENEKIT使用SWIFT RUNTIME动态加载COLLADA文件

    问题:今天接到一个项目,负责弄需求的美眉跟我讲能不能做一个原型能够加载Collada文件,流程如下: 用户用app下载Collada 压缩包(如内购项目) 压缩包解压 展示Collada文件里的内容 ...

  3. Android系统加载Apk文件的时机和流程分析(1)--Android 4.4.4 r1的源码

    本文博客地址:https://blog.csdn.net/QQ1084283172/article/details/80982869 Android系统在启动时安装应用程序的过程,这些应用程序安装好之 ...

  4. webpack模块加载css文件及图片地址

    webpack支持css文件加载并打包,只需安装相应加载器并在配置文件中配置 . 加载的css文件内容会与该模块里的js内容混合封装,这样做的好处是一个js文件包含了所有的css与js内容,有效减少了 ...

  5. 速战速决 (5) - PHP: 动态地创建属性和方法, 对象的复制, 对象的比较, 加载指定的文件, 自动加载类文件, 命名空间

    [源码下载] 速战速决 (5) - PHP: 动态地创建属性和方法, 对象的复制, 对象的比较, 加载指定的文件, 自动加载类文件, 命名空间 作者:webabcd 介绍速战速决 之 PHP 动态地创 ...

  6. Java提高篇——JVM加载class文件的原理机制

    在面试java工程师的时候,这道题经常被问到,故需特别注意. 1.JVM 简介 JVM 是我们Javaer 的最基本功底了,刚开始学Java 的时候,一般都是从“Hello World ”开始的,然后 ...

  7. assets 加载资源文件

    引用:http://abc20899.iteye.com/blog/1096620 1.获取资源的输入流 资源文件 sample.txt 位于 $PROJECT_HOME/assets/ 目录下,可以 ...

  8. 动态加载js文件

    由于最近在弄echarts,关于地图类的效果,但是全国地图整体的js文件太大了,加载很耗费资源,所以要根据不同省份加载不同地区的js地图, 于是就想的比较简单, var script = docume ...

  9. spring入门(二)【加载properties文件】

    在开发过程当中需要用到配置信息,这些信息不能进行硬编码,这时配置文件是一个比较好的方式,java提供了properties格式的文件,以键值对的方式保存信息,在读取的时候通过键获得键对应的值,spri ...

随机推荐

  1. 20145234黄斐《Java程序设计》实验一—Java开发环境的熟悉(Linux + Eclipse)

    实验步骤 由于实验时间比较紧张,这里只有最终结果的截图 (一)命令行下Java程序开发 (二)Eclipse下Java程序开发.调试 (三)练习 实现求正整数1-N之间所有质数的功能,并进行测试 实验 ...

  2. SublimeText 改变 tab的距离

    view -> Indentation -> Tab width ……

  3. 创龙OMAPL138的SPI FLASH读写

    1. 目前最大的疑问是OMAPL138和DSP6748的DSP部分是完全一样的吗(虽然知道芯片完全是引脚兼容的)?因此现在使用OMAPL138的DSP内核去读写一下外部的SPI FLASH芯片,先看下 ...

  4. 车牌,车架号,VIN码毫秒识别技术,汽车后市场的春天到来了

    vin码(车架号)识别运用 不仅在制造.销售.保养.保险.车辆评估.交易环节会需要录入汽车的VIN码,在交通事故处理中,作为汽车身份唯一识别码,VIN码是处理事故的执法人员必须要记录的信息之一.随着汽 ...

  5. 关于Eclipse在servlet中连接数据库时出现驱动加载失败的解决

    问题:在队友发来的项目中想将他获取到的数据通过数据库储存,出现驱动加载失败问题 解决:首先百度了下相关情况,大多数都是说下载mysql-connector-java-5.1.39-bin.jar包,然 ...

  6. selenium、unittest——POM框架并出报告

    学习隔壁大神的POM框架,结合自己的用例进行修改整理并执行,操作遇到的主要问题是如何分布的写各个模块并统一运行,每个文件夹想要import里面的模块需要有__init__模块 POM主要分为三个部分, ...

  7. halcon学习相关资料(转载)

    https://blog.csdn.net/maweifei/article/details/78162581 论坛.培训 halcon学习网:http://www.ihalcon.com/ 鸟叔机器 ...

  8. Vue学习计划基础笔记(三)-class与style绑定,条件渲染和列表渲染

    Class与style绑定.条件渲染和列表渲染 目标: 熟练使用class与style绑定的多种方式 熟悉v-if与v-for的用法,以及v-if和v-for一起使用的注意事项 class与style ...

  9. phpcms单页顶级栏目默认打开第一个子栏目方法

    首先phpcms单页如过下面有子栏目,那么当前栏目是不能被编辑内容的,且访问后是没有内容的,首先不知道这是不是产品设计的一个缺陷,但是在使用过程中确实在后台也没有找到其他的对应解决办法,刚好在某QQ群 ...

  10. 从零开始的Python学习Episode 11——装饰器

    装饰器 装饰器是用来处理其他函数的函数,主要作用是在不修改原有函数的情况下添加新的功能,装饰器的返回值也是一个函数对象. 简单的装饰器 import time def show_time(f): de ...