纹理压缩

在OpenGL中使用纹理压缩

OpenGL 2008-07-14 18:08:45 阅读25 评论0   字号: 订阅

 纹理压缩技术已经广泛应用在各种3D游戏之中,它们包括:DXTC(Direct X Texture Compress,DirectX纹理压缩,以S3TC为基础)、S3TC(S3 Texture Compress,S3纹理压缩,仅支持S3显卡)、VTC(Volume Texture Compression,体积纹理压缩)、PTC(Palletized Texture Compression,并行纹理压缩)、VQTC(Vector-Quantization Texture Compression,向量纹理压缩)、YAB NCTC(Narrow Channel Texture compression,狭窄通道纹理压缩)、FXT 1。下面,我为各位介绍OpenGL中使用纹理压缩的两种主要方法:ARB纹理技术扩展和S3TC。

一、概述

  与其它压缩技术一样,ARB可以把高分辨率纹理放入未压缩前只能存储低分辨率纹理的空间,并提供渲染管道优化,特点有:

- 增加渲染速度

- 减低纹理内存需求

- 快速纹理下载到纹理内存

- 低磁盘存储空间需求和高速磁盘存取

纹理压缩通常分为纹理生成层和运行时间层二部分,纹理生成又分成两种方法:

- 通常方法:使用GL来压缩图像,存储到磁盘

- S3TC DDS文件格式:使用ISV的S3TC压缩工具来处理图像,在GL中载入DDS文件

运行时间层的主要工作分三步:

- 从磁盘载入压缩图像

- 上载压缩图像到GL

- 不压缩动态纹理

二、具体过程

  最理想的压缩纹理技术是在游戏的开头画面预压缩所有的纹理,减少磁盘存储需要,加速纹理载入速度。下面是用两种不同的方法处理纹理位图,第一种方法使用OpenGL压缩,第二种使用直接支持OpenGL的ISV S3TC文件格式。

ARB允许未压缩的纹理通过glTexImage2D呼叫来进行纹理压缩,并设置内部格式参数。图1的上半部分为常规方式,下半部分为S3TC扩展集,基本内部格式通过压缩后将变成压缩内部格式。

1、常规压缩

  使用代理纹理可以在压缩前指示欲压缩的纹理图像,然后再以glGetTexLevelParameteriv加上$#@60;pname$#@62;到GL_TEXTURE_COMPRESSED_ARB,来检查图像是否进行适当的压缩。如果纹理已经得到正确处理,会返回一个非零参量,若是纹理压缩出错,则变回相应的基本内部格式。

  使用普通压缩内部格式,查询OpenGL会自动选择内部格式作为$#@60;internalFormat$#@62;,具体过程是 鰃lGetTexLevelParameteriv再加上$#@60;pname$#@62;参数,变成GL_TEXTURE_INTERNAL_FORMAT。

  下一步,查询OpenGL会通过glGetTexLevelParameteriv呼叫获得压缩纹理图像的容量,然后用$#@60;pname$#@62;生成GL_TEXTURE_COMPRESSED_IMAGE_SIZE_ARB,建立返回尺寸的缓冲区。glGetCompressedTexImageARB呼叫可取得压缩纹理数据,再用$#@60;img$#@62;取得缓冲区地址。压缩纹理到磁盘后,OpenGL会把下列属性加入到压缩纹理本身之中,以备运行时间层调用,属性包括:

- 缓冲区容量

- 压缩内部格式

- 宽度

- 高度

- 边界$#@60;border$#@62;(非S3TC)

- 深度$#@60;depth$#@62;(3D纹理和非S3TC技术)

  如果使用S3TC,压缩内部格式完成时,$#@60;border$#@62;将变设置为零。这意味着$#@60;border$#@62;不为零会导致“glCompressedTexImage2DARB”发生“INVALID_OPERATION(内部操作)”错误。若是图像仅适用于2D纹理,$#@60;depth$#@62;与S3TC格式无关,一旦$#@60;internalFormat$#@62;(内部格式)变成了S3TC,“glCompressedTexImage1DARB”和“glCompressedTexImage3DARB”会生成“INVALID_ENUM”错误。

  在另一平台使用压缩纹理,压缩内部格式不属于标准的话,必须让平台支持特殊的格式。比如用

“glGetIntegerv”呼叫加上“GL_NUM_COMPRESSED_TEXTURE_FORMATS_ARB”和“GL_COMPRESSED_TEXTURE_FORMATS_ARB”。下面是一段支持压缩内部格式的代码:

GLint * compressed_format;

GLint num_compressed_format;

glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS_ARB, &num_compressed_format);

compressed_format = (GLint*)malloc(num_compressed_format * sizeof(GLint));

glGetIntegerv(GL_COMPRESSED_TEXTURE_FORMATS_ARB, compressed_format);

  S3TC无须列出所支持的格式,也不用特殊的扩展集,因此S3TC不提供“GL_COMPRESSED_RGBA_S3TC_DXT1_EXT”格式,所有变化都会被自动认成是“正常”的RGBA格式。

随后的语句概括了压缩未压缩图像和把它存储进硬盘的过程:

glBindTexture(GL_TEXTURE_2D, compressed_decal_map);

glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGB_ARB, width, height,

0, GL_BGR_EXT, GL_UNSIGNED_BYTE, pixels);

glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_COMPRESSED_ARB, &compressed);

/* if the compression has been successful */

if (compressed == GL_TRUE)

{

glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT,

&internalformat);

glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_COMPRESSED_IMAGE_SIZE_ARB,

&compressed_size);

img = (unsigned char *)malloc(compressed_size * sizeof(unsigned char));

glGetCompressedTexImageARB(GL_TEXTURE_2D, 0, img);

SaveTexture(width, height, compressed_size, img, internalFormat, 0);

}

  有多种方法可以评估压缩扩展集的画质,比如:图像质量参数RED_BITS, GREEN_BITS等,或压缩纹理映象,或用glGetTexImage呼叫存取非压缩图像缓冲区。

2、S3TC DDS文件格式

  ISV工具(如:S3的Adobe PhotoShop Plug-in插件和微软DirectX的Dxtex)可以把普通文件格式转换成dds文件。故名思义,DDS(Direct Draw Surface,直接绘画表面)的基本操作是把内存的数据通过DirectX DDS接口下载到硬盘。DDS文件读取程度包含在DirectX的ddraw.h中,还可以得到DDSURFACEDESC2定义。此外,DirectX的架构与OpenGL不同,其屏幕原始坐标也不一样,DirectX的原始屏幕坐标位于左上角,OpenGL则位于左下角。在转换图像或纹理调整之前,必须改变纹理的垂直方向。

下面是一段DDS读取程序的源码:

#include $#@60;ddraw.h$#@62;

gliGenericImage *

ReadDDSFile(const char *filename, int * bufsize, int * numMipmaps)

{

gliGenericImage *genericImage;

DDSURFACEDESC2 ddsd;

char filecode[ 4];

FILE *fp;

/* try to open the file */

fp = fopen(filename, "rb");

if (fp == NULL)

return NULL;

/* verify the type of file */

fread(filecode, 1, 4, fp);

if (strncmp(filecode, "DDS ", 4) != 0) {

fclose(fp);

return NULL;

}

/* get the surface desc */

fread(&ddsd, sizeof(ddsd), 1, fp);

genericImage = (gliGenericImage*) malloc(sizeof(gliGenericImage));

memset(genericImage,0,sizeof(gliGenericImage));

/* how big is it going to be including all mipmaps? */

*bufsize = ddsd.dwMipMapCount $#@62; 1 ? ddsd.dwLinearSize * 2 : ddsd.dwLinearSize;

genericImage-$#@62;pixels = (unsigned char*)malloc(*bufsize * sizeof(unsigned char));

fread(genericImage-$#@62;pixels, 1, *bufsize, fp);

/* close the file pointer */

fclose(fp);

genericImage-$#@62;width = ddsd.dwWidth;

genericImage-$#@62;height = ddsd.dwHeight;

genericImage-$#@62;components = (ddsd.ddpfPixelFormat.dwFourCC == FOURCC_DXT1) ? 3 : 4;

*numMipmaps = ddsd.dwMipMapCount;

switch(ddsd.ddpfPixelFormat.dwFourCC)

{

case FOURCC_DXT1:

genericImage-$#@62;format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;

break;

case FOURCC_DXT3:

genericImage-$#@62;format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;

break;

case FOURCC_DXT5:

genericImage-$#@62;format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;

break;

default:

free(genericImage-$#@62;pixels);

free(genericImage);

return NULL;

}

/* return data */

return genericImage;

}

  当DDS文件被看成是MIP映射时,它会压缩所有的映射图像,此问题通常发生于GL在一段时间内取出一个映射的时候。DDS文件的缓冲区读取不能直接由GL控制,你必须精确计算每一个MIP映射在缓冲区的偏移量,传递正确的数据地址和属性给

glCompressedTexImage2DARB,代码如下:

/* load the .dds file */

ddsimage = ReadDDSFile("flowers.dds",&ddsbufsize,&numMipmaps);

height = ddsimage-$#@62;height;

width = ddsimage-$#@62;width;

offset = 0;

div = (ddsimage-$#@62;format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) ? 1 : 0;

glBindTexture(GL_TEXTURE_2D, dds_compressed_decal_map);

/* load the mipmaps */

for (i = 0; i $#@60; numMipmaps && (width $#@62; 1 && height $#@62; 1); ++i)

{

size = width * height;

size $#@62;$#@62;= div;

glCompressedTexImage2DARB(GL_TEXTURE_2D, i, ddsimage-$#@62;format, width, height,

0, size, ddsimage-$#@62;pixels + offset);

GLErrorReport();

offset += size;

width $#@62;$#@62;= 1;

height $#@62;$#@62;= 1;

}

  最后,大家会注意到微软的DirectX SDK DXTex工具提供了把立体位图(带MIP映射)压缩到单个DDS文件的能力。因为DirectX围绕一个立体位图需要的所有数据而设计,压缩非常容易实现,而且DDS文件阅读器还能按照位图的不同自由定制。如果一个立体位图与DirectX相匹配,那么,它也能够用在OpenGL身上。

原文地址:https://www.cnblogs.com/lizhengjin/p/1917409.html