2013年6月26日 星期三

Mobile GPU大廠

 
目前移动市场的GPU主要有四大厂商系列:

1)Imagination Technologies的PowerVR SGX系列

    
代表型号:PowerVR SGX 535、PowerVR SGX 540、PowerVR SGX 543MP、PowerVR SGX 554MP等
代表作  :Apple iPhone全系
iPad全系,三星I9000、P3100等

2)Qualcomm(高通)的Adreno系列

    
代表型号:Adreno 200、Adreno 205、Adreno 220、Adreno 320等
代表作  :HTC G10、G14,小米1、2等

3)ARM的Mali系列

    
代表型号:Mali-400、Mali-T604等
代表作  :三星Galaxy SII、Galaxy SIII、Galaxy Note1、Galaxy Note2(亚版)等

4)nVIDIA(英伟达)的Tegra系列

    
代表型号:nVIDIA Tegra2、nVIDIA Tegra3等
代表作  :Google Nexus 7,HTC One X等



5)性能出众的“小众”产品——Vivante的GCxx
与PowerVR、Mali、Adreno等明星相比,Vivante公司的GC系列GPU核心就不太为人熟知了,就连市场份额比它少的Tegra系列都 比Vivante知名。去年让Vivante露一大脸的是华为旗下的海思K3V2,这颗号称“世界最快四核”的移动处理器在华为去年的Ascend D1四核、荣耀2四核以及今年初的Ascend D2、6.1寸超大屏的Mate上全面应用,其16核GPU实际上就源于Vivante的GC系列GPU。
.
华为的K3V2处理器使用的就是Vivante的GC4000核心
Vivante其实还是挺有资本的,创立于2004年,创始人好像还是个华裔,他们专注于移动GPU市场,2010年的时候就有超过40家授权单位,主要 客户有Marvell、飞思卡尔等,很多人不知道的是国内的瑞芯微电子的RK2918以及自主知识产权的国产明星“龙芯-2H”使用的也是Vivante 的GPU核心。 Vivante给人的感觉就是“小众”,好听点的说法是“Vivante虽然小众,但是性能出众”,难听点的说法是“Vivante的GPU性能出众,但 是市场小众”。只不过从市场统计上看,Vivante的GC系列在移动GPU的份额上仅次于PowerVR、Adreno以及Mali之后,2012年上 半年的全球份额是9.8%,比Tegra的2.5%份额要高得多。 说起来Tegra才是小众,他们是大众产品呢,只不过Vivante在消费级的智能手机和平板领域确实露面太少,给人的印象是“小众”。
主力产品:GC400、GC800、GC4000




大部份資料都在參考網站中,懶得轉貼了。

參考網站
http://m.mydrivers.com/newsview.aspx?id=266555&cid=1&p=4

http://m.mydrivers.com/newsview.aspx?id=266555&cid=1&p=5

http://m.mydrivers.com/newsview.aspx?id=266555&cid=1&p=6

http://m.mydrivers.com/newsview.aspx?id=266555&cid=1&p=7

http://m.mydrivers.com/newsview.aspx?id=266555&cid=1&p=8

http://www.cnblogs.com/luming1979/archive/2013/02/04/2891421.html

 

OpenGL基本瞭解(二十)(Texture compression)




貼圖是在 3D 場景中,增加真實性的一個重要的工具。就像一般的影像一樣,貼圖的大小愈大,它的圖像就愈精細。事實上,貼圖往往需要比一般的影像更大。因為,在 3D 場景中,觀察者可能會很靠近貼圖,使得貼圖需要放大很多倍,而造成模糊的現象。所以,一般來說,如果可能的話,貼圖愈大就愈好。

不過,貼圖是非常佔用記憶體空間的。現在的貼圖都是以不壓縮的形式存放在顯示記憶體中,而目前常用的貼圖格式是 16 bits、24 bits 和 32 bits。下表列出一些貼圖大小所佔用的空間:

貼圖大小16 bits16 bits mipmap24 bits24 bits mipmap32 bits32 bits mipmap
64x648 KB10.6 KB12 KB16 KB16 KB21 KB
256x256128 KB170 KB192 KB256 KB256 KB340 KB
1024x10242 MB2.6 MB3 MB4 MB4 MB5.3 MB



常用的圖像文件格式有BMP,TGA,JPG,GIF,PNG等;
不過象JPG這種常見圖像壓縮格式對於多數應用的內存佔用和顯示總線帶寬佔用並沒有直接的好處,因為還得解壓縮成原始像素再傳給顯卡,而且還有加載時的解碼計算負擔這是因為顯卡的紋理解碼硬件不理解JPG格式。所以,在沒有顯卡硬件支持的情況下,用壓縮格式保存紋理沒什麼意義,特別是對於手持移動設備來說,解碼象JPG這種複雜格式是很浪費電的。考慮到現代遊戲對紋理圖片的嚴重依賴,及相應的對視頻總線的巨大壓力,硬件實時解壓縮獲得了廣泛的支持,不過這個還沒有一種格式獲得多個廠家的支持。在OpenGL ES裡已經為此定義了一個標準接口glCompressedTexImage2D(..., format, ..., data),不過紋理數據的格式則沒有標準,要參考廠商的SDK或文檔獲得format值。這也就意味著,使用了壓縮紋理之後就不能跨平台了。



常用的紋理格式有R5G6B5,A4R4G4B4,A1R5G5B5,R8G8B8, A8R8G8B8等。
   
文件格式是圖像為了存儲信息而使用的對信息的特殊編碼方式,它存儲在磁盤中,或者內存中,但是並不能被GPU所識別,因為以向量計算見長的GPU對於這些複雜的計算無能為力。這些文件格式當被遊戲讀入後,還是需要經過CPU解壓成R5G6B5,A4R4G4B4,A1R5G5B5,R8G8B8, A8R8G8B8等像素格式,再傳送到GPU端進行使用。
    
 

紋理格式是能被GPU所識別的像素格式,能被快速尋址並採樣。舉個例子,DDS文件是遊戲開發中常用的文件格式,它內部可以包含A4R4G4B4的紋理格式,也可以包含A8R8G8B8的紋理格式,甚至可以包含DXT1的紋理格式。在這裡DDS文件有點容器的意味。
    
 

OpenGL ES 2.0支持以上提到的R5G6B5,A4R4G4B4,A1R5G5B5,R8G8B8,A8R8G8B8等紋理格式,其中 R5G6B5,A4R4G4B4,A1R5G5B5每個像素佔用2個字節(BYTE),R8G8B8每個像素佔用3個字節,A8R8G8B8每個像素佔用4個字節。

RGB565 ....這類的顏色資料,可參考http://en.wikipedia.org/wiki/High_color

   
對於一張512*512的紋理的話,R5G6B5格式的文件需要佔用512KB的容量,A8R8G8B8格式的文件需要佔用1MB的容量;如果是1024*1024的紋理,則各需要2M和4M的容量,這對於動輒需要幾十、幾百張甚至更多紋理的遊戲,上G容量的遊戲在移動平台上是不容易被接受的(當然,還是有1、2G的大作的,裡麵包含了幾千張的紋理)
現在一般的顯示卡上通常裝有 32MB 的顯示記憶體。如果每個貼圖都要 2MB 的話,即使不計 frame buffer 所佔用的空間,也只能使用 16 張貼圖。這顯然是不可接受的。所以,現在的遊戲通常無法使用很大的貼圖。

然而,在儲存一般的影像的時候,通常會使用某些壓縮方式。現在常見的 JPEG 壓縮,可以達到 1:6 甚至 1:12 的壓縮比。如果把類似的壓縮方式應用在貼圖上,不就可以大量減少貼圖所用的空間了嗎?

不幸的是,一般的影像壓縮方式,是沒有辦法用在貼圖上面的。因為,顯示晶片在存取貼圖時,是一種「隨機存取」的動作。也就是說,顯示晶片通常會需要以任意 的順序存取貼圖裡的資料。一般的壓縮方式如 JPEG,都利用了 variable length 的 coding,簡單的說,它們必需以一定的順序才能解開。因此,不能用這種方式來壓縮貼圖。

一種壓縮方式,是改變顏色空間。例如,3dfx 的 YAB 格式,就是一種不同的顏色空間。利用 YAB,每個像點只需要 8 bits,就可以達到接近 16 bits 的效果。不過,無論如何,這樣都使顏色的數目減少。因此,整個貼圖的色彩變化就受到了限制。

另一種方式,就是用傳統的「調色盤」結構。利用一個 256 種顏色的調色盤,就可以把貼圖以 8 bits 的方式儲存。不過,雖然它的色彩空間較大(可以是 24 bits 或 32 bits),但是總顏色數目還是不能超過 256 種。所以,它的應用範圍仍然有限。

現在常用的貼圖壓縮方式,則是利用以區塊為基礎的方式。通常的做法是,把貼圖切割成許多小區塊,再對各個區塊進行壓縮。例如,S3TC 就是把貼圖切成 4x4 的小區塊。利用這種做法,就可以對區塊進行某種處理(通常就是 vector quantization 或是其變形),顯示晶片也可以區塊為單位,進行隨機的存取動作。因此,這是適合用在貼圖的方式。

不過,區塊的大小會影響到壓縮的效果。一般來說,區塊愈大,就能有愈高的壓縮比。不過,愈大的區塊也會使額外的負擔增加。因為顯示晶片只能以區塊為單位來讀取貼圖資料,如果區塊愈大,則每個區塊中就可能會有愈多的資料是不需要的。所以,也不能任意把區塊的大小加大。


在Beers,Agrawala和Chaddha於1996發表的一篇影響深遠的論文基於已壓縮紋理的渲染 [1]中,他們列舉四項紋理壓縮的特點,使其不同於其他圖像壓縮技術。
  • 解壓速度:由於最好能直接從已壓縮的紋理直接渲染,為了儘可能地不影響性能,解壓縮要儘可能快。
  • 隨機訪問:由於幾乎不可能預測紋素被訪問的順序,任何紋理壓縮演算法必須允許對其中紋素的隨機訪問。所以幾乎所有的紋理壓縮演算法都以塊為單位壓縮和存儲紋素,當某一紋素被訪問時,只有同一塊中若干紋素被讀取和解壓縮。這項需求也排除了很多壓縮率較高的圖像壓縮方式,例如JPEG行程長度編碼
  • 壓縮率和圖像質量:由於人眼的不精確性,相比於其他應用領域,圖像渲染更適宜使用有損數據壓縮
  • 編碼速度:紋理壓縮對壓縮速度要求不高,因為絕大多數情況下,紋理只需要進行一次壓縮。
由於其數據訪問模式是事先知道的,紋理壓縮常作為整個繪圖管線的一部分,在繪製時對動態地已壓縮數據進行解壓縮。而反過來繪製管線也可以通過紋理壓縮技術來降低對於頻寬和存儲的需求。在紋理貼圖中,已壓縮紋理和沒有經過壓縮的紋理使用起來基本沒有區別,都可以被用來存儲顏色數據或其他數據,例如凹凸貼圖法線貼圖,也都可以和Mipmapping各向異性過濾等共同使用。


