您的位置:首页 > 运维架构

OpenGL 加载DDS文件(压缩纹理)

2017-04-11 00:00 1676 查看
想必很多人都见过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[11];
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 0;
}

if (header->Header.dwSize != 124) {
printf("bad header size\n");
return 0;
}

if (!(header->Header.dwFlags & DDSD_LINEARSIZE)) {
printf("bad file type\n");
return 0;
}

if (!(header->Header.ddspf.dwFlags & DDPF_FOURCC)) {
printf("bad pixel format\n");
return 0;
}

compressFormat = header->Header.ddspf.dwFourCC;

if (compressFormat != D3DFMT_DXT1 &&
compressFormat != D3DFMT_DXT3 &&
compressFormat != D3DFMT_DXT5) {
printf("bad compress format\n");
return 0;
}

data = (GLvoid *)(header + 1);    // header data skipped

glGenTextures(1, &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, 0, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, header->Header.dwWidth, header->Header.dwHeight, 0, imageSize, data);
break;
case D3DFMT_DXT3:
imageSize = SIZE_OF_DXT2(header->Header.dwWidth, header->Header.dwHeight);
glCompressedTexImage2DARB(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, header->Header.dwWidth, header->Header.dwHeight, 0, imageSize, data);
break;
case D3DFMT_DXT5:
imageSize = SIZE_OF_DXT2(header->Header.dwWidth, header->Header.dwHeight);
glCompressedTexImage2DARB(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, header->Header.dwWidth, header->Header.dwHeight, 0, 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, 0);

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, 0, SEEK_END);
size = ftell(fp);
fseek(fp, 0, SEEK_SET);

data = malloc(size);
if (!data) {
fclose(fp);
return;
}

if (fread(data, size, 1, fp) != 1) {
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(0, 0, 0, 0);
}

void display(void)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

RECT rc;
rc.left = 10;
rc.top = 10;
rc.right = rc.left + 1280;
rc.bottom = rc.top + 720;

glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, g_texnum);

glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glColor4f(1, 1, 1, 1);

glBegin(GL_QUADS);
glTexCoord2f(0, 0);
glVertex2f(rc.left, rc.top);
glTexCoord2f(1, 0);
glVertex2f(rc.right, rc.top);
glTexCoord2f(1, 1);
glVertex2f(rc.right, rc.bottom);
glTexCoord2f(0, 1);
glVertex2f(rc.left, rc.bottom);
glEnd();

glutSwapBuffers();
glutPostRedisplay();
}

void reshape(int width, int height)
{
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0, width, height, 0);
glMatrixMode(GL_MODELVIEW);

glViewport(0, 0, width, height);
}

int main(int argc, char **argv)
{
glutInitWindowPosition(200, 200);
glutInitWindowSize(10+1280+10, 10+720+10);
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
glutCreateWindow("OpenGL DDS");
init();
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutMainLoop();

return 0;
}


View Code

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

https://developer.nvidia.com/legacy-texture-tools http://pan.baidu.com/s/1pKKRL3P[/code] 
以下是文件大小对比:



以下是图像质量对比:

原图(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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: