OpenGL 加載DDS文件(壓縮紋理)

想必不少人都見過DDS這種文件,它是一個「圖片文件」,若是你安裝了某些看圖軟件,你能夠直接雙擊打開它來進行預覽。windows

那麼,這種DDS文件和咱們常見的TGA/PNG之類的文件有何不一樣呢?ide

DDS和TGA/PNG/JPG之類的「圖片文件」 同樣,支持「壓縮」,減小磁盤空間佔用(把文件變小)。函數

一般咱們要加載一個TGA或者PNG文件到OpenGL的時候,都要先把文件數據還原成RGB格式的像素數據,而後用glTexImage2D把像素數據傳到顯存。這個過程至關於「解壓」,這一般很是消耗CPU資源,速度較慢。工具

可是DDS的壓縮數據不須要「解壓」就能直接傳到顯存,並且傳到顯存以後也不會解壓,這極大減小了顯存的使用量,而且提升了紋理加載速度,有絕對的優點。咱們只須要讀取好壓縮數據,而後使用glCompressedTexImage2D(代替glTexImage2D)就能夠直接把壓縮數據傳到顯存,完成加載。oop

DDS能夠保存許多種格式的像素數據,這裏只講最經常使用的3種(DXT一、DXT三、DXT5)。ui

* 固然DDS文件也能存儲不壓縮的像素數據。spa

爲了在OpenGL中使用DDS壓縮紋理(下文簡稱壓縮紋理),咱們須要一下2個OpenGL擴展:code

GL_ARB_texture_compression

提供函數 「glCompressedTexImage2D」orm

GL_EXT_texture_compression_s3tc

提供如下格式的壓縮紋理支持:
GL_COMPRESSED_RGB_S3TC_DXT1_EXT
GL_COMPRESSED_RGBA_S3TC_DXT3_EXT
GL_COMPRESSED_RGBA_S3TC_DXT5_EXTblog

 

完整的加載過程代碼:

#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

 

如下是文件大小對比:

如下是圖像質量對比:

原圖(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).aspxhttp://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_texture_compression.txthttp://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_texture_compression_s3tc.txt
相關文章
相關標籤/搜索