主流紋理壓縮標準:ETC、PVRTC、S3TC首先說OpenGL ES標準中的,2.0版規範中將ETC(Ericsson Texture Compression)作為基本的紋理壓縮標準,這是大部分移動GPU都會支持的紋理標準。 OpenGL ES 3.0中還引入了ETC2、EAC紋理壓縮格式,二者基本一致,只不過EAC主要用於1-2通道數據的情況。目前ECT2還在改進中,除了高通的Adreno 320之外還沒有移動GPU支持,Tgera 4也不行。

此外,OpenGL ES 3.0中還有一種可選紋理壓縮格式——ASTC(Adaptive Scalable Texture Compression,自適應擴展紋理壓縮),這是ARM提出的,去年被Khronos組織認可,納入到標準中來,不過並不是強制性的,目前也只有Mali-T600系列支持。Imagination旗下的PowerVR GPU支持的是PVRTC(PowerVR texture compression)和ETC,高通的Adreno 2xx系列支持ETC之外還有3Dc和ATITC。後兩者都是原來的ATI開發的,Adreno 320除了前面三種標準之外還支持ETC2紋理壓縮。ARM的Mali-300/400系列支持ETC,Mali-T600還多了ASTC紋理支持。NVIDIA的Tegra系列更有趣。之前的說法稱Tegra支持自己的紋理格式,實際上除了通用的ETC之外,Tegra支持的紋理叫做S3TC(S3 Texture Compression),也被稱為DXT​​n或者DXTC。 S3TC是S3公司在1999年引入的,後來被DX 6.0和OpenGL 1.3吸收為官方標準,DXTC相當於Windows版的名字,S3TC是OpenGL中的名字。說到S3TC,之前蘋果和HTC大打專利戰的時候就涉及到了這個標準。 S3已經歸為VIA威盛旗下,HTC和威盛又有同一個老闆——王雪紅。為了支援HTC打專利戰,威盛去年就把S3部門出售給了HTC,算是左手倒右手吧。S3TC是DX顯卡都支持的標準,NVIDIA也在Tegra中支持了這個標準,S3TC根據不同算法又分為DXT1-DXT5這五個級別,Terga支持的實際上是DXT1、DXT3和DXT5。Vivante的GC系列也支持ETC和S3TC,跟NVIDIA的Tegra路線相同。以前都說Vivante支持的是NVIDIA Tegra的紋理數據,實際上二者是選擇了共同的路線而已,DXT也不是NVIDIA的專利。


基於OpenGL ES的壓縮紋理有常見的如下幾種實現:1. ETC1(Ericcson texture compression)  //共同支援度最高2. PVRTC(PowerVR texture compression)3. ATITC(ATI texture compression)對於使用NVIDIA Tegra2芯片的手機如Motorola XOOM,ATRIX和DRIOID BIONIC則支持如下的紋理壓縮4. S3TC(S3 texture compression)


ETC是最通用的紋理壓縮格式,不過ETC並不招廠商待見,因為ETC紋理壓縮不支持Alpha通道,只能用於壓縮不透明的材質,不過ETC也有自己的優點,幾乎所有的安卓設備都可以支持ETC壓縮的GPU加速。S3TC無論壓縮速度還是壓縮比都不錯,也支持GPU加速,而且是桌面顯卡通用的壓縮格式,看起來是最完美的選擇,可惜的是移動市場跟PC不一樣,大家各自為王,NVIDIA現在還沒強大到讓其他GPU廠商低頭採用S3TC標準的程度,因為S3TC說到底還是一種私有的標準,有專利上的麻煩。ETC2壓縮標準補全了ETC1不支持Alpha通道的缺陷,支持更高質量的RGBA(RGB+Alpha)壓縮,而ARM提出的ASTC標准在壓縮速度和質量上比S3TC要好,但是這兩種壓縮格式都是新出的,支持的廠商實在太少了。
 

 
ETC1:
ETC1格式是OpenGL ES圖形標準的一部分,並且被所有的Andr​​oid設備所支持。擴展名為: GL_OES_compressed_ETC1_RGB8_texture,不支持透明通道,所以僅能用於不透明紋理。當加載壓縮紋理時,<internal format>參數支持如下格式:
    
GL_ETC1_RGB8_OES(RGB,每個像素0.5個字節)



PVRTC:
被 用在Motorola的一些机器上,比如DROID系列。GPU为Imagination Technologies的PowerVR SGX 530。OpenGL ES的扩展名为: GL_IMG_texture_compression_pvrtc,支持预处理压缩。当加载压缩纹理时,<internal format>参数支持如下几种格式:
COMPRESSED_RGB_PVRTC_4BPPV1_IMG (RGB 4 bit per pixel)
COMPRESSED_RGB_PVRTC_2BPPV1_IMG (RGB 2 bit per pixel)
COMPRESSED_RGBA_PVRTC_4BPPV1_IMG (RGB 4 bit per pixel with alpha channel)
COMPRESSED_RGBA_PVRTC_2BPPV1_IMG (RGB 2 bit per pixel with alpha channel)

ATITC:
当前使用该种纹理压缩的机器有Nexus One。支持的OpenGL ES扩展名为: GL_ATI_texture_compression_atitc。当加载压缩纹理时,<internal format>参数支持如下类型的纹理:
ATC_RGB_AMD (RGB textures)
ATC_RGBA_EXPLICIT_ALPHA_AMD (RGB textures using explicit alpha encoding)
ATC_RGBA_INTERPOLATED_ALPHA_AMD (RGBA textures using interpolated alpha encoding)

S3TC
也 被称为DXTC,在PC上广泛被使用,但是在移动设备上还是属于新鲜事物。在使用NVIDA芯片的手机上被使用。OpenGL ES扩展名为: GL_EXT_texture_compression_dxt1和GL_EXT_texture_compression_s3tc。当加载压缩纹理 时,<internal format>的参数有如下几种格式:
GL_COMPRESSED_RGB_S3TC_DXT1 (RGB data is compressed, alpha is always 1.0)
GL_COMPRESSED_RGBA_S3TC_DXT1 (RGB data is compressed, alpha is either 1.0 or 0.0)
GL_COMPRESSED_RGBA_S3TC_DXT3 (RGB data is compressed, alpha is stored as 4 bits)
GL_COMPRESSED_RGBA_S3TC_DXT5 (RGB data is compressed, alpha is a weighted average of 8-bit values)

在程序在开始检测这些可用的扩展很重要。对于ETC1压缩来说,使用ETC1Util.isETC1Supported()即可。可以使用android.openGL.getString(GL10.GL_EXTENSIONS)解析字符串获取更多的可用扩展。
 



以下是其它較常見的壓縮模式的細節介紹



VQTC (Vector Quantization Texture Compression)


首先,我們會介紹一種簡單的貼圖壓縮方式:VQTC。

VQTC 是由 NEC/Videologic 的 PowerVR 系列所使用的一種貼圖壓縮方式,但是因為 PowerVR 系列一直沒有得到很大的成功,所以 VQTC 一直未受到重視。而現在 Videologic 最新的 Kyro 顯示晶片也不再支援 VQTC。不過,因為 VQTC 相當簡單,所以還是簡單做個介紹。

VQTC 是使用 vector quantization 的方式,所以叫 VQTC。它的原理是:把貼圖切成許多相同大小的區塊(例如 2x2 或 4x4),對這些區塊做 vector quantization。例如,可以從這些區塊中,找出 4096 個最具代表性的區塊。這樣一來,每個區塊就只需要存放 index,也就是 12 bits 的空間。

顯示晶片在讀取貼圖資料時,要先將整個向量(即 4096 個區塊)讀入顯示晶片中的一個向量表。這個動作對每個貼圖只需要做一次。在讀取各個 texel 時,則是先判斷出所需的 texel 所在的區塊(以 2x2 的區塊來說,就是把位置座標分別除以二),讀取 index,再從向量表中查出區塊的內容。基本上,這些動作都非常簡單


在壓縮比方面,以上面的例子來說,如果使用 256x256 32bpp 的貼圖,並使用 2x2 的區塊,那麼貼圖就有 128x128 = 16384 個區塊。找出 4096 個最具代表性的區塊後,整個貼圖需要的空間就是 4096 個區塊再加上 16384 個 index 的空間,即 4096x16 + 16384x1.5 = 88 KB。和原來未壓縮所需要的 256KB 相比,它的壓縮比約為 1:2.9。

如果是更大的貼圖,則可以使用更大的區塊(例如 4x4),不過,這樣會使品質變差。但是,經由選擇不同的區塊大小,和區塊的數目,就可以調整出品質和壓縮比的平衡點。這是 VQTC 的優點之一。

不過,VQTC 並非沒有缺點的。事實上,VQTC 最大的缺點,就是「不能做部分更新」 。簡單的說,如果想要更新一個 VQTC 貼圖中的一小部分,幾乎可以說是不可能的,除非將貼圖重新壓縮,或是犧牲一些品質,使用原有的向量來壓縮。當然,對大部分的情形來說,這並不是個嚴重的問題;因為需要做部分更新的情形,通常是相當少見的。

另外,VQTC 也不太適合處理有「透明度」(即 alpha channel)的貼圖。一般來說,如果貼圖中有 alpha channel,會對 VQTC 的品質造成影響

VQTC 現在已經不常用了(也許只在 Dreamcast 遊戲機中還被使用)。目前最流行的貼圖壓縮,則是由 S3 所開發的 S3TC。我們會在下一篇文章中介紹這個方法。



S3TC (S3 graphics Texture Compression)



這篇文章將會簡單介紹有名的 S3TC 貼圖壓縮方式。

S3TC 是由 S3 公司所發展出來的,最早用在它的 Savage 系列顯示晶片中。雖然 Savage 顯示晶片並不算成功,但是 S3TC 卻因為微軟公司將其加入 DirectX 而變得相當成功。微軟向 S3 授權 S3TC 技術後,其它公司若在 DirectX 中支援 S3TC(在 DirectX 中稱為 DXTC),則不需另外再付給 S3 費用,因此得到了相當程度的支援。

S3TC 包括五種貼圖壓縮格式,在 DirectX 中,分別稱為 DXT1 ~ DXT5。這些不同的貼圖壓縮格式,其實就是為了透明度(即 alpha channel)所設計的。

DXT1 是 S3TC 中,最基本的壓縮格式。它是用來處理「沒有 alpha channel」或是「alpha channel 為 1 bit」的貼圖。其它的壓縮方式都是只是 DXT1 的變化而已。

DXT1 所用的方法,和 VQTC 類似,也是將貼圖切成許多小區塊,然後再利用 vector quantization 的方式來壓縮。不過,和 VQTC 不同的是,DXT1 的區塊總是 4x4 的大小,而且它將 vector quantization 用在區塊內,而不是像 VQTC 對所有的區塊做 vector quantization。

對每個 4x4 的區塊,儲存了兩個 16 bits 的顏色,顏色的格式是 5-6-5 的 RGB。從這兩個顏色,可以用線性內插的方式,得到另外兩個顏色。簡單的說,如果這兩個顏色分別是 RGB0 和 RGB1,那 RGB2 和 RGB3 可以由下面的式子求出:

RGB2 = (2 * RGB0 + 1 * RGB3) / 3
RGB3 = (1 * RGB0 + 2 * RGB3) / 2


這樣就得到了四個顏色。區塊中的每個 texel 則是以一個 2 bits 的 index 表示。如果 index = 0,則表示這個 texel 的顏色是 RGB0;index = 1 則是 RGB1;index = 2 則是 RGB2;index = 3 則是 RGB3。以這樣的方式儲存,每個區塊只需要 16x2 + 2x4x4 = 64 bits。因此,在壓縮 16 bits 的貼圖時,壓縮比是 1:4。

DXT1 同時也可以處理「具有 1 bit alpha channel」的貼圖。一般來說,在貼圖中使用 1 bit 的 alpha channel,就是用來註明「透明」的地方。因此,DXT1 的處理方式,是犧牲一個顏色,用它的表示「透明」的 texel。

如果一個區塊需要存放透明色的話,那麼,顏色的產生方式,就變成:

RGB2 = (RGB0 + RGB1) / 2

和前面一樣,texel 的 index 仍是 2 bits。index = 0 ~ 2 時,分別是表示 RGB0 ~ RGB2。當 index = 3 時,就表示這個 texel 是「透明」的,它的顏色是黑色,且 alpha = 0。

不過,要怎麼區分「有 alpha」和「沒有 alpha」的情形呢?DXT1 使用了一個簡單的方法:如果 RGB0 比 RGB1 要大的話,那就表示區塊是沒有 alpha 的。相反的,就表示有 alpha channel 了。

當 alpha channel 不只是 1 bit 時,就需要其它的方式了。DXT2 ~ DXT5 就是在處理這種情形。因為它的原理和 DXT1 是相同的,所以就不再多介紹了。不過,DXT2 ~ DXT5 每個 4x4 區塊需要 128 bits,也就是說,它們的壓縮比只有 DXT1 的一半。

因為 S3TC 是針對每個 4x4 的區塊做壓縮,所以可以對貼圖做「部分更新」的動作。要更新貼圖中的某些部分,只要需更新這些部分的區塊即可,其它的區塊則完全不會被影響。所以,「部分更新」是很容易做到的。

在下一篇文章中,會簡單介紹 3dfx 的 FXT1 貼圖壓縮



FXT1 (3dfx Texture Compression )



在這篇文章中,會簡單介紹 3dfx 的 FXT1 貼圖壓縮技術。

基本上,FXT1 和 S3TC 是非常類似的。不過,FXT1 使用 8x4 的區塊,而非 S3TC 的 4x4 區塊。而且,相對於 S3TC 只有一種壓縮方式(即 DXT1,DXT2 ~ DXT5 只是將 DXT1 加上 alpha channel 而已),FXT1 有四種不同的方式。這使得在壓縮的時候,可以針對不同的情形,使用最適當的壓縮方式

FXT1 有四種壓縮方式,都是使用 8x4 的區塊。第一種方式稱為 CC_HI,用來壓縮使用 1 bit alpha channel 來表示「透明 texel」的貼圖。

CC_HI 和 DXT1 非常類似,它也是存放兩個顏色。不過,和 DXT1 不同的是,CC_HI 用的是 15 bits 5-5-5 RGB 的格式,而不是 16 bits 5-6-5 RGB 的格式。

利用線性內插的方式(類似 DXT1 的方式),CC_HI 的兩個顏色會內插出五個顏色,合起來是七個顏色,再加上一個顏色表示「透明 texel」。每個 texel 則使用 3 bits 的 index 來指向這八個顏色
。另外,CC_HI 使用 2 bits 的 mode bits 來表示這個區塊是 CC_HI 格式。所以,一個 8x4 的區塊需要 2 + 2x15 + 32x3 = 128 bits。因此,如果貼圖是 16 bits 的話,壓縮比就是 1:4。

第二種方式稱為 CC_CHROMA它是專門用來存放沒有 alpha channle 的貼圖。 基本上,CC_CHROMA 的方式很簡單:它存放四個 15 bits 的顏色,每個 texel 則用一個 2 bits 的 index 指向這四個顏色。它的 mode bits 是 3 bits,還有一個未使用的 bit,所以它需要的空間是 3 + 1 + 4x15 + 32x2 = 128 bits。

第三種方式稱為 CC_MIXED,這個模式非常的複雜。它將 8x4 的區塊分成兩個 4x4 的小區塊,每個小區塊有兩個 16 bits 5-6-5 RGB 的顏色。每個小區塊中的壓縮方式則和 DXT1 幾乎是相同的,但是它不是用顏色的大小來區分「是否有 alpha channel」,而是用一個 alpha bit 來區分。這個模式需要的空間也是 128 bits。

第四種方式則是 CC_ALPHA,是用來處理具有多位元 alpha channel 的模式(類似 DXT2 ~ DXT5)。它使用三個 20 bits 5-5-5-5 ARGB 格式的顏色,並有兩種模式:第一種模式,每個 texel 的 index(2 bits)指向這三個顏色,而第四個顏色則是表示「透明 texel」。第二種模式,則是將區塊分成兩個小區塊,並使用類似 DXT1 的方式(第一個小區塊用 COLOR0 和 COLOR1 為基礎色,而第二個小區塊則用 COLOR1 和 COLOR2 為基礎色),只不過有加上 alpha channel。這個格式的 mode bit 是 3 bits,再加上一個「模式 bit」,所以需要的空間是 3 + 1 + 20x3 + 32x2 = 128 bits。

基本上,FXT1 的壓縮比和 S3TC 並沒有很大的差別,但是一般來說,效果(特別是壓縮 24 bits 或 32 bits 的貼圖)則比 S3TC 要好一些。不過,它的區塊較大,也會使顯示晶片在處理時的效率較差。

FXT1 和 S3TC 都有相對應的 OpenGL extension 來提供支援。所以,如果想要取得更詳細的資料,可以到 OpenGL® Extension Registry 網站取得。S3TC 的 extension 稱為 GL_EXT_texture_compression_s3tc,而 FXT1 的 extension 則稱為 GL_3DFX_texture_compression_FXT1。在 extension 的文件中,有詳細的格式說明。

 



压缩纹理工具的使用
   每种压缩纹理以及相应的厂商都提供了压缩纹理的工具,工具都分两个版本:
   a. 可视化转换工具 (给美工或小白少量使用)
   b. 命令行转换工具 (给程序批量使用)

下面对每个工具的用法进行说明。
1)Imagination Technologies PowerVR
工具下载地址
http://www.imgtec.com/powervr/insider/sdkdownloads/index.asp?installer=Windows%20Installer

可视化转换界面

    
命令行转换脚本
for %%i in (*.tga) do PVRTexTool.exe -f PVRTC4 -i %%i
(将本目录下的所有tga文件,转换成"PVRTC4"编码格式的pvr文件,不带mipmap)
详细使用说明:PvrTexTool.exe /?

2)Qualcomm Adreno
工具下载地址
https://developer.qualcomm.com/mobile-development/mobile-technologies/gaming-graphics-optimization-adreno/tools-and-resources

可视化转换界面

   
命令行转换脚本
for %%i in (*.tga) do QCompressCmd.exe %%i %%i.ktx "ATC RGBA Explicit" yes
(将本目录下的所有tga文件,转换成"ATC RGBA Explicit"编码格式的ktx文件,带mipmap)
详细使用说明:QCompressCmd.exe /?

3)ARM Mali
工具下载地址
http://malideveloper.arm.com/develop-for-mali/mali-gpu-texture-compression-tool/

可视化转换界面

   
命令行转换脚本
for %%i in (*.tga) do PVRTexTool.exe -f ETC -i %%i
(将本目录下的所有tga文件,转换成"ETC"编码格式的pvr文件,不带mipmap这里还是使用的PVRTexTool.exe,也可以使用QCompressCmd.exe)
详细使用说明:PVRTexTool.exe /?

4)nVIDIA Tegra
可以使用DirectX SDK中自带的DirectX Texture Tool进行转换
可视化转换界面

   
命令行转换脚本
for %%i in (*.tga) do texconv.exe -f DXT5 %%i
(将本目录下的所有tga文件,转换成"DXT5"编码格式的dds文件,不带mipmap)
详细使用说明:TexConv.exe /?







參考網址
http://www.opengpu.org/forum.php?mod=viewthread&tid=417

http://zh.wikipedia.org/wiki/%E7%BA%B9%E7%90%86%E5%8E%8B%E7%BC%A9

http://m.mydrivers.com/newsview.aspx?id=266555&cid=1&p=2

http://www.blogjava.net/demibug/archive/2011/12/16/366543.html

http://www.cnblogs.com/luming1979/archive/2013/02/04/2891421.html


OpenGL基本實作(十四)(Texture Filters)

根據前一個實作例,修改一下,加入灰階的圖檔,以作為Texture的實驗,為此並加入自動旋轉的機制,以方便觀察Filter的修補。因為要做Filter的實驗,因此也有
GL_TEXTURE_MIN_FILTER及GL_TEXTURE_MAG_FILTER這兩個選項的實驗。


1.  為了新的實驗,將原本的實作專案稍改一下名稱



2. 加入一個黑白相間的圖檔,以作為灰階texture filter的測試


3. 依序就修改之處貼出GLView.h

#import <UIKit/UIKit.h>

#import "Interfaces.hpp"
#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>

@interface GLView : UIView {
@private
    IApplicationEngine* m_applicationEngine;
    IRenderingEngine* m_renderingEngine;
    IResourceManager* m_resourceManager; // for Textured File
    EAGLContext* m_context;
    float m_timestamp;
   
@public
    float smallIconRotateXValue;
    float smallIconRotateYValue;
    float smallIconRotateZValue;
   
    float  lightPosX;
    float  lightPosY;
    float  lightPosZ;
   
    int GLSL_mode;
    int textureMode;
    int ES_mode;
   
    int textureWrapS;
    int textureWrapT;
   
    int textureMagFilter;
    int textureMinFilter;

   
    BOOL enAutoRoate;
   
    Quaternion old_orientation;
   
    CADisplayLink* displayLink;
}

- (void) drawView: (CADisplayLink*) displayLink;

- (id) initSet:(CGRect) frame;

- (void) setGLSL:(int) mode;

- (void) setTexture:(int) mode;
- (void) setES:(int) mode;

- (void) setTextureWrapS:(int) mode;
- (void) setTextureWrapT:(int) mode;

- (void) setTextureMagFilter:(int) mode;
- (void) setTextureMinFilter:(int) mode;

