Chinaunix首页 | 论坛 | 博客
  • 博客访问: 537983
  • 博文数量: 150
  • 博客积分: 5000
  • 博客等级: 大校
  • 技术积分: 1705
  • 用 户 组: 普通用户
  • 注册时间: 2008-11-11 23:29
文章分类

全部博文(150)

文章存档

2011年(9)

2010年(25)

2009年(94)

2008年(22)

我的朋友

分类:

2009-04-20 10:50:31

纹理图片数据量巨大,一个256x256 RGBA16的原始象素就有128KB之多,而一个实际应用中会用到巨量的纹理图片。这些数据都需要从硬盘或存储卡上读进内存,然后再上传到显存里。通过有损压缩,图片的文件尺寸可以急剧减小,512x512的真彩BMP有768KB,转成100%质量的JPG就只有235KB。

不过象JPG这种常见图象压缩格式对于多数应用的内存占用和显示总线带宽占用并没有直接的好处,因为还得解压缩成原始象素再传给显卡,而且还有加载时的解码计算负担。这是因为显卡的纹理解码硬件不理解JPG格式。所以,在没有显卡硬件支持的情况下,用压缩格式保存纹理没什么意义,特别是对于手持移动设备来说,解码象JPG这种复杂格式是很浪费电的。

考虑到现代游戏对纹理图片的严重依赖,及相应的对视频总线的巨大压力,硬件实时解压缩获得了广泛的支持,不过这个还没有一种格式获得多个厂家的支持。在 OpenGL ES里已经为此定义了一个标准接口glCompressedTexImage2D(..., format, ..., data),不过纹理数据的格式则没有标准,要参考厂商的SDK或文档获得format值。这也就意味着,使用了压缩纹理之后就不能跨平台了。对于 OpenGL ES应用来说可能问题不大,可以用不压缩纹理做为起点,然后针对不同硬件平台转换相应的优化版本。而对于标准OpenGL应用来说问题可能也不太大,因为真正值得关注的也就ATI vs. NVIDIA吧。

纹理压缩算法除了要求高压缩比、低失真之外,可随机读取任意一点及便于低成本硬件实现是最大的挑战。2700G支持的PowerVR Texture Compression,就是一种按照这种需求开发的高效率压缩算法。对于PVRTC算法及Texture Compression算法的演化,可以参考Simon Fenney题为"Texture Compression using Low-Frequency Signal Modulation"的论文,可以在PowerVR的GLES PCE SDK里找到。

PVRTC有两种压缩算法,512x512 RGB的原始象素768KB,PVRTC 4BPP压缩后为128KB,PVRTC 2BPP压缩后为64K。不过PVRTC 2BPP效果看上去明显比较差。

值得一提的是,Intel 2700G SDK里带有一个 PVRTextureTool.exe 可将BMP压缩成PVRTC格式,这个tool版本比较低是1.x的。在PowerVR SDK里有更新版本(3.0),新版本明显好用得多,可以处理alpha channel,支持OpenGL纹理格式等等。这个tool有命令行版本,也有GUI版本,而且还有一个库封装了算法和文件格式处理代码可用来制作自己的工具。PVRTC的纹理存储顺序是从上到下逐行保存的,而OpenGL ES定义纹理数据是从下到上逐行排列的,所以使用3.0版本并且在编码时勾上OpenGL选项是非常必要的。

根据实测,PVR PCE不支持带MipMap的PVRTC数据。可能设计者认为对于现有的240x320或480x640的屏幕来说,MipMap实在是意义不大吧。终于知道为什么PCE上跑得好好的程序在X51v上却显示不出纹理来了,原来WinCE是没有当前目录这个说法的,fopen ("t.pvr")会去"My Devices"目录里找这个文件,显然是找不到,把相应的.pvr文件放进去就好了。

