zoukankan      html  css  js  c++  java
  • OpenGL 加载DDS文件(压缩纹理)

    想必很多人都见过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
    ");
            return 0;
        }
    
        if (header->Header.dwSize != 124) {
            printf("bad header size
    ");
            return 0;
        }
    
        if (!(header->Header.dwFlags & DDSD_LINEARSIZE)) {
            printf("bad file type
    ");
            return 0;
        }
    
        if (!(header->Header.ddspf.dwFlags & DDPF_FOURCC)) {
            printf("bad pixel format
    ");
            return 0;
        }
    
        compressFormat = header->Header.ddspf.dwFourCC;
    
        if (compressFormat != D3DFMT_DXT1 &&
            compressFormat != D3DFMT_DXT3 &&
            compressFormat != D3DFMT_DXT5) {
                printf("bad compress format
    ");
                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

    以下是文件大小对比:

    以下是图像质量对比:

    原图(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
  • 相关阅读:
    [转]: 浅谈Java中的equals和==
    易忘易混的java基本概念
    mysql查看锁表锁进程
    [转] Python 包管理工具解惑
    双网卡单网关的路由问题
    [转]火狐 SSL 收到了一个弱临时 Diffie-Hellman 密钥
    Linux中如何进入减号开头的目录中
    zabbix的一点记录
    从图形界面配置zabbix
    调用API自动配置zabbix version 3.0
  • 原文地址:https://www.cnblogs.com/crsky/p/6691611.html
Copyright © 2011-2022 走看看