- (void) enAutoRotate;


- (void) resetdisplayLink;

@end


4. GLView.mm

#import "GLView.h"

@implementation GLView
{

}

+ (Class) layerClass
{
    return [CAEAGLLayer class];
}

- (id) initWithFrame: (CGRect) frame
{
    smallIconRotateXValue =0;
    smallIconRotateYValue =0;
    smallIconRotateZValue =0;
   
    lightPosX =0.25;
    lightPosY =0.25;
    lightPosZ =1;
   
    GLSL_mode = 0;
    textureMode = DefaultTexture;
    ES_mode = 1;
   
    textureWrapS =0;
    textureWrapT =0;
   
    textureMagFilter =0;
    textureMinFilter =0;

   
    old_orientation = Quaternion(0, 0, 0, 1);
   
    if (self = [super initWithFrame:frame])
    {
        if  ([self initSet:frame] == nil)
            return nil;
    }
    return self;
}

- (id) initSet:(CGRect) frame
{
    CAEAGLLayer* eaglLayer = (CAEAGLLayer*) self.layer;
    eaglLayer.opaque = YES;

    EAGLRenderingAPI api;
   
    if (ES_mode == 1)
       api = kEAGLRenderingAPIOpenGLES2;
    else
       api = kEAGLRenderingAPIOpenGLES1;
   
   
    m_context = [[EAGLContext alloc] initWithAPI:api];
       
    if (!m_context) {
        api = kEAGLRenderingAPIOpenGLES1;
        m_context = [[EAGLContext alloc] initWithAPI:api];
    }
   
    if (!m_context || ![EAGLContext setCurrentContext:m_context]) {
        //[self release];
        return nil;
    }
   
    m_resourceManager = Darwin::CreateResourceManager();
   
    if (api == kEAGLRenderingAPIOpenGLES1) {
        NSLog(@"Using OpenGL ES 1.1");
        //m_renderingEngine = WireframeES1::CreateRenderingEngine();
        //m_renderingEngine = SolidES1::CreateRenderingEngine();
        m_renderingEngine = TexturedES1::CreateRenderingEngine(m_resourceManager);
    } else {
        NSLog(@"Using OpenGL ES 2.0");
        //m_renderingEngine = WireframeES2::CreateRenderingEngine(); // 完成 m_colorRenderbuffer 設定
       
        //m_renderingEngine = SolidES2::CreateRenderingEngine();  // replace
       
        m_renderingEngine = TexturedES2::CreateRenderingEngine(m_resourceManager);
       
    }
   
       m_applicationEngine = ParametricViewer::CreateApplicationEngine(m_renderingEngine);
   

    m_applicationEngine->SetIconRotateValue(smallIconRotateXValue, smallIconRotateYValue, smallIconRotateZValue);
   
    //m_applicationEngine->setLightPos(lightPosX, lightPosY, lightPosZ);
   
    //m_applicationEngine->setOldRotation(old_orientation);
   
    //m_applicationEngine->setGLSL(GLSL_mode);
    m_applicationEngine->setTexture(textureMode);
    m_applicationEngine->setTextureWrapS(textureWrapS);
    m_applicationEngine->setTextureWrapT(textureWrapT);
   
    m_applicationEngine->setTextureMagFilter(textureMagFilter);
    m_applicationEngine->setTextureMinFilter(textureMinFilter);

   
        [m_context
            renderbufferStorage:GL_RENDERBUFFER
            fromDrawable: eaglLayer];
   
    int width = CGRectGetWidth(frame);
    int height = CGRectGetHeight(frame);
    m_applicationEngine->Initialize(width, height);
      [self setdisplayLink];
   
    return self;
}

.....

- (void) setTextureMagFilter:(int) mode
{
    textureMagFilter = mode;
}

- (void) setTextureMinFilter:(int) mode
{
    textureMinFilter = mode;
}


- (void) enAutoRotate
{
    enAutoRoate = YES;
   
    m_applicationEngine->setAutoRotate();
}

.....

- (void) touchesBegan: (NSSet*) touches withEvent: (UIEvent*) event
{
    UITouch* touch = [touches anyObject];
    CGPoint location  = [touch locationInView: self];
    m_applicationEngine->OnFingerDown(ivec2(location.x, location.y));
   
    enAutoRoate = NO;
}

- (void) touchesEnded: (NSSet*) touches withEvent: (UIEvent*) event
{
    UITouch* touch = [touches anyObject];
    CGPoint location  = [touch locationInView: self];
    m_applicationEngine->OnFingerUp(ivec2(location.x, location.y));  //傳入所按的平面位置
   
    enAutoRoate = NO;
}

- (void) touchesMoved: (NSSet*) touches withEvent: (UIEvent*) event
{
    UITouch* touch = [touches anyObject];
    CGPoint previous  = [touch previousLocationInView: self];
    CGPoint current = [touch locationInView: self];
    m_applicationEngine->OnFingerMove(ivec2(previous.x, previous.y),
                                      ivec2(current.x, current.y));
   
    enAutoRoate = NO;
}

@end

namespace FacetedES2 { IRenderingEngine* CreateRenderingEngine() { return 0; } }
namespace SolidGL2 { IRenderingEngine* CreateRenderingEngine() { return 0; } }
namespace TexturedGL2 { IRenderingEngine* CreateRenderingEngine() { return 0; } }

5. Interfaces.hpp

#pragma once
#include "Vector.hpp"
#include "Quaternion.hpp"
#include <vector>
#include <string>

using std::vector;
using std::string;

enum VertexFlags {
    VertexFlagsNormals = 1 << 0,  //  ==1
    VertexFlagsTexCoords = 1 << 1,  // ==2
};

struct IApplicationEngine {
    virtual void Initialize(int width, int height) = 0;
    virtual void Render() const = 0;
    virtual void UpdateAnimation(float timeStep) = 0;
    virtual void OnFingerUp(ivec2 location) = 0;
    virtual void OnFingerDown(ivec2 location) = 0;
    virtual void OnFingerMove(ivec2 oldLocation, ivec2 newLocation) = 0;
    virtual void SetIconRotateValue(float x, float y, float z) =0;  // add for icon rotation
    virtual void setLightPos(float x, float y, float z) = 0; // add for light position
    virtual void setGLSL(int mode) =0;
    virtual void setTexture(int mode) =0;
    virtual void setTextureWrapS(int mode) =0;
    virtual void setTextureWrapT(int mode) =0;
    virtual void setTextureMagFilter(int mode) =0;
    virtual void setTextureMinFilter(int mode) =0;
    virtual void setAutoRotate() =0;

   
    //virtual Quaternion get_m_orientation() =0;
    virtual ~IApplicationEngine() {}
};

.....

struct IRenderingEngine {
    virtual void Initialize(const vector<ISurface*>& surfaces) = 0;
    virtual void Render(const vector<Visual>& visuals) const = 0;
    virtual void setLightPos(float x, float y, float z) =0; //const = 0;  /// 不能使用const
    virtual void setGLSL(int mode) =0;
    virtual void setTexture(int mode) =0;
    virtual void setTextureWrapS(int mode) =0;
    virtual void setTextureWrapT(int mode) =0;
    virtual void setTextureMagFilter(int mode) =0;
    virtual void setTextureMinFilter(int mode) =0;
    //virtual void setAutoRotate();

   
    ///virtual Quaternion get_m_orientation () const =0;
    virtual ~IRenderingEngine() {}
};


#define DefaultTexture 5

.....

 6. mainViewController.mm

#import "mainViewController.h"
#import "Interfaces.hpp"   //define放置處

@interface mainViewController ()
{
    int GLSL_mode;
    int textureMode;
    int ES_mode;
    int textureWrapS;
    int textureWrapT;
    BOOL isAutoRotate;
   
    int textureMinFilter;
    int textureMagFilter;
   

}
@end

@implementation mainViewController
{

    UISlider  *sIconXRotateSlider;
    UISlider  *sIconYRotateSlider;
    UISlider  *sIconZRotateSlider;
   
    UISlider  *lightPosXSlider;
    UISlider  *lightPosYSlider;
    UISlider  *lightPosZSlider;
   
    //UISegmentedControl *GLSL_Selector;
    UISegmentedControl *textureSelector;
   
    UISegmentedControl *ES_Mode_Selector;
    UISegmentedControl *textureWrapS_Selector;
    UISegmentedControl *textureWrapT_Selector;
   
    UIButton *autoRotateBtn;
   
    UISegmentedControl *textureMinFilterSel;
    UISegmentedControl *textureMagFilterSel;
   

   
}
- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
   
    CGRect screenBounds = [[UIScreen mainScreen] bounds];
   
    m_window = [[UIWindow alloc] initWithFrame: screenBounds];
   
    m_view = [[GLView alloc] initWithFrame: screenBounds];
   
    [m_window addSubview: m_view];
    [m_window makeKeyAndVisible];

   
    [self setSlideInterface1];
    [self setSlideInterface2];
    [self setSlideInterface3];
   
    [self setSlideInterfaceLightX];
    [self setSlideInterfaceLightY];
    [self setSlideInterfaceLightZ];
   
    //[self setGLSL_Selector];
    [self setTextureSelector];
    [self setES_Selector];
    [self setTextureWrapS];
    [self setTextureWrapT];
    [self setAutoRotateBtn];
   
    [self setTextureMinFilter];
    [self setTextureMagFilter];
   

   
    [m_window addSubview:sIconXRotateSlider];
    [m_window addSubview:sIconYRotateSlider];
    [m_window addSubview:sIconZRotateSlider];
   
    [m_window addSubview:lightPosXSlider];
    [m_window addSubview:lightPosYSlider];
    [m_window addSubview:lightPosZSlider];
   
    [m_window addSubview:textureSelector];
    [m_window addSubview:ES_Mode_Selector];
   
    //[m_window addSubview:textureWrapS_Selector];
    //[m_window addSubview:textureWrapT_Selector];
   
    [m_window addSubview:autoRotateBtn];
   
    [m_window addSubview:textureMinFilterSel];
    [m_window addSubview:textureMagFilterSel];
   

   
    GLSL_mode = 0;
    textureMode = 0;
    ES_mode = 1;  // ES = 2.0
   
    textureWrapS = 0;
    textureWrapT = 0;
   
}

.....


- (void) setAutoRotateBtn
{
    autoRotateBtn = [UIButton  buttonWithType:UIButtonTypeRoundedRect];
    //動態產生一個RoundedRect 形式的  Button
   
    autoRotateBtn.frame = CGRectMake(0,0, 100, 30); // 大小
   
    [autoRotateBtn setCenter:CGPointMake(700, 100)];//位置放在x=150, y=50的位置
   
    // 由於設定成LandScape,X在垂直方向
    autoRotateBtn.transform = CGAffineTransformMakeRotation( M_PI / 2.0);
   
    [autoRotateBtn addTarget:self action:@selector(startRotate:) forControlEvents:UIControlEventTouchUpInside];
    //設定Button動作呼叫的function在 onHelloActionButton,方式為按下
   
    //_helloActionButton.= @"Action Button";
    [autoRotateBtn setTitle:@"自動旋轉" forState:UIControlStateNormal];
    //將動態Button上放置Action Button這兩個字
   
}


