废墟鱼池

我的游戏开发生涯就这样开始了吗?

« 上一篇: Bump Mapping概述(转载) 下一篇: 日版存档变美版 PS2各版存档转换详解 »
游不远的鱼 @ 2005-12-23 21:51

   纹理压缩,也有称之为材质压缩的,不过笔者喜欢称之为纹理压缩。因为这种技术实际上压缩的是没有往3D模型上贴图之前的图片,还不能称之为材质。 

    首先我们先来理解一下纹理压缩技术。说得直白一些,所谓纹理压缩就是将预备贴到3D模型上的贴图进行数据压缩,从而节省它们所占用的显存空间容量。我们都知道,虚拟的3D环境或者是模型能够在电脑屏幕被我们看到,除了无数的点、线、面以外,最重要的是这些模型上面都被贴上了贴图。

 

   仅有骨架,3D图形是远远不能满足人们视觉要求的

    举一个现实中最形象的例子就是扎风筝。最开始我们用竹篾扎成的骨架仅仅是模型的立体几何构成的部分,后来在竹篾附上的丝绸或者是纸带都相当于3D模型的贴图。

 

披上“虎皮”,就有点老虎的味道了

 

每个多边形都有一个平面贴图覆盖其上

    这些贴图都将作为一种资源文件被存储在相应的文件夹中,需要用到的时候会由程序调用,然后存储到显存中,通过显示芯片的运算贴到3D模型上。

    现在的问题就来了,图片在被调用的时候,将耗费巨大的显卡资源。因为如果在实时的3D程序中,需要完成的贴图数量是非常巨大的,一个场景可能会同时需要调用很多个贴图文件。

 

一张香蕉的贴图,左边是原图,右边是压缩后的贴图

    那么我们就来算一个帐。现在显卡支持的最大贴图文件尺寸是4096×4096像素。如果按照32位色来存储,我们可以轻易的算得一个贴图文件的大小是:4096×4096×32=536,870,912bit,也就是67,108,864Byte,合算是67MB。这意味着什么?这意味着256MB的显存也只能放上不到4张贴图。

 

放大8倍的比较,左为原图,右为压缩后图片,后者存在细节损失

    这样的话,再大的显存也是不够用的。于是就引入了我们今天的主题:纹理压缩。纹理压缩的原理就是将这些贴图进行压缩后存储进显存,这样就能大大的减小对显存容量的依赖。

    最开始的纹理压缩技术并不是微软在DirectX中提出的,这种技术的渊源要追溯到5年以前了。最开始采用纹理压缩技术是S3在OpenGL中首先提出的,最初的名称叫做S3TC。

    到了1999年,微软吸纳了这种做法,在他们的DirectX中加入了纹理压缩技术,并且取名为DXTC(DirectX Texture Compression)。 

 

S3TC技术的原理

    博采众长的微软在这一步走的非常成功,他们的DXTC技术得到了游戏开发者的肯定,也被显示芯片厂商所支持。

    值得提醒的是,纹理压缩的技术虽然看似简单,但是将压缩后的数据存储进显存并不是终结。重要的是需要用显示芯片把这些压缩后的数据进行解压缩,再经过运算贴图到3D模型上。这个过程就需要显示芯片在硬件上支持了,如果不支持,对于压缩后的数据无法识别,照样不能完成纹理压缩,也不能发挥出纹理压缩的优势。

    看到这里,可能已经有一些朋友对纹理压缩有了一定的认识,但是如果简单的认为纹理压缩就是将BMP位图压成JPG一类的东西存储进显存里,然后再解开的话那就错了。

    纹理压缩应用的算法由于涉及硬件复杂度、解压效率、色彩空间变换等等技术问题,要比单纯的压缩图片复杂的多了。就拿我们刚刚提到的DXTC来讲,这其中就包含着5种不同的算法,DXT1一直到DXT5,这5种不同的算法是根据5种不同的情况来使用的。

    纹理压缩技术让游戏开发人员能够轻易的在较小的模型上贴上尺寸较大的纹理贴图,这样就能够让游戏更加逼真。微软的DXTC技术能够对纹理进行6:1的压缩,这就意味着原来一张贴图文件原大6MB,经压缩后存储进显存仅仅为1MB了,这样就能够在同样的显存容量中存储更多的贴图了。

    然而,DXTC纹理压缩技术虽然能够带来性能上的提升,但是这种压缩技术却是一种有损压缩,这也就意味着在压缩→存储→解压的过程中势必会对细节有一定的损失,但是这个损失的幅度并不是很大,所以人们也就接受了这种折中的做法。DXTC技术得到了充分而广泛的应用,几乎所有游戏都采用了这种技术。

    时过境迁,这种技术给人们带来方便和性能提升的同时也给3D程序开发埋下了隐患。

    normal mapping技术是时下3d游戏中非常流行的贴图技术,它用很少的多边形就可以表现非常高的表面纹理细节。由于法线贴图RGB通道中存贮的是法线向量,在使用传统的压缩算法的时候就遇到了麻烦。

 

采用色彩通道存储法线向量

 

法线贴图的生成过程

 

Doom3中的一张纹理贴图和这张贴图对应的法线贴图    

    法线贴图技术的诞生要比纹理压缩技术晚,这就带来了另外一个问题——纹理压缩的过程中会损失掉一些细节,造成法线贴图的失真,从而导致最后的效果出现误差。

    产生这种结果的根本原因还是纹理压缩技术。因为作为一幅贴图来讲,其中绝大部分信息记录的都是每个点的颜色,如果要对纹理进行压缩的话,势必损失掉一些颜色的信息,这样的损失对于普通的贴图来讲影响是非常小的,因为玩家绝对不会去在意具体的一个点的颜色是否和原来的纹理保持了一致。

    但是,对于法线贴图就不是这样了,因为法线贴图中每个点的颜色反映的是该点法线的角度,也就是该点的凹凸的信息,如果这些信息丢失了,带来的结果就会糟糕的多——阴影失真是小,甚至可能造成贴图错误,因此,3Dc技术应运而生。

 

局部未压缩

 

DXTC压缩

 


   那么,如何解决法线贴图的压缩的问题呢?如何保证法线贴图在压缩的过程中失真较小而且能保证较大的压缩比呢?ATI的最新3Dc技术提供了较为满意的答案……

    3Dc压缩算法是一种基于“块”的压缩方式。什么叫做基于“块”呢?就是指压缩的过程中将要压缩的内容进行分块,再对每一个单独的“块”进行压缩。

    在3Dc技术中,ATI选择了将一个法线贴图分成若干个4×4像素(纹素)的块,每个块拥有16个像素。为什么这样分,这其中也是有一定的道理的。

    因为作为一个法线贴图来讲,虽然每个点表示了相应位置的凹凸情况。但是,一个平面的凹凸通常不会太细,即使有小尺寸的凹凸情况,也不会比贴图上4个像素还要小。所以ATI选择了以4×4个像素为一个基本的单元,然后对这个单元内的信息进行压缩。

 

先将一个法线贴图分成很多4*4像素的块

    接下来我们面对的就是一个4×4像素的小的贴图了,这上面的16个点中分别以红、绿、蓝3个通道的颜色值记录了该点法线的方向,而这个方向恰恰就是该点的凹凸的情况的一个最直观的表达的形式。

 

放大看每个独立的“块”

 

提取其中任意的一个“块”

    接下来,3Dc技术将这16个点的颜色值中X项和Y项独立提取出来,分成两个4×4的矩阵,然后再针对每个矩阵进行压缩。

    由于法线向量只有方向没有大小,所以只需要x,y2项即可确定。我们就拿X项作为例子来讲一讲压缩的最底层算法。

    现在我们已经得到了一个由16个数组成的矩阵,或者说是数列也可以,我们就不难从中提取出这16个数中的最大值和最小值,先暂时存储起来。

 

将X项分量的值统计出来

    在得到了最大值和最小值以后,我们接下来的步骤就是要进行插值运算了。所谓插值运算,就是在这两个数之间插入一些经处理器生成的数值。具体到3Dc算法中,他们采用了在这最大值和最小值之间等分7份,然后将中间的6个值插进刚刚统计得来的最大值和最小值之间。

 

将均分得来的值插到最大值和最小值之间

    举个简单的例子,例如我们上面的这张图,现在经过统计,得到了最大值是240,最小值是128。我们就不难算出它们的差是112,将这112分成7份,每份就是16。

    所以我们就可以算出这8个数分别为:128、144、160、176、192、208、224、240,这其中除了最小值128和最大值240,剩下的6个数都是经计算得来的。(在实际计算中都是使用的2进制)

    有了插值运算的基础,我们接下来就不难理解其中压缩的实现方法了。因为我们在记录数据的时候就能够摒弃原来的16个数了,转而使用两个数(最大值和最小值)就能表示了,两个数自然要比16个数占的空间要小,这也就是压缩的意义所在。

    当然,这个过程中还有特别重要的一点就是将原数据和插值后的数据进行比较,从而让计算机知道那些不是最大值也不是最小值的点,究竟应该取这8个数值中的哪一个。 

    再举个简单的例子,例如,在这个4×4的块中,其中第2行第3个像素的X项数值为154,经过比较,我们就将它本身的154舍掉,我们用插值中的156来代替它。

    这些信息也同样需要进行记录,而且这些信息占用的空间相对于具体的数据还要大,因为具体的数据仅仅需要记录那两个最大值和最小值就可以了。

    现在的问题就是我们如何来记录每个点的数值应该取插值得来的8个数值中的哪个数呢?最简单的就是用序号表示了。例如前面我们刚刚举的例子,第2行第3个像素采用了156,也就是8个数中的第3个,我们就用“2”来表示(最小的序号用0,最大的序号用7)。

    而表示序号的时候最大不过取到7,所以在存储的时候就大大的减少了占用的空间,仅仅使用一个3bit的二进制空间就能正好放下。

 