2700G或PVR PCE SDK里都带用glext.h,里面定义了所需要的常量,主要有用的是GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG 等传给glCompressedTexImage2D()的format常量。PVRTextureTool生成的文件是.pvr格式的,其格式在SDK 文档里有详细的描述,甚至连struct都定义好了。

总的说来利用上压缩纹理还是相当简单而直接的,所以闲话少说,看代码吧:
主程序部份,提示一下,可以用't'轮换纹理图片,用'm'轮换贴图公式,其它也就是画了个带纹理的矩形而矣。主要的代码在 PVRTexture.upload()里。
  1 // 
2 // @ Project : PVRTC Example
3 // @ File Name : texturepvr.cpp
4 // @ Date : 2007-8-30
5 // @ Author : kongfu.yang
6 // @ Company :
7 // @ Copyright : 2007-8, Example source license. By keep this header comment,

you may use this source file in your project for non-commicial or commicial purpose.

8
9 #include "main.h"
10 #include "PVRTexture.h"
11
12 EGLWrapper * egl;
13 PVRTexture * g_tex = 0;
14
15 bool g_wireframe;
16 GLfixed g_rx, g_ry;
17 GLfixed zoom = Float2Fixed(1.0);
18
19 #define TEXTURE_COUNT 9
20 char * fileNames[TEXTURE_COUNT] = {
21 "yuquan4.pvr", "yuquan4mip.pvr", "yuquan4a.pvr", "yuquan4amip.pvr",
22 "yuquan4gl.pvr", "yuquan4mipgl.pvr", "yuquan4agl.pvr", "yuquan4amipgl.pvr",
23 "yuquan4mip_intel.pvr"
24 };
25
26 #define MAX_TEX_MODE 5
27 int modeIdx = 0;
28 GLfixed texModes[MAX_TEX_MODE] = {
29 GL_REPLACE, GL_MODULATE, GL_DECAL, GL_BLEND, GL_ADD
30 };
31 LPTSTR texModeNames[MAX_TEX_MODE] = {
32 L"GL_REPLACE", L"GL_MODULATE", L"GL_DECAL", L"GL_BLEND", L"GL_ADD"
33 };
34
35 void nextMode(void)
36 {
37 if ( ++modeIdx >= MAX_TEX_MODE ) modeIdx = 0;
38 glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, texModes[modeIdx]);
39 }
40
41 void nextTexture(void)
42 {
43 static int idx = 3;
44 if ( g_tex != 0 ) delete g_tex;
45 g_tex = new PVRTexture(fileNames[idx++]);
46 g_tex->upload();
47
48 if ( idx >= TEXTURE_COUNT ) idx = 0;
49
50 glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
51 glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
52 glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
53 glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
54
55 glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, texModes[modeIdx]);
56 }
57
58 void init(HWND hWnd, EGLWrapper *g_egl)
59 {
60 egl = g_egl;
61 egl->init(hWnd, 16, true);
62
63 glDisable(GL_LIGHTING);
64
65 glEnable(GL_TEXTURE_2D);
66 glEnable(GL_BLEND);
67
68 nextTexture();
69
70 glClearColorx(0, 0, Float2Fixed(1.0f), Float2Fixed(0.5f));
71 glColor4x(Float2Fixed(1.0f), 0, 0, Float2Fixed(0.5f));
72 }
73
74 GLfixed rect[] = { Float2Fixed(-1.0f), Float2Fixed(-1.0f),0,
75 Float2Fixed(1.0f), Float2Fixed(-1.0f), 0,
76 Float2Fixed(1.0f), Float2Fixed(1.0f), 0,
77
78 Float2Fixed(-1.0f), Float2Fixed(-1.0f),0,
79 Float2Fixed(1.0f), Float2Fixed(1.0f), 0,
80 Float2Fixed(-1.0f), Float2Fixed(1.0f) , 0
81 };
82 GLfixed rnormal[] = {
83 0, Float2Fixed(1.0f), 0,
84 0, Float2Fixed(1.0f), 0,
85 0, Float2Fixed(1.0f), 0,
86
87 0, Float2Fixed(1.0f), 0,
88 0, Float2Fixed(1.0f), 0,
89 0, Float2Fixed(1.0f), 0 };
90 GLfixed rectuv[] = {
91 Float2Fixed(0.0f), Float2Fixed(0.0f),
92 Float2Fixed(1.0f), Float2Fixed(0.0f),
93 Float2Fixed(1.0f), Float2Fixed(1.0f),
94
95 Float2Fixed(0.0f), Float2Fixed(0.0f),
96 Float2Fixed(1.0f), Float2Fixed(1.0f),
97 Float2Fixed(0.0f), Float2Fixed(1.0f)
98 };
99
100 void testPVRTC()
101 {
102 //glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, 128, 128 , 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, embTex);
103 glEnableClientState( GL_VERTEX_ARRAY );
104 //glEnableClientState( GL_NORMAL_ARRAY );
105 glEnableClientState( GL_TEXTURE_COORD_ARRAY );
106 glVertexPointer( 3, GL_FIXED, 0, rect );
107 //glNormalPointer( GL_FIXED, 0, rnormal);
108 glTexCoordPointer( 2, GL_FIXED, 0, rectuv );
109
110 glActiveTexture( g_tex->id );
111 glDrawArrays( GL_TRIANGLES, 0, 6);
112 }
113
114 void draw(HWND hWnd, HDC hdc)
115 {
116 if ( ! egl->isInitialized() ) return;
117
118 if ( egl->makeCurrent() )
119 {
120 glEnable(GL_DEPTH_TEST);
121 glDepthFunc(GL_LEQUAL);
122
123 glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
124 glPushMatrix();
125
126 //glRotatex( g_rx, Float2Fixed(1.0f), 0, 0 );
127 //glRotatex( g_ry, 0, Float2Fixed(1.0f), 0 );
128
129 glScalex( zoom, zoom, Float2Fixed(1.0f) );
130
131 // draw here
132 testPVRTC();
133
134 glPopMatrix();
135 egl->swapBuffers();
136 }
137
138 reportError();
139 }
140
141 bool msgHandler(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
142 {
143 TCHAR wbuf[1024];
144
145 switch (message)
146 {
147 case WM_CHAR:
148 switch(wParam){
149 case 'f':
150 case 'F':
151 g_wireframe = ! g_wireframe;
152 InvalidateRect(hWnd, 0, false);
153 break;
154 case 't':
155 nextTexture();
156 ZeroMemory( wbuf, 1024 );
157 MultiByteToWideChar(CP_ACP, 0, g_tex->fileName, strlen(g_tex->fileName), wbuf, 1024);
158 SetWindowText(hWnd, wbuf);
159 InvalidateRect(hWnd, 0, false);
160 break;
161 case 'm':
162 nextMode();
163 SetWindowText(hWnd, texModeNames[modeIdx]);
164 InvalidateRect(hWnd, 0, false);
165 break;
166 case '+':
167 // zoom in to exam mipmap
168 zoom += Float2Fixed(0.1);
169 InvalidateRect(hWnd, 0, false);
170 break;
171 case '-':
172 // zoom out to exam mipmap
173 zoom -= Float2Fixed(0.1);
174 InvalidateRect(hWnd, 0, false);
175 break;
176 default:
177 ;
178 }
179 break;
180 case WM_KEYDOWN:
181 switch(wParam)
182 {
183 case VK_UP:
184 g_rx = Float2Fixed(Fixed2Float(g_rx) + 10.0f);
185 InvalidateRect( hWnd, 0, false );
186 break;
187 case VK_DOWN:
188 g_rx = Float2Fixed(Fixed2Float(g_rx) - 10.0f);
189 InvalidateRect( hWnd, 0, false );
190 break;
191 case VK_LEFT:
192 g_ry = Float2Fixed(Fixed2Float(g_ry) + 10.0f);
193 InvalidateRect( hWnd, 0, false );
194 break;
195 case VK_RIGHT:
196 g_ry = Float2Fixed(Fixed2Float(g_ry) - 10.0f);
197 InvalidateRect( hWnd, 0, false );
198 break;
199 default:
200 ;
201 }
202 break;
203 default:
204 ;
205 }
206
207 return false;
208 }
我已经写了一个非常简单的类来封装PVRTC,包括读取文件,分析格式,定义成OpenGL ES纹理等。如下:
 1 // 
2 // @ Project : PVRTC Wrapper
3 // @ File Name : PVRTexture.h
4 // @ Date : 2007-8-30
5 // @ Author : kongfu.yang
6 // @ Company :
7 // @ Copyright : 2007-8, Example source license.

By keep this header comment, you may use this source file in your project for non-commicial or commicial purpose.

8
9 #ifndef PVR_TEXTURE_H
10 #define PVR_TEXTURE_H
11
12 #include "gles/gl.h"
13 #include "gles/glext.h"
14 #include "string.h"
15 #include "PVRTCFile.h"
16
17 extern void reportError(void);
18
19 class PVRTexture
20 {
21 public:
22 PVRTexture(char * fname){ fileName = _strdup(fname); id=0; data=0; size=0; }
23 ~PVRTexture(){ free(fileName); if ( data != 0 ) free(data); }
24
25 public:
26 char * fileName;
27 unsigned int id;
28
29 void upload(void)
30 {
31 load();
32 glGenTextures(1, &id);
33 glBindTexture(GL_TEXTURE_2D, id);
34 glCompressedTexImage2D(GL_TEXTURE_2D, levels, format, width, height, 0, size, data);
35 reportError();
36 }
37
38 protected:
39 int format; // .pvr format map to gles format
40 int width;
41 int height;
42 int levels; // mipmap levels
43
44 long size;
45 unsigned char * data;
46
47 void load(void)
48 {
49 FILE *fp = fopen(fileName, "rb");
50 PVR_Header h;
.pvr文件头部是可变长度的,其头部长度由第一个DWORD记录,这个长度已经将该DWORD自己计算在内了:
51 fread(& h.dwHeaderSize, sizeof(DWORD), 1, fp);
读取头部的剩余部分:
52 fread(& h.dwHeight, h.dwHeaderSize - sizeof(DWORD), 1, fp);
53
54 // format info
55 width = h.dwWidth;
56 height = h.dwHeight;
57 levels = h.dwMipMapCount;
58 size = h.dwTextureDataSize;
59
Flags由两部份组成,0x100以上是位掩码,记录制作时的选项,低位部分是格式代号:
60 h.dwpfFlags &= ~PVRTC_MIPMap; // redundant with levels?
61 h.dwpfFlags &= ~PVRTC_Twiddle; // todo : record?
62 h.dwpfFlags &= ~PVRTC_NormalMap; // todo : handle?
63
头部有一个dwAlphaBitMask字段,用来记录图片每个单元里有几个位是用来记录alpha信息的。
PVRTextureTool 1.x生成的纹理似乎总是不含alpha通道的,而3.x则可以控制是否生成。
目前如果有alpha通道,这个alpha bit总是1。
64 bool hasAlpha = h.dwAlphaBitMask != 0;
65 switch( h.dwpfFlags )
66 {
PVRTC4是用PVRTC 4BPP算法压缩的:
67 case PVRTC_PVRTC4:
如果纹理数据中有alpha通道,那就映射到GL_RGBA格式,否则映射到GL_RGB格式。
68 format = hasAlpha? GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG : GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG;
69 //if the texture contains alpha information, then use it.
70 break;
PVRTC2是用PVRTC 2BPP算法压缩的,压缩比更高,失真也更厉害:
71 case PVRTC_PVRTC2:
如果纹理数据中有alpha通道,那就映射到GL_RGBA格式,否则映射到GL_RGB格式。
72 format = hasAlpha? GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG : GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG;
73 //if the texture contains alpha information, then use it.
74 break;
75 // _GL format: the order of scan lines are reverted.
76 case PVRTC_PVRTC4_GL:
77 format = GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
78 break;
79 case PVRTC_PVRTC2_GL:
80 format = GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
81 break;
82 default:
83 // if not special compressed, why use PVRTC format?
84 // todo : mapping more PVR possible content to GLES internal format
唔,虽然写了个TODO,但是不知道啥时候才DO,所以如果需要的话自己先写吧,
写好了如果不介意公开的话, 发到论坛上或email我,我会补充到这个页面上的。
85 format = -1;
86 ;
87 }
88
89 data = (unsigned char *) malloc( h.dwTextureDataSize );
90 fread( data, h.dwTextureDataSize, 1, fp );
91 fclose(fp);
92 }
93 };
94
95 #endif // PVR_TEXTURE_H
文件格式相关的结构和常量在一个单独的文件里:
内容基本上是直接抄的SDK PVR文件格式说明文档的,所以应该不需要再解释什么了。
 1 // 
2 // @ Project : PVRTC Wrapper
3 // @ File Name : PVRTCFile.h
4 // @ Date : 2007-8-30
5 // @ Author : kongfu.yang
6 // @ Company :
7 // @ Copyright : 2007-8, Example source license.

By keep this header comment, you may use this source file in your project for non-commicial or commicial purpose.

8
9 #ifndef PVR_TC_FILE_H
10 #define PVR_TC_FILE_H
11
12 typedef struct PVR_Header_TAG
13 {
14 DWORD dwHeaderSize; /* size of the structure */
15 DWORD dwHeight; /* height of surface to be created */
16 DWORD dwWidth; /* width of input surface */
17 DWORD dwMipMapCount; /* Number of additional mipmap levels */
18 DWORD dwpfFlags; /* pixel format flags */
19 DWORD dwTextureDataSize; /* Total size in bytes */
20 DWORD dwBitCount; /* number of bits per pixel */
21 DWORD dwRBitMask; /* mask for red bit */
22 DWORD dwGBitMask; /* mask for green bits */
23 DWORD dwBBitMask; /* mask for blue bits */
24 DWORD dwAlphaBitMask; /* mask for alpha channel */
25 } PVR_Header;
26
27 #define PVRTC_ARGB_4444 0x00000000
28 #define PVRTC_ARGB_1555 0x00000001
29 #define PVRTC_RGB_565 0x00000002
30 #define PVRTC_RGB_555 0x00000003
31 #define PVRTC_RGB_888 0x00000004
32 #define PVRTC_ARGB_8888 0x00000005
33 #define PVRTC_ARGB_8332 0x00000006
34 #define PVRTC_I_8 0x00000007
35 #define PVRTC_AI_88 0x00000008
36 #define PVRTC_1_BPP 0x00000009
37 #define PVRTC_VY1UY0 0x0000000A
38 #define PVRTC_Y1VY0U 0x0000000B
39 #define PVRTC_PVRTC2 0x0000000C
40 #define PVRTC_PVRTC4 0x0000000D
41 #define PVRTC_RGBA_4444 0x00000010
42 #define PVRTC_RGBA_5551 0x00000011
43 #define PVRTC_RGBA_8888 0x00000012
44 #define PVRTC_PVRTC2_GL 0x00000018
45 #define PVRTC_PVRTC4_GL 0x00000019
46 #define PVRTC_MIPMap 0x00000100
47 #define PVRTC_Twiddle 0x00000200
48 #define PVRTC_NormalMap 0x00000400
49
50
51 #endif // PVR_TC_FILE_H
52
阅读(1545) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~