- (void) setTextureMinFilter
{
    //[self removeSubViewByLabelClass];
   
    NSArray *itemArray =[NSArray arrayWithObjects: @"LINEAR", @"NEAREST", @"NEAREST_NEAREST",@"LINEAR_NEAREST" ,@"LINEAR_LINEAR",@"NEAREST_LINEAR",nil];
   
    //使用陣列來建立UISegmentedControl
    textureMinFilterSel= [[UISegmentedControl alloc] initWithItems:itemArray];
   
    //設定外觀大小與初始選項
    textureMinFilterSel.segmentedControlStyle = UISegmentedControlStyleBar;
    textureMinFilterSel.frame = CGRectMake(20.0, 100.0, 800.0, 44.0);
   
    [textureMinFilterSel setCenter:CGPointMake(600, 400)];

    textureMinFilterSel.selectedSegmentIndex = 0;
    textureMinFilterSel.tag = 1;
   
    // 由於設定成LandScape,X在垂直方向
    textureMinFilterSel.transform = CGAffineTransformMakeRotation( M_PI / 2.0);
   
    //設定所觸發的事件條件與對應事件
    [textureMinFilterSel addTarget:self action:@selector(textureMinFilterChoice:) forControlEvents:UIControlEventValueChanged];
   
    //加入畫面中並釋放記憶體
    [self.view addSubview:textureMinFilterSel];
   
}

- (void) setTextureMagFilter
{
    //[self removeSubViewByLabelClass];
   
    NSArray *itemArray =[NSArray arrayWithObjects:@"LINEAR", @"NEAREST" ,nil];
   
    //使用陣列來建立UISegmentedControl
    textureMagFilterSel= [[UISegmentedControl alloc] initWithItems:itemArray];
   
    //設定外觀大小與初始選項
    textureMagFilterSel.segmentedControlStyle = UISegmentedControlStyleBar;
    textureMagFilterSel.frame = CGRectMake(20.0, 100.0, 200.0, 44.0);
   
    [textureMagFilterSel setCenter:CGPointMake(550, 100)];
   
    textureMagFilterSel.selectedSegmentIndex = 0;
    textureMagFilterSel.tag = 1;
   
    // 由於設定成LandScape,X在垂直方向
    textureMagFilterSel.transform = CGAffineTransformMakeRotation( M_PI / 2.0);
   
    //設定所觸發的事件條件與對應事件
    [textureMagFilterSel addTarget:self action:@selector(textureMagFilterChoice:) forControlEvents:UIControlEventValueChanged];
   
    //加入畫面中並釋放記憶體
    [self.view addSubview:textureMagFilterSel];
   
}

......

- (void) textureMagFilterChoice:(id)sender {
   
    textureMagFilter = [sender selectedSegmentIndex];
   
    [m_view setTextureMagFilter:textureMagFilter];
   
    [m_view initSet:m_window.frame];
   
}

- (void) textureMinFilterChoice:(id)sender {
   
    textureMinFilter = [sender selectedSegmentIndex];
   
    [m_view setTextureMinFilter:textureMinFilter];
   
    [m_view initSet:m_window.frame];
   
}


- (void) startRotate:(id)sender {
   
    [m_view enAutoRotate];
   
}


@end


7. RenderingEngine.ES1.cpp

#include <OpenGLES/ES1/gl.h>
#include <OpenGLES/ES1/glext.h>
#include "Interfaces.hpp"
#include "Matrix.hpp"

#include <iostream>
using namespace std;

//namespace WireframeES1 {
//namespace SolidES1 {
namespace TexturedES1 {
   
struct Drawable {
    GLuint VertexBuffer;
    GLuint IndexBuffer;
    int IndexCount;
};

class RenderingEngine : public IRenderingEngine {
public:
//        RenderingEngine();
    RenderingEngine(IResourceManager* resourceManager);
    void Initialize(const vector<ISurface*>& surfaces);
    void Render(const vector<Visual>& visuals) const;
    void setLightPos(float x, float y, float z); // const // 使用const就變成惟讀
    void setGLSL(int mode);
    void setTexture(int mode);
    void setTextureWrapS(int mode);
    void setTextureWrapT(int mode);
   
    void setTextureMagFilter(int mode);
    void setTextureMinFilter(int mode);
    //void setAutoRotate();

private:
    vector<Drawable> m_drawables;
    GLuint m_colorRenderbuffer;
    GLuint m_depthRenderbuffer;
    GLuint m_gridTexture;  // new var in texture
    mat4 m_translation;
    IResourceManager* m_resourceManager; // new var in texture
   
    int textureMode;
   
    int textureWrapS;
    int textureWrapT;
   
    int textureMagFilter;
    int textureMinFilter;
   

        float lightPosX;
        float lightPosY;
        float lightPosZ;
       
    };
   
.....
RenderingEngine::RenderingEngine(IResourceManager* resourceManager)
{
    m_resourceManager = resourceManager;
    glGenRenderbuffersOES(1, &m_colorRenderbuffer);
    glBindRenderbufferOES(GL_RENDERBUFFER_OES, m_colorRenderbuffer);
   
    //GLSL_Mode = 0;
    textureMode = DefaultTexture;
}

void RenderingEngine::Initialize(const vector<ISurface*>& surfaces)
{
    vector<ISurface*>::const_iterator surface;
    for (surface = surfaces.begin(); surface != surfaces.end(); ++surface) {
       
        // Create the VBO for the vertices.
        vector<float> vertices;
        unsigned char vertexFlags = VertexFlagsNormals | VertexFlagsTexCoords; // new in texture
            //(*surface)->GenerateVertices(vertices);
            //(*surface)->GenerateVertices(vertices, VertexFlagsNormals); // replace
        (*surface)->GenerateVertices(vertices, vertexFlags);   // new in texture        
        GLuint vertexBuffer;
            glGenBuffers(1, &vertexBuffer);  //設定GPU memory 給 Vertex用
        glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
            glBufferData(GL_ARRAY_BUFFER,   // 將vertex資料存到GPU memory
                         vertices.size() * sizeof(vertices[0]),  // 確認記憶體大小
                         &vertices[0], GL_STATIC_DRAW);
           
        // Create a new VBO for the indices if needed.
            //int indexCount = (*surface)->GetLineIndexCount();
        int indexCount = (*surface)->GetTriangleIndexCount();
        GLuint indexBuffer;
        if (!m_drawables.empty() && indexCount == m_drawables[0].IndexCount) {
            indexBuffer = m_drawables[0].IndexBuffer;
        } else {
            vector<GLushort> indices(indexCount);
                //(*surface)->GenerateLineIndices(indices);  // 4個 indices為一組
                (*surface)->GenerateTriangleIndices(indices);  // replace               
            glGenBuffers(1, &indexBuffer);
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
            glBufferData(GL_ELEMENT_ARRAY_BUFFER,
                         indexCount * sizeof(GLushort),
                         &indices[0],
                             GL_STATIC_DRAW);  // 表示该缓存区不会被修改
        }
       
        Drawable drawable = { vertexBuffer, indexBuffer, indexCount};
        m_drawables.push_back(drawable);
    }

    // Extract width and height from the color buffer.
    int width, height;
    glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES,
                                    GL_RENDERBUFFER_WIDTH_OES, &width);
    glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES,
                                    GL_RENDERBUFFER_HEIGHT_OES, &height);

    // Create a depth buffer that has the same size as the color buffer.
    glGenRenderbuffersOES(1, &m_depthRenderbuffer);
    glBindRenderbufferOES(GL_RENDERBUFFER_OES, m_depthRenderbuffer);
    glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_DEPTH_COMPONENT16_OES,
                             width, height);


    // Create the framebuffer object.
    GLuint framebuffer;
    glGenFramebuffersOES(1, &framebuffer);
    glBindFramebufferOES(GL_FRAMEBUFFER_OES, framebuffer);
    glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES,
                                 GL_RENDERBUFFER_OES, m_colorRenderbuffer);
    glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES,
                                 GL_RENDERBUFFER_OES, m_depthRenderbuffer);
    glBindRenderbufferOES(GL_RENDERBUFFER_OES, m_colorRenderbuffer);
       
//---------- start of texture process --------------------------
    float degree;
     //一個檢查的機制指令,來確認其各異向性紋理過濾最大運算能力。
    glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &degree);


    // Load the texture.
    glGenTextures(1, &m_gridTexture);

//======  Texture filtering  =============================================================   
    glBindTexture(GL_TEXTURE_2D, m_gridTexture);
    glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);  //指定自動產生Mipmap的小圖
   
    //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
   
    if (textureMinFilter == 1)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    else if (textureMinFilter == 2)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
    else if (textureMinFilter == 3)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
    else if (textureMinFilter == 4)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    else if (textureMinFilter == 5)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR);
    else
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
   
   
    if (textureMinFilter == 1)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    else if (textureMinFilter == 2)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

   
   
    if (textureMode !=5)
       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, degree);
//=========================================================================

   
//------  texture coordinate ------------------------------------------------------------   
    glBindTexture(GL_TEXTURE_2D, m_gridTexture);
   
    //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
   
    if (textureWrapS == 1)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    else if (textureWrapS == 2)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    else
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
   
   
    if (textureWrapT == 1)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    else if (textureWrapT == 2)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    else
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
//-------------------------------------------------------------------------
   
   
    //m_resourceManager->LoadPngImage("Grid16");
   
    if (textureMode == 2)
        m_resourceManager->LoadPngImage("Spiked32x32");
    else if (textureMode == 4)
        m_resourceManager->LoadPngImage("大雄128x64");
    else if (textureMode == 3)
        m_resourceManager->LoadPngImage("flake64x64");
    else if (textureMode == 1)
        m_resourceManager->LoadPngImage("Sunflower16x16");
    else if (textureMode == 5)
        m_resourceManager->LoadPngImage("Checkerboard");

    else
        m_resourceManager->LoadPngImage("Grid16");
   
   
    void* pixels = m_resourceManager->GetImageData();

    ivec2 size = m_resourceManager->GetImageSize();
   
    if (textureMode ==5)
        glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, size.x, size.y, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, pixels);
    else
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
   
    //glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, size.x, size.y, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, pixels);
   
 
    m_resourceManager->UnloadImage();

//---------- end of texture process --------------------------

    // Set up various GL state.
    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_NORMAL_ARRAY);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    ////Tell OpenGL to enable the texture coordinate vertex attribute.
   
        glEnable(GL_LIGHTING);   // new
        glEnable(GL_LIGHT0);     // Enable lighting and turn on the first light source (known as GL_LIGHT0). The iPhone supports up to eight light sources, but we're using only one.
       
        glEnable(GL_DEPTH_TEST);   // new

    glEnable(GL_TEXTURE_2D);
   
    // Set up the material properties.
    vec4 specular(0.5f, 0.5f, 0.5f, 1);
    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specular.Pointer());
    glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 50.0f);

    m_translation = mat4::Translate(0, 0, -7);
}

