OpenGL ES系列 之 深入 - 1:压缩纹理

 

纹理图片数据量巨大,一个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 : http://www.play3d.net
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 : http://www.play3d.net
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 : http://www.play3d.net
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
例子程序:
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值