3Dc算法演示

    同理可得到Y项

    现在我们就可以通过计算,来看看3Dc技术究竟能够带来多大的压缩比例了: 

    我们仍然举一个4×4像素的块的情况,首先我们来算算在没有压缩之前记录这16个点上的信息所需要的空间,由于未压缩之前采用的是32bit的标准位图来存储的信息,RGB等3个色彩通道的颜色值分别表示X、Y、Z的分量值,原来用作记录Alpha通道信息的通道则被浪费了,这样的话这16个点每个点都需要4个8bit的空间来记录相应的信息,总的占用空间就是4×8×16=512bit。

 

采用未压缩格式需要512bit

    那么压缩以后呢?每个点需要分别用3bit记录X分量和Y分量的数值的编号,然后再分别开辟两个空间记录这16个点中的两个分量的最大值以及最小值,这么算起来的话总共需要的空间就是 6×16+8×4=128bit。

 

压缩后仅仅需要128bit

    前后一对比,我们就能看出,3Dc技术的压缩比例是4:1

    我们看到,在3Dc算法中其实是有一定的失真的,这一点也不必隐晦,因为如果在一个取样范围较大的空间实现压缩的话,失真是在所难免的,这16个点中每一个色彩通道的取值范围均有256个,3Dc算法仅仅使用了8个数来表示这些值,其中就有可能舍掉了248个数。

    但是由于我们采用的8个数值在这个空间中是平均分布的,而且每个值都用了和原数据最为接近的一个值来表示,所以每个数值失真的幅度就被控制在了[0,16]的闭区间上。

 

3Dc算法让法线贴图保持了较小的失真

    当然这个范围还是一个变化的范围,每个4×4的块中失真幅度的大小还和这个块中的最大值以及最小值有关。就像我们前面举的例子,在最小值为128,最大值为240的时候,失真幅度的取值范围就被限制在了[0,7]的闭区间上。

    显然,随着每个块的具体情况不同,3Dc算法的失真幅度的可能性也是不断变化的,而且由于实际情况中在一个4×4的块中两个极值的差通常会比较接近,所以实际应用中3Dc的失真的影响也会比较小。

    在3Dc算法中所需要做的步骤一共有以下几点: 

 

● CPU需要做

    将法线贴图分块 → 分离其中的X分量和Y分量 → 统计每个分量上的最大值和最小值 → 计算得到中间的插值 → 统计每个值的大小,确定每个值应取的新值 → 用3bit的值存储每个点上的新值应该取第几个数 → 将16个3bit的值和最大值最小值送进显存,

 

● 显示芯片需要做的步骤

    从显存读出数据 → 经显示芯片运算,根据最大值和最小值算出中间的插值 → 根据每个点的序号为每个点找出新值,重新生成解压后的法线贴图 → 接下来的操作就和普通的贴图过程一致了。

 

    我们可以看到,这个算法的具体操作过程非常简单,这些步骤在通过CPU和显示芯片运算的过程中耗费的资源相对于其他的步骤来讲比较小,所以CPU和显示芯片就能够在更短的时间内完成更多的操作,从而提升了效率,反映在用户的直观感受上就是看到了3D效果但是显示的帧速率并没有太大的降低。

    3Dc技术现在已经被MS纳入Dx9.0c作为一种材质压缩技术标准了,nvidia在新一代显卡中已经加入了对3Dc的支持,相信以后支持3Dc的游戏会越来越多



评论 / 个人网页 / 扔小纸条
*昵称

已经注册过? 请登录

Email
网址
*评论
 


 
日历
网志分类
· 所有网志
· 日记
· 学习
· 软件技术
· 杂谈
· 临时
· 秘密
· 好玩东东
· 珍藏
· 游戏
· 图形学和游戏开发
站内搜索
友情链接
· 我的歪酷 非非共享界
· Friday
· Fishrot的相册
· Fishrot的网站

订阅 RSS

0038535

歪酷博客

本模版系 歪酷博客Zazamu Studio 授权使用 请尊重知识产权