void RenderingEngine::Render(const vector<Visual>& visuals) const
{
    glClearColor(0.5f, 0.5f, 0.5f, 1);

        //glClear(GL_COLOR_BUFFER_BIT);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // replace   GL_DEPTH_BUFFER_BIT沒設到,會造成無法顯示
       
    vector<Visual>::const_iterator visual = visuals.begin();
    for (int visualIndex = 0; visual != visuals.end(); ++visual, ++visualIndex) {
       
        // Set the viewport transform.
        ivec2 size = visual->ViewportSize;
        ivec2 lowerLeft = visual->LowerLeft;
        glViewport(lowerLeft.x, lowerLeft.y, size.x, size.y); // original
            //glViewport(lowerLeft.y, lowerLeft.x, size.y, size.x);
       
        // Set the light position.
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
     
        //vec4 lightPosition(0.25, 0.25, 1, 0);
        vec4 lightPosition(lightPosX,lightPosY, lightPosZ,0);
        glLightfv(GL_LIGHT0, GL_POSITION, lightPosition.Pointer());
       
        // Set the model-view transform.
        mat4 rotation = visual->Orientation.ToMatrix();
        mat4 modelview = rotation * m_translation;

         //   glMatrixMode(GL_MODELVIEW); //指定哪一个矩阵是当前矩阵,
         // GL_MODELVIEW/GL_PROJECTION/GL_TEXTURE 但在 texture 取消了 
        glLoadMatrixf(modelview.Pointer());

        // Set the projection transform.
        float h = 4.0f * size.y / size.x;
        mat4 projection = mat4::Frustum(-2, 2, -h / 2, h / 2, 5, 10);
        glMatrixMode(GL_PROJECTION);
        glLoadMatrixf(projection.Pointer());

       
        //glMatrixMode(GL_TEXTURE);   // new in texture
        //glTranslatef(0.01f, 0, 0);  // new in texture 貼圖會轉動是這個指令,並且會影響到GL_CLAMP_TO_EDGE的設定,這是ES1.x的缺點之一

       
       
//            // Set the color.
//            vec3 color = visual->Color;
//            glColor4f(color.x, color.y, color.z, 1);
//
        // Set the diffuse color.
        vec3 color = visual->Color * 0.75f;
        vec4 diffuse(color.x, color.y, color.z, 1);
        //glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse.Pointer()); /// 顯示出金屬色
       

//------------ start to replace in texture --------------------
        // Draw the surface.
        int stride = sizeof(vec3) + sizeof(vec3) + sizeof(vec2);
        const GLvoid* normalOffset = (const GLvoid*) sizeof(vec3);
        const GLvoid* texCoordOffset = (const GLvoid*) (2 * sizeof(vec3));
        const Drawable& drawable = m_drawables[visualIndex];
        glBindBuffer(GL_ARRAY_BUFFER, drawable.VertexBuffer);
        glVertexPointer(3, GL_FLOAT, stride, 0);
        glNormalPointer(GL_FLOAT, stride, normalOffset);
        glTexCoordPointer(2, GL_FLOAT, stride, texCoordOffset);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, drawable.IndexBuffer);
        glDrawElements(GL_TRIANGLES, drawable.IndexCount, GL_UNSIGNED_SHORT, 0);

//------------ end to replace in texture --------------------
      
    }
}
   
   
   
    void RenderingEngine::setLightPos(float x, float y, float z)  // const // 使用const就變成惟讀函數
    {
        lightPosX = x;
        lightPosY = y;
        lightPosZ = z;
    }
   
    void RenderingEngine::setGLSL(int mode)
    {
        //GLSL_mode = mode;
    }
   
    void RenderingEngine::setTexture(int mode)
    {
        textureMode = mode;
    }
   
    void RenderingEngine::setTextureWrapS(int mode)
    {
        textureWrapS = mode;
    }
   
    void RenderingEngine::setTextureWrapT(int mode)
    {
        textureWrapT = mode;
    }
   
    void RenderingEngine::setTextureMagFilter(int mode)
    {
        textureMagFilter = mode;
    }
   
    void RenderingEngine::setTextureMinFilter(int mode)
    {
        textureMinFilter = mode;
    }
   
}

8. RenderingEngine.ES2.cpp

#include <OpenGLES/ES2/gl.h>
#include <OpenGLES/ES2/glext.h>
#include "Interfaces.hpp"
#include "Matrix.hpp"
#include <iostream>

namespace TexturedES2 {

#define STRINGIFY(A)  #A
/*
#include "./Shaders/Simple.es2.vert"
#include "./Shaders/Simple.es2.frag"
   
#include "./Shaders/PixelLighting.es2.frag"
#include "./Shaders/PixelLighting.es2.vert"
   
#include "./Shaders/ToonShading.es2.frag"
*/
#include "./Shaders/TexturedLighting.es2.vert"
#include "./Shaders/TexturedLighting.es2.frag"


struct UniformHandles {
    GLuint Modelview;
    GLuint Projection;
    GLuint NormalMatrix;
    GLuint LightPosition;
    GLint AmbientMaterial;
    GLint SpecularMaterial;
    GLint Shininess;
    GLint Sampler;  // new in texture
};

 
struct AttributeHandles {
    GLint Position;
    GLint Normal;
    GLint DiffuseMaterial;
    GLint TextureCoord;  // new in texture
};
   
struct Drawable {
    GLuint VertexBuffer;
    GLuint IndexBuffer;
    int IndexCount;
};
   
class RenderingEngine : public IRenderingEngine {
public:
//        RenderingEngine();
    RenderingEngine(IResourceManager*); // replace in texture
    void Initialize(const vector<ISurface*>& surfaces);
    void Render(const vector<Visual>& visuals) const;
        void setLightPos(float x, float y, float z) ;  // const // 使用const就變成惟讀
        void setGLSL(int mode);
    void setTexture (int mode);
   
    void setTextureWrapS(int mode);
    void setTextureWrapT(int mode);
   
    void setTextureMagFilter(int mode);
    void setTextureMinFilter(int mode);

private:
    GLuint BuildShader(const char* source, GLenum shaderType) const;
    GLuint BuildProgram(const char* vShader, const char* fShader) const;
    vector<Drawable> m_drawables;
    GLuint m_colorRenderbuffer;
    GLuint m_depthRenderbuffer;
    mat4 m_translation;
       
    UniformHandles m_uniforms;   // new  改成用struct 模式來控制 GLSL 檔中的uniform 變數
    AttributeHandles m_attributes;  // new  改成用struct 模式來控制 GLSL 檔中的 attribute 變數

    GLuint m_gridTexture;  // new in texture
    IResourceManager* m_resourceManager;  // new in texture

    float lightPosX;
    float lightPosY;
    float lightPosZ;
       
    int GLSL_Mode;
   
    int textureMode;
   
    int textureWrapS;
    int textureWrapT;
   
    int textureMagFilter;
    int textureMinFilter;
   

};
 
.....

RenderingEngine::RenderingEngine(IResourceManager* resourceManager)
{
    m_resourceManager = resourceManager;
    glGenRenderbuffers(1, &m_colorRenderbuffer);
    glBindRenderbuffer(GL_RENDERBUFFER, m_colorRenderbuffer);
    GLSL_Mode = 0;
    textureMode = DefaultTexture;
}

//只要與Initialize有關的變數,都要重新啓動
void RenderingEngine::Initialize(const vector<ISurface*>& surfaces)
{
    vector<ISurface*>::const_iterator surface;
    for (surface = surfaces.begin(); surface != surfaces.end(); ++surface) {
       
        // Create the VBO for the vertices.
        vector<float> vertices;

/*         
            //(*surface)->GenerateVertices(vertices);
            (*surface)->GenerateVertices(vertices, VertexFlagsNormals);  // new / replace
*/           
        unsigned char vertexFlags = VertexFlagsNormals | VertexFlagsTexCoords;
        (*surface)->GenerateVertices(vertices, vertexFlags); // now vertexFlags == 3
        //Tell the ParametricSurface object that
        //we need normals by passing in the new VertexFlagsNormals flag.
       

        GLuint vertexBuffer;
        glGenBuffers(1, &vertexBuffer);
        glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
        glBufferData(GL_ARRAY_BUFFER,
                     vertices.size() * sizeof(vertices[0]),
                     &vertices[0],
                     GL_STATIC_DRAW);
       
        // Create a new VBO for the indices if needed.
        int indexCount = (*surface)->GetTriangleIndexCount();
        GLuint indexBuffer;
        if (!m_drawables.empty() && indexCount == m_drawables[0].IndexCount) {
            indexBuffer = m_drawables[0].IndexBuffer;
        } else {
            vector<GLushort> indices(indexCount);
            (*surface)->GenerateTriangleIndices(indices);
            glGenBuffers(1, &indexBuffer);
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
            glBufferData(GL_ELEMENT_ARRAY_BUFFER,
                         indexCount * sizeof(GLushort),
                         &indices[0],
                         GL_STATIC_DRAW);
        }
       
        Drawable drawable = { vertexBuffer, indexBuffer, indexCount};
            m_drawables.push_back(drawable); // 新增drawable至 m_drawables 的尾端,必要時會進行記憶體配置。
    }


       
    // Extract width and height.
    int width, height;
    glGetRenderbufferParameteriv(GL_RENDERBUFFER,
                                 GL_RENDERBUFFER_WIDTH, &width);
    glGetRenderbufferParameteriv(GL_RENDERBUFFER,
                                 GL_RENDERBUFFER_HEIGHT, &height);
   
    // Create a depth buffer that has the same size as the color buffer.
    glGenRenderbuffers(1, &m_depthRenderbuffer);
    glBindRenderbuffer(GL_RENDERBUFFER, m_depthRenderbuffer);
    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);

       
    // Create the framebuffer object.
    GLuint framebuffer;
    glGenFramebuffers(1, &framebuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);

        // 设置FrameBuffer并使用glFramebufferRenderBuffer相互关联
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                              GL_RENDERBUFFER, m_colorRenderbuffer);
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
                              GL_RENDERBUFFER, m_depthRenderbuffer);
    glBindRenderbuffer(GL_RENDERBUFFER, m_colorRenderbuffer);
      
    GLuint program = BuildProgram(SimpleVertexShader, SimpleFragmentShader);
    glUseProgram(program);
       

    // Extract the handles to attributes and uniforms.
    m_attributes.Position = glGetAttribLocation(program, "Position");
    m_attributes.Normal = glGetAttribLocation(program, "Normal");
    m_attributes.DiffuseMaterial = glGetAttribLocation(program, "DiffuseMaterial");
    m_attributes.TextureCoord = glGetAttribLocation(program, "TextureCoord"); // for texture
       
    m_uniforms.Projection = glGetUniformLocation(program, "Projection");
    m_uniforms.Modelview = glGetUniformLocation(program, "Modelview");
    m_uniforms.NormalMatrix = glGetUniformLocation(program, "NormalMatrix");
    m_uniforms.LightPosition = glGetUniformLocation(program, "LightPosition");
    m_uniforms.AmbientMaterial = glGetUniformLocation(program, "AmbientMaterial");
    m_uniforms.SpecularMaterial = glGetUniformLocation(program, "SpecularMaterial");
    m_uniforms.Shininess = glGetUniformLocation(program, "Shininess");
    m_uniforms.Sampler = glGetUniformLocation(program, "Sampler"); // for texture
       
   
    // Set the active sampler to stage 0.  Not really necessary since the uniform
    // defaults to zero anyway, but good practice.
    glActiveTexture(GL_TEXTURE0);
    glUniform1i(m_uniforms.Sampler, 0);
   
    // Load the texture.
    glGenTextures(1, &m_gridTexture);
   

    glBindTexture(GL_TEXTURE_2D, m_gridTexture);
   
//======  Texture filtering  =============================================================
    //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
   
    //glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); only in v1.1
   
    //
   
    if (textureMinFilter == 1)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    else if (textureMinFilter == 2)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
    else if (textureMinFilter == 3)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
    else if (textureMinFilter == 4)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    else if (textureMinFilter == 5)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR);
    else
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
   
   
    if (textureMinFilter == 1)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    else if (textureMinFilter == 2)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
   
      
    //glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, degree); only in v1.1
   
//==============================

   
   
//------  texture coordinate ------------------------------------------------------------
    //glBindTexture(GL_TEXTURE_2D, m_gridTexture);
   
    if (textureWrapS == 1)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
    else if (textureWrapS == 2)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    else
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
   
   
    if (textureWrapT == 1)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
    else if (textureWrapT == 2)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    else
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
   
//--------------------------------
   
    //glHint(GL_GENERATE_MIPMAP_HINT, GL_FASTEST);
   
   
    if (textureMode == 2)
        m_resourceManager->LoadPngImage("Spiked32x32");
    else if (textureMode == 4)
        m_resourceManager->LoadPngImage("大雄128x64");
    else if (textureMode == 3)
        m_resourceManager->LoadPngImage("flake64x64");
    else if (textureMode == 1)
        m_resourceManager->LoadPngImage("Sunflower16x16");
    else if (textureMode == 5)
        m_resourceManager->LoadPngImage("Checkerboard");

    else
        m_resourceManager->LoadPngImage("Grid16");
   
   
    void* pixels = m_resourceManager->GetImageData();
    ivec2 size = m_resourceManager->GetImageSize();
   
    if (textureMode == 5)
        glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, size.x, size.y, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, pixels);
    else
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.x, size.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);

   
    m_resourceManager->UnloadImage(); // 這時已經不需圖檔資源了,已經在GL_TEXTURE_2D中了。
    glGenerateMipmap(GL_TEXTURE_2D); //自動產生Mipmap的小圖
   
    // Set up some default material parameters.
    glUniform3f(m_uniforms.AmbientMaterial, 0.04f, 0.04f, 0.04f);
    glUniform3f(m_uniforms.SpecularMaterial, 0.5, 0.5, 0.5);
    glUniform1f(m_uniforms.Shininess, 50);


        //        // Set up some matrices.
        //        m_translation = mat4::Translate(0, 0, -7);
        //        m_projectionUniform = glGetUniformLocation(simpleProgram, "Projection");
        //        m_modelviewUniform = glGetUniformLocation(simpleProgram, "Modelview");
       
    // Initialize various state.
    glEnableVertexAttribArray(m_attributes.Position);
    glEnableVertexAttribArray(m_attributes.Normal);
    glEnableVertexAttribArray(m_attributes.TextureCoord);  // for texture
    glEnable(GL_DEPTH_TEST);

    // Set up transforms.  (change line position)
    m_translation = mat4::Translate(0, 0, -7);
}
       
void RenderingEngine::Render(const vector<Visual>& visuals) const
{
    glClearColor(0.5f, 0.5f, 0.5f, 1);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    vector<Visual>::const_iterator visual = visuals.begin();
        // 將所有的圖像都畫出,Button上的圖像,使用相對小的Size(64,48), 主圖大小為(320,432)
        // 0~5, 5指的是主畫面
    for (int visualIndex = 0; visual != visuals.end(); ++visual, ++visualIndex) {

        // Set the viewport transform.
        ivec2 size = visual->ViewportSize;
        ivec2 lowerLeft = visual->LowerLeft;
        glViewport(lowerLeft.x, lowerLeft.y, size.x, size.y);
       
        // Set the light position.
        //vec4 lightPosition(0.25, 0.25, 1, 0);
            vec4 lightPosition(lightPosX,lightPosY, lightPosZ,0);
        glUniform3fv(m_uniforms.LightPosition, 1, lightPosition.Pointer());

        // Set the model-view transform.
        mat4 rotation = visual->Orientation.ToMatrix(); // 只有主圖像的旋轉四元值被讀入,並轉為矩陣。
        mat4 modelview = rotation * m_translation;  // m_translation  =[0,0, -7]
        //glUniformMatrix4fv(m_modelviewUniform, 1, 0, modelview.Pointer());
        glUniformMatrix4fv(m_uniforms.Modelview, 1, 0, modelview.Pointer());
       
        // Set the normal matrix.
        // It's orthogonal, so its Inverse-Transpose is itself!
        mat3 normalMatrix = modelview.ToMat3();
        glUniformMatrix3fv(m_uniforms.NormalMatrix, 1, 0, normalMatrix.Pointer());

        // Set the projection transform.
        float h = 4.0f * size.y / size.x;
        mat4 projectionMatrix = mat4::Frustum(-2, 2, -h / 2, h / 2, 5, 10);
        //glUniformMatrix4fv(m_projectionUniform, 1, 0, projectionMatrix.Pointer());
        glUniformMatrix4fv(m_uniforms.Projection, 1, 0, projectionMatrix.Pointer());

            // Set the color.
//            vec3 color = visual->Color;
//            glVertexAttrib4f(m_colorSlot, color.x, color.y, color.z, 1);
//           
        // Set the diffuse color.
        vec3 color = visual->Color * 0.75f;
        glVertexAttrib4f(m_attributes.DiffuseMaterial, color.x, color.y, color.z, 1);
       

        // Draw the surface.
        int stride = sizeof(vec3) + sizeof(vec3) + sizeof(vec2);
        const GLvoid* normalOffset = (const GLvoid*) sizeof(vec3);
        const GLvoid* texCoordOffset = (const GLvoid*) (2 * sizeof(vec3));
        GLint position = m_attributes.Position;
        GLint normal = m_attributes.Normal;
        GLint texCoord = m_attributes.TextureCoord;
        const Drawable& drawable = m_drawables[visualIndex];
        glBindBuffer(GL_ARRAY_BUFFER, drawable.VertexBuffer);
        glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, stride, 0);
        glVertexAttribPointer(normal, 3, GL_FLOAT, GL_FALSE, stride, normalOffset);
        glVertexAttribPointer(texCoord, 2, GL_FLOAT, GL_FALSE, stride, texCoordOffset);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, drawable.IndexBuffer);
        glDrawElements(GL_TRIANGLES, drawable.IndexCount, GL_UNSIGNED_SHORT, 0);
    }
}

.....

    void RenderingEngine::setTextureMagFilter(int mode)
    {
        textureMagFilter = mode;
    }
   
    void RenderingEngine::setTextureMinFilter(int mode)
    {
        textureMinFilter = mode;
    }

}

9. ApplicationEngine.Viewer.cpp

#include "Interfaces.hpp"
#include "ParametricEquations.hpp"

using namespace std;

namespace ParametricViewer {

static const int SurfaceCount = 6;
static const int ButtonCount = SurfaceCount - 1;

struct Animation {
    bool Active;
    float Elapsed;
    float Duration;
    Visual StartingVisuals[SurfaceCount];
    Visual EndingVisuals[SurfaceCount];
};

class ApplicationEngine : public IApplicationEngine {
public:
    //ApplicationEngine(IRenderingEngine* renderingEngine, IResourceManager* resourceManager);   
    ApplicationEngine(IRenderingEngine* renderingEngine);
    ~ApplicationEngine();
    void Initialize(int width, int height);
    void OnFingerUp(ivec2 location);
    void OnFingerDown(ivec2 location);
    void OnFingerMove(ivec2 oldLocation, ivec2 newLocation);
    void Render() const;
    void UpdateAnimation(float dt);
    void SetIconRotateValue(float x, float y, float z);
    void setLightPos(float x, float y, float z);
    void setGLSL(int mode);
    void setTexture(int mode);
    void setTextureWrapS(int mode);
    void setTextureWrapT(int mode);
    void setAutoRotate();
    void setTextureMagFilter(int mode);
    void setTextureMinFilter(int mode);

        //Quaternion get_m_orientation();
        //void set_m_orientation(Quaternion in_orientation);       
private:
    void PopulateVisuals(Visual* visuals) const;
    int MapToButton(ivec2 touchpoint) const;
    vec3 MapToSphere(ivec2 touchpoint) const;
    float m_trackballRadius;
    ivec2 m_screenSize;
    ivec2 m_centerPoint;
    ivec2 m_fingerStart;
    bool m_spinning;
    Quaternion m_orientation;
    Quaternion m_previousOrientation;
    int m_currentSurface;
    ivec2 m_buttonSize;
    int m_pressedButton;
    int m_buttonSurfaces[ButtonCount];
    Animation m_animation;
    bool m_animationAuto;  // add by kirenenko for auto rotating
   
    IRenderingEngine* m_renderingEngine;
    //IResourceManager* m_resourceManager;  //  objViewer only

    float  smallIconRotateXValue;
    float  smallIconRotateYValue;
    float  smallIconRotateZValue;
       
    float  lightPosX;
    float  lightPosY;
    float  lightPosZ;
       
    int    GLSL_Mode;
   
    int    textureMode;
   
    int textureWrapS;
    int textureWrapT;
   
    int textureMagFilter;
    int textureMinFilter;

   
   
        //Quaternion  old_m_orientation;
};
   
IApplicationEngine* CreateApplicationEngine(IRenderingEngine* renderingEngine)
{
    return new ApplicationEngine(renderingEngine);
}

ApplicationEngine::ApplicationEngine(IRenderingEngine* renderingEngine) :
    m_spinning(false),
    m_pressedButton(-1),
    m_renderingEngine(renderingEngine)
{
    m_animation.Active = false;
    m_animationAuto = 1;
    m_buttonSurfaces[0] = 0;
    m_buttonSurfaces[1] = 1;
    m_buttonSurfaces[2] = 4;
    m_buttonSurfaces[3] = 5;
    m_buttonSurfaces[4] = 2;
    m_currentSurface = 3;   //調換一下順序,方便觀察
   
    textureMode = DefaultTexture;
}

ApplicationEngine::~ApplicationEngine()
{
    delete m_renderingEngine;
}

void ApplicationEngine::Initialize(int width, int height)
{
    m_trackballRadius = width / 3;
    m_buttonSize.y = height / 10;
    m_buttonSize.x = 4 * m_buttonSize.y / 3;
    m_screenSize = ivec2(width, height - m_buttonSize.y);
    m_centerPoint = m_screenSize / 2;

    vector<ISurface*> surfaces(SurfaceCount);
    surfaces[0] = new Cone(3, 1);  // 設定半徑及高,其他
    surfaces[1] = new Sphere(1.4f);
    surfaces[2] = new Torus(1.4f, 0.3f);
    surfaces[3] = new TrefoilKnot(1.8f);
    surfaces[4] = new KleinBottle(0.2f);
    surfaces[5] = new MobiusStrip(1);
       
    //m_renderingEngine->setGLSL(GLSL_Mode);
    m_renderingEngine->setTexture(textureMode);
    m_renderingEngine->setTextureWrapS(textureWrapS);
    m_renderingEngine->setTextureWrapT(textureWrapT);
   
    m_renderingEngine->setTextureMagFilter(textureMagFilter);
    m_renderingEngine->setTextureMinFilter(textureMinFilter);

   
   
    m_renderingEngine->Initialize(surfaces);
    for (int i = 0; i < SurfaceCount; i++)
        delete surfaces[i];
}

void ApplicationEngine::PopulateVisuals(Visual* visuals) const
{
    //設定所有圖像的顏色與大小,包含主圖及Button
    for (int buttonIndex = 0; buttonIndex < ButtonCount; buttonIndex++) {
       
        int visualIndex = m_buttonSurfaces[buttonIndex];
        //visuals[visualIndex].Color = vec3(0.25f, 0.25f, 0.25f);
        visuals[visualIndex].Color = vec3(1, 1, 1);
       
       
        if (m_pressedButton == buttonIndex)
            visuals[visualIndex].Color = vec3(0.5f, 0.5f, 0.5f);


            // 設定每一個Button上的圖案大小
//            visuals[visualIndex].ViewportSize = m_buttonSize;
//            visuals[visualIndex].LowerLeft.x = buttonIndex * m_buttonSize.x;
//            visuals[visualIndex].LowerLeft.y = 0;
//            visuals[visualIndex].Orientation = Quaternion(); // 基本button的旋轉值為0
           
            visuals[visualIndex].ViewportSize = m_buttonSize;
            visuals[visualIndex].LowerLeft.x = 0;
            visuals[visualIndex].LowerLeft.y = m_screenSize.y - buttonIndex * m_buttonSize.y ;
            //visuals[visualIndex].Orientation = Quaternion(0,0,0,1); // 基本button的旋轉值為0
           

/* texture code
        visuals[visualIndex].ViewportSize = m_buttonSize;
        visuals[visualIndex].LowerLeft.x = buttonIndex * m_buttonSize.x;
        visuals[visualIndex].LowerLeft.y = 0;
        visuals[visualIndex].Orientation = Quaternion();         
*/
 
            float angleValueX = smallIconRotateXValue;  // -1 ~ 1 , 負值為順時鐘
            float angleX = M_PI_2 *angleValueX;
           
            float sinx = sin(angleX);
            float cosx = cos(angleX);
           
            float angleValueY = smallIconRotateYValue;  // -1 ~ 1 , 負值為順時鐘
            float angleY = M_PI_2 *angleValueY;
           
            float siny = sin(angleY);
            float cosy = cos(angleY);
           
            float angleValueZ = smallIconRotateZValue;  // -1 ~ 1 , 負值為順時鐘
            float angleZ = M_PI_2 *angleValueZ;
           
            float sinz = sin(angleZ);
            float cosz = cos(angleZ);
           
            Quaternion x = Quaternion(sinx*1,sinx*0, sinx*0, cosx);
            Quaternion y = Quaternion(siny*0,siny*1, siny*0, cosy);
            Quaternion z = Quaternion(sinz*0,sinz*0, sinz*1, cosz);
           
            Quaternion xy = x.Rotated(y);
            Quaternion xyz = xy.Rotated(z);
       
            visuals[visualIndex].Orientation = xyz;
        }
       
    // 顯示主圖的顏色,m_spinning代表手指按下的狀態,蓋掉前面所設的值   
    //visuals[m_currentSurface].Color = m_spinning ? vec3(1, 1, 0.75f) : vec3(1, 1, 0.5f); //沒按時是金黃色
    visuals[m_currentSurface].Color = m_spinning ? vec3(1, 1, 0.75f) : vec3(1, 1, 1); // 回復黑白色

    visuals[m_currentSurface].LowerLeft = ivec2(0, m_buttonSize.y);
    visuals[m_currentSurface].ViewportSize = ivec2(m_screenSize.x, m_screenSize.y);
    visuals[m_currentSurface].Orientation = m_orientation; //主圖的旋轉值
}

void ApplicationEngine::Render() const
{
    vector<Visual> visuals(SurfaceCount);
   
    if (!m_animation.Active) {
       //visuals[0]->set_m_orientation(old_m_orientation);
        PopulateVisuals(&visuals[0]);
    } else {
        float t = m_animation.Elapsed / m_animation.Duration;
       
        for (int i = 0; i < SurfaceCount; i++) {
           
            // 找出起始的visuals[x] 及最後的visuals[y]
            const Visual& start = m_animation.StartingVisuals[i];
            const Visual& end = m_animation.EndingVisuals[i];
            Visual& tweened = visuals[i];  // 這時的 visuals[]中是空的
               
            // 以下將所有的值重新填到新的visuals[]中,根據現在所見的狀態
            tweened.Color = start.Color.Lerp(t, end.Color); //將顏色做線性插補取得時間變化值
            tweened.LowerLeft = start.LowerLeft.Lerp(t, end.LowerLeft);
            tweened.ViewportSize = start.ViewportSize.Lerp(t, end.ViewportSize);
            tweened.Orientation = start.Orientation.Slerp(t, end.Orientation);
        }
    }
       
        m_renderingEngine->setLightPos(lightPosX,lightPosY,lightPosZ);
       
    m_renderingEngine->Render(visuals);
}

void ApplicationEngine::UpdateAnimation(float dt)
{
     if (m_animation.Active) {  // 改選成另一個物件
        m_animation.Elapsed += dt;
        if (m_animation.Elapsed > m_animation.Duration)
            m_animation.Active = false;
    }
   
    if (m_animationAuto) {  // 第一次為true,計算主圖的旋轉值
        vec3 axis(0, 1, 0);
        float angle = M_PI / 2;
        Quaternion spin = Quaternion::CreateFromAxisAngle(axis, angle);
        Quaternion rotated = m_orientation.Rotated(spin);
        m_orientation = m_orientation.Slerp(dt / 5, rotated);
    }


   
}
   
.....

    void ApplicationEngine::setAutoRotate()
    {
        m_animationAuto = 1;
    }
   
    void ApplicationEngine::setTextureMagFilter(int mode)
    {
        textureMagFilter = mode;
    }
   
    void ApplicationEngine::setTextureMinFilter(int mode)
    {
        textureMinFilter = mode;
    }

     
// 3. 手指離開
void ApplicationEngine::OnFingerUp(ivec2 location)
{
    m_spinning = false;
   
    m_animationAuto = false;
   
        if (m_pressedButton != -1 && m_pressedButton == MapToButton(location) &&
            !m_animation.Active)  // 如果按選了其他的物件,就進行以下的程序
        {
        m_animation.Active = true;
        m_animation.Elapsed = 0;
        m_animation.Duration = 0.25f;
       
        PopulateVisuals(&m_animation.StartingVisuals[0]);
        swap(m_buttonSurfaces[m_pressedButton], m_currentSurface);  // 點選的Button圖像與主圖像交換
        PopulateVisuals(&m_animation.EndingVisuals[0]);
    }
   
    m_pressedButton = -1;
}

//  1. 壓下手指
void ApplicationEngine::OnFingerDown(ivec2 location)
{
    m_animationAuto = false;
   
    m_fingerStart = location;
    m_previousOrientation = m_orientation;
    m_pressedButton = MapToButton(location); // 取得現在的旋轉值
    if (m_pressedButton == -1)
        m_spinning = true;
}
   
// 2. 移動手指讓物件轉動
void ApplicationEngine::OnFingerMove(ivec2 oldLocation, ivec2 location)
{
    m_animationAuto = false;
   
    if (m_spinning) {
        vec3 start = MapToSphere(m_fingerStart);
        vec3 end = MapToSphere(location);
        Quaternion delta = Quaternion::CreateFromVectors(start, end); // 取得方向向量
        m_orientation = delta.Rotated(m_previousOrientation); // 根據前一個旋轉值,計算再次旋轉後的值,此為主圖所用
    }
   
    if (m_pressedButton != -1 && m_pressedButton != MapToButton(location))
        m_pressedButton = -1;
}

   
// 確認所按的位置為主圖像的位置,並回傳所按的點,經計算後的3D位置。       
vec3 ApplicationEngine::MapToSphere(ivec2 touchpoint) const
{
    vec2 p = touchpoint - m_centerPoint;
   
    // Flip the Y axis because pixel coords increase towards the bottom.
    p.y = -p.y;
   
    const float radius = m_trackballRadius;
    const float safeRadius = radius - 1;
   
    if (p.Length() > safeRadius) {
        float theta = atan2(p.y, p.x);
        p.x = safeRadius * cos(theta);
        p.y = safeRadius * sin(theta);
    }
   
    float z = sqrt(radius * radius - p.LengthSquared());
    vec3 mapped = vec3(p.x, p.y, z);
    return mapped / radius;
}
   
    /*
// 確認按到了Button 的位置,並回傳所按的Button代號。原始的
int ApplicationEngine::MapToButton(ivec2 touchpoint) const
{
    if (touchpoint.y  < m_screenSize.y - m_buttonSize.y)
        return -1;
   
    int buttonIndex = touchpoint.x / m_buttonSize.x;
    if (buttonIndex >= ButtonCount)
        return -1;
   
    return buttonIndex;
}
*/
   
    // 確認按到了Button 的位置,並回傳所按的Button代號。 修改後給 landscape用
    int ApplicationEngine::MapToButton(ivec2 touchpoint) const
    {
        if (touchpoint.x  >  m_buttonSize.x)
            return -1;
       
        //m_screenSize.y - buttonIndex * m_buttonSize.y ;
        int buttonIndex = (touchpoint.y) / m_buttonSize.y;
        if (buttonIndex >= ButtonCount)
            return -1;
       
        return buttonIndex;
    }
   
}


10. 結果,並看不出差異之所在,也許實驗的例子太差的緣故。