


美囯金士頓U盤一直是信得過產品.剛買入Kingston DT101 G2 64GB雖然是USB2.0 『寫入』大約 8M/S.比之前買16GB速度快.但價格低廉.而且U盤在讀寫會閃燈.之前買無閃燈.
| 性能 | 速度 |
| 讀 | 1.5GB |
| 寫 | 8MB/S |
www.bookcard.net


若為Photoshop安裝『圖案PAT』.可以通過『雙擊』載入但可能造成重複載入.
| 版本 | PAT資料夾路徑 |
| PS7.0 | C:\Program Files (x86)\Adobe\Photoshop 7.0\Presets\Patterns |
| PSCC2015 | C:\Program Files\Adobe\Adobe Photoshop CC 2015\Presets\Patterns |

經多日不懈努力.不斷重裝DeepPaint3D、Photoshop終於可以在Windows10通過DeepPaint3D將紋理導出到Photoshop.特將其寫低以免日後忘記.
| 圖層Layer | 簡介 |
| Material:WireFrame | 財質線框圖層 |
| Base Layer:Color | 顏色基楚圖層 |
| 3D模型導入方式 | 簡介 |
| 直接打開 | DeepPaint3D->File->Load |
| DeepUV導出 | DeepUV->File->Export->Paint with Deep Paint 3D |
| 3DSMAX導出 | 3DS MAX9->Right Hemisphere->Deep Paint 3D->Paint Selection |
| 財質導出 | 簡介 |
| Export Material to Photoshop | 導出財質到Photoshop |
| File/Export/Materials->Photoshop | 文件/導出/財質-> Photoshop |

DeepPaint3D強大再於可以在3D模型上直接繪製.並且可以導出Photoshop做細加工.
| 版本 | Photoshop插件資料夾 |
| Photoshop 7.0 | C:\Program Files (x86)\Adobe\Photoshop 7.0\Plug-Ins |
| Photoshop CS | C:\Program Files (x86)\Adobe\Photoshop CS\Plug-Ins |


重裝Windows10後手多多關左『病毒與威脅防護』即Windows Defender.後在網上Download左只軟體.無耐發現大量文檔後綴被加上[unlockmeplease@cock.li].HRM並且出現DECRYPT_INFORMATION.html一睇話你知文檔以被『愛馬仕HERMES』以RSA2048算法加密.只有支付比特幣作為解密贖金.睇來是中左大名頂頂『勒索病毒』.為免中毒加重『文檔被加密』.果斷關機重裝Win10.經點算大量文檔與被加密.好彩有備份數據實制損失十幾篇blog文章與Android代碼. 此病毒與2017年舉世聞名『魔窟WannaCry』同一原理.其毒害之處再於加密所有檔案,而非破壞運行程式.令你有大量數據損失.經此一劫總結如下:







微軟自1993年推出WinNT到現在.都基於當年WinNT架構.發展到當今Win10已非常成熟.與Win9x架構相比系統崩潰機率已大幅減小.Windows安裝也越來越容易.因只120GB磁碟裝Windows10略欠不足.剛好淘汰只固態混合型500GB磁碟.用於做Windows10系統盤剛合適.


若用U盤重裝Win10.你需要進入底板BIOS設定USB啟動


當Win9x盛行年代系統經常崩潰.安裝Windows從最早磁盤引導加載光驅.記得有同學因無法加載光驅驅動,而將Win95分多張1.44磁盤安裝.後期光驅直接引導安裝Windows.到現在USB啟動安裝已很方便.下面以製作Win10安裝U盤為例

使用trueSpace建模後使用DeepUV展開與平鋪UV紋理座標.以下是操作紀要

使用DeepUV進行UV貼圖,在左側『材質』窗口即展開UV坐標.DeepUV可以使用『自動貼圖』Automatic Mapping對模型自動展開UV貼圖.它會基於多種基礎模型貼圖技術展開選區UV座標.但只要模型稍複雜『自動貼圖』都不會按你所期望展開物體.你需要手動貼圖既拆分後展開
| Automatic『自動』 | 簡介 |
| Automatic Mapping | 『自動貼圖』工具所有模型均需先使用此紋理座標展開 |
| Box | 『箱體/方體』或者睇上去四四方方 |
| VAMP | |
| Face | 多邊形面 |
| Unfold | 展開. |
| Planar1 | 平面1.共享UV空間適用於左右對稱多邊形.即紋理座標重疊.以節省紋理貼圖空間 |
| Planar2 | 平面2.共享UV空間適用於左右對稱多邊形.即紋理座標重疊.以節省紋理貼圖空間 |
| Interactive『交互』 | 簡介 |
| Plane | 平面 |
| Sphere | 球體 |
| Cylinder | 圓柱體 |
| Polar |

為3DStudioMax安裝『Deep UV』與『Deep Paint 3D』插件.
| 插件工具 | 簡介 |
| Map Selection | 將模型導出到DeepUV更新UV座標 |
| Switch to Deep UV | 切換到DeepUV |
| Paint Selection | 將模型導出到DeepPaint3D繪畫紋理 |
| Switch to Deep Paint 3D | 切換到DeepUV |
| Auto Create Map | 自動創鍵映射.若3DStudioMax曾經關閉.則需要勾選此項.否則DeepUV與DeepPaint3D無法導出到3DStudioMax |


近日在Windows10-64Bit安裝早年『PS7、PhotoshopCS、PhotoshopCS2』等.但安裝程式『Setup.exe』一直在後臺等待居然無法轉到前臺,它等屬Win32程式Win10理應支持.可以肯定有某個程式佔用系統造成阻塞
而PhotoshopCS2安裝後無法啟動則可通過『相容模式』解決.


當DeepUV『展開』並『緊縮』UV紋理座標後.要將UV紋理座標發送給3DStudioMax保存.當然你可以手動保存File->Save As『保存』3DS文檔更新UV紋理座標.



當用trueSpace建立等離子槍3D模型後.需要展開UV紋理座標.將其在空白位圖上展開鋪平,然後以恰當方式排列.最後為UV貼圖繪製紋理.在DeepUV展開UV座標時需要養成保存工作習慣.『文件/另存為』File->Save As保存為RH3 Scene File(.RH3)

當將模型分別展開UV紋理座標後,需將UV座標排列在灰色正方形中,以後在其上繪畫紋理,其困難在於將所有UV座標都裝在其中,DeepUV有兩方法,最簡單是使用『緊縮所有』Pack ALL工具,DeepUV會將所有UV座標自動排其在灰色正方形,但其排列方式往往唔是你所期望.更好方法是手動排列
| Packing緊縮 | 簡介 |
| Pack Selection | 緊縮所選 |
| Pack All | 緊縮所有 |
| Type緊縮類型 | 簡介 |
| L-Packer(Fast) | 默認快速 |
| Organic(Fast) | 有機快速 |
| Rectangle(Medium) | 矩形,緊縮較慢 |
| Options緊縮選項 | 簡介 |
| Equal Areas | 默認勾選 |
| Preserve Orientation | 保持定向 |
| Space Filing | 空間歸檔 |
| Spacing | 間距默認50% |


DeepUV對『環形』或者『彎曲圓柱體』展開紋理座標需要技巧,因為DeepUV無環形展開,你需要將環形分成兩部分,展開後在連接一起.這裡以展開不完整環形為例

對3D模型展開紋理座標時,絕大部形狀並不規則.將模型分開組件展開.這裡以展開不規則圓柱體為例


trueSpace生成模型後你需要使用3DStudioMax進行『STL檢查』STL Check進一步校驗模是否有錯.如果模型有錯如開洞會引起DeepUV或遊戲引擎出錯。

在為模型製作紋理之前,需轉為STL文檔格式.在3D Studio Max檢查模型是否存在問題如『非閉合模型』『重複頂點』





Android系統通病是越用越慢.近日對台『雜牌古董』Android重寫系統,過程尚算順利. 使用『晶晨燒錄工具』USB Burning Tool進行線擦並把其過程記錄


當想對『磁碟』測速.而手邊又無安裝CrystalDiskMark.可借助Windows10內置測速工具:
D是盤符.但對希捷酷鷹2TB監控盤測量,結果可能有問題.讀取速度達7802.15MB/S
| 命令 | 簡介 |
| winsat disk -drive d | 執行下列所有讀寫測試 |
| winsat disk -drive d -ran -read | 磁盤隨機讀取16 MB時間
Disk Random 16.0 Read |
| winsat disk -drive d -seq -read | 磁盤順序讀取64 MB時間
Disk Sequential 64.0 Read |
| winsat disk -drive d -seq -write | 磁盤順序寫取64 MB時間
Disk Sequential 64.0 Write |
| winsat disk -drive d -flush -seq | 循序寫入的平均讀取時間 |
| winsat disk -drive d -flush -ran | 隨機寫入的平均讀取時間 |


『磁碟』製造商自上世紀未達到萬轉.但一直未有普及.而現役『磁碟』多為5400轉、5900轉、7200轉.可見轉速提高讀寫加快,但工藝與壽命都難以保障.
HD Tune Pro提供『自動噪聲管理』AAM(Automatic Acoustic Management),其原理降低『磁盤』轉速而減小震動和噪聲.從而延長使用壽命.因直接更改『磁碟』固件,所以一次更改永遠有效






近日使用CrystalDiskInfo檢測希捷固態混合磁碟.『磁碟健康狀態』出現黃色警告.而『重定位磁區計數』以達100即已出現壞道.此『磁碟』只用一年多D.睇來只能更換『磁碟』.因為要儘量保障數據安全.聽講HGST『磁碟』故障率最低但價格稍高.而希捷酷鷹為監控而設計.可以長時間大量讀寫數據而不出故障.而且價格比HGST稍低.
網上落單盒裝全新行貨.安裝後分區並格式.使用CrystalDiskMark測『讀寫』速度均接近150MB/S,『機械磁碟』達如此高速,實超我預期.
| 性能 | 簡介 |
| 品牌 | SEAGATE希捷 |
| 系列 | SKYHAWK酷鷹 |
| 型號 | ST200VX008 |
| 接口 | SATA3 SATA 6GBS |
| 轉速 | 5900轉 |
| 容量 | 2TB |
| 級別 | 64路7*24全天監控級 |
| 讀取READ | 149.4MB/S |
| 寫入WRITE | 148.6MB/S |


S.M.A.R.T即『自監測分析與報告技術』據我記憶所及應在上世紀未出現.當時並不清其用途.近日使用CrystalDiskInfo檢測磁碟健康狀態.其工作原理就是讀取磁碟S.M.A.R.T.資訊.磁碟工作其間會自動監測電機、電路、磁盤、磁頭狀態進行分析與記錄.此技術支持『HDD,SSD, SSHD,USB,RAID,NVME』磁碟.在檢測『希捷固態混合磁碟』出現黃色警號.『重定位磁區計數』以達100遠超臨界值36.亦即壞道出現.雖然使用HD Tune Pro進行『錯誤掃描』未見壞區,這只是由於壞道被映射到後備扇區.屬于『磁碟』自我修復.當『磁碟』發現壞區時,便將其物理空間指向到特定無損區域進行重映射修復,從而使硬盤出現壞區仍可使用.但當過多壞道出現.後備扇區消耗殆盡而無法再重映射時,就會出現壞區並不可修復.若繼續使用必造成數據損失.此『磁碟』只使用一年多D睇來質量確實有問題.
| 警訊 | 簡介 |
| 綠色 | 安全使用 |
| 黃色 | 危險警告,更換磁碟轉移數據 |
| 紅色 | 死亡邊緣,備份數據暫停使用 |
| ID | 屬性名稱 | 簡介 |
| 01 | 底層資料讀取錯誤率 | 磁頭讀取數據時錯誤率.只有SSD此值為0 |
| 03 | 碟片啟動時間 | |
| 04 | 馬達啟動關閉次數 | |
| 05 | 重定位磁區計數 | 出現壞區.若數值不斷整長說面壞道擴散 |
| 07 | 尋道錯誤率 | |
| 09 | 總通電時間 | 用於確定是否返修盤 |
| 0A | 啟動重試次數 | 電機重啟量.有可能是USB電壓不足 |
| 0C | 開關電源次數 | |
| B8 | 終端校驗出錯 | |
| BB | 報告不可糾正錯誤 | |
| BC | 通訊超時 | 磁碟無法連接.若非零則可能是電源或獲數據線問題 |
| BD | 磁頭寫入高度 | |
| BE | 氣流溫度 | |
| BF | 加速度錯誤率 | |
| C0 | 電源關閉磁頭收回計數 | |
| C1 | 載入/卸載迴圈計數 | |
| C2 | 硬碟溫度 | |
| C5 | 等候重定的磁區計數 | 假性壞區可修復 |
| C6 | 無法校正的磁區計數 | 不修復壞區量 |
| C7 | UltraDMA高速傳輸模式錯誤次數 |

CrystalDiskMark用於測量各種存儲『HDD、SSD、USB』磁碟速度基準軟件.它由日本開發商Hiyohiyo開發而且是免費軟件.我手頭上只有幾款磁碟.讀寫速度差距非常之大.基本上都以SSD>HDD>USB這樣排列.從下表可睇到『固態混合型』HDD+SSD讀取快寫入慢.而最慢則是USB移動磁碟.
| 磁碟 | 類型 | 讀MB/S | 寫MB/S |
| THNSNH128GBST : 128.0 GB | SSD | 548.1 | 502.7 |
| ST2000VX008-2E3164 : 2000.3 GB | HDD | 149.4 | 148.6 |
| ST500LX025-1U717D : 500.1GB | SSHD | 171.3 | 57.67 |
| ST1000LM024 HN-M101MBB : 1000.2 GB | HDD+USB3.0 | 39.10 | 36.28 |
| 測試 | 簡介 |
| ALL | 執行所有測試 |
| Seq Q32T1 | 順序讀寫測試32隊列,1線程測試 |
| 4KiB Q8T8 | 4KiB分頁隨機讀寫測試8『佇列』,1『執行緒』 |
| 4KiB Q32T1 | 4KiB分頁隨機讀寫測試32『佇列』,1『執行緒』 |
| 4KiB Q1T1 | 4KiB分頁隨機讀寫測試1『佇列』,1『執行緒』 |


若你購買全新磁碟非返修盤,必定是未建立分區.當你連結電源與SATA還需要建立分區.在Windows10有兩種方法分別是『儲存空間』與『磁碟管理』
使用『儲存空間』建立分區並格式化
使用『磁碟管理』建立分區並格式化

DeepUV左則『材質』Material窗口.表示UV座標最終再這裡展平,並限制在灰色正方中.它最終將包含紋理貼圖座標佈局.在縮放各UV選區時要儘量放大.則該區域紋理貼圖中所含細節越多.要進行手動貼圖,需先理解期選擇工具使用
| 選擇工具 | 簡介 |
| Rectangular Selection Tool | 矩形選擇工具, ,按住Shift拖選為多選否則為單選 M鍵 |
| Polygon Wand Selection Tool | 多邊形魔術棒選擇工具,按住Shift點選W鍵 |
| Lasso Selection Tool | 套索選擇工具. L鍵 |
| Selection Tool | 選擇工具 |
| Pan Tool | 手型工具用於移動『材質』Material與『投視』Orthographic視點H鍵 |
| Zoon Tool | 縮放工具,按住滑鼠左鍵移動.或者直接滾動滑鼠滾輪 |
| Move Tool | 移動工具,移動已選頂點.v鍵 |
| Rotate Tool | 旋轉工具,『材質』視點平移.
『投視』視點旋轉左右移動繞Y軸旋轉.而上下移動則繞X軸旋轉 |
| 選擇模式 | 簡介 |
| Ponit Selection Mode | 頂點選擇模式,默認此項 |
| Polygon Selection Mode | 多邊形選擇模式 |
| Component Selection Mode | 部分選擇模式 |
| Element Selection Mode | 元素選擇模式 |
| select選擇命令 | 簡介 |
| Free Transform | 『自由變換』自由縮放與拉伸Ctrl+T |
| ALL | 『全選』Ctrl+A |
| Deselect | 『取消選擇』Ctrl+D |
| 『多選』 | 按住Shift鍵 |
| 『減選』 | 按住ALT鍵 |
| Invert | 『反選』Ctrl+Shift+I |
| Back Faces | 此選項好有用,你無需轉轉模型即可選則背面頂點. |
| Reduce selection to seams | |
| Reduce selection to non-seams |

DeepUV是最優UV貼圖軟件之一.支持使用鍵盤『加速鍵』幫助UV貼圖,即展開UV坐標.但『加速鍵』大部分是隱藏.
| 工具 | 『快捷鍵/加速鍵』簡介 |
| 『自由變換』Free Transform | Ctrl+T |
| 『全選』ALL | Ctrl+A |
| 『取消選擇』Deselect | Ctrl+D |
| 『多選』 | Shift |
| 『反選』Invert | Ctrl+Shift+I |
| 『切換已打開3D模型』Next | Ctrl+Tab |
| 『切換已打開3D模型』Previous | Ctrl+Shift+Tab |
| 撤銷 | Ctrl+Z |
| 恢復 | Ctrl+Y |
| 『矩形選擇工具』Rectangular Selection Tool | M鍵 |
| 『多邊形魔術棒選擇工具』Polygon Wand Selection Tool | W鍵 |
| 『套索選擇工具』Lasso Selection Tool | L鍵 |
| 『選擇工具』Selection Tool | |
| 『手型工具用於移動視點』Pan Tool | H鍵 |
| 『縮放工具』Zoon Tool | |
| 『移動工具』Move Tool | V鍵 |
| 『旋轉工具』Rotate Tool | |
| 『頂視圖』Top | SHIFT+T |
| 『前視圖』Front | SHIFT+F |
| 『左視圖』Left | SHIFT+L |
| 『底視圖』Underneath | SHIFT+U |
| 『後視圖』Back | SHIFT+B |
| 『右視圖』Right | SHIFT+R |



購買Godaddy虛擬主機,均免費贈送你私有域名電子郵箱.你可在名片印以你公司域名獨有電郵.但你要設定電郵帳戶與Outlook賬戶才能啟用.
1. 登入Godaddy帳戶->我的產品
2. 個人電郵->Workspace 電子信箱->管理所有
3. 在Workspace Control Center創建電郵帳戶Create a new account
4. Email Address:填電郵地址如:XXX@bookcard.net
5. Password 與Confirm Password:填電郵密碼,不小於5個字符.
6. 設定郵箱空間配額Quota:我這裡填1000MB
設定Outlook電郵賬戶
1. 賬戶資訊->新增賬戶
2. 填電郵帳戶如:XXX@bookcard.net
3. 『進階選項』->勾選『我要手動設定我的帳戶』
4. 選擇『POP』類型郵箱
5. 內送郵件『伺服器』pop.secureserver.net
6. 內送郵件『連接埠』995
7. 勾選『此伺服器需要加密連線(SSL/TLS)』
8. 外寄郵件『伺服器』smtpout.secureserver.net
9. 外寄郵件『連接埠』465
10. 『加密方法』選『SSL/TLS』
11. 填電郵『密碼』然後『連線』
後期設定Outlook電郵帳戶
1. 『賬戶資訊』選擇pop/smtp電郵帳戶
2. 賬戶設定->管理設定檔
3. 帳戶設定->電子郵件
4. 雙擊編輯電郵帳戶.內容與上面設定一至不再重複.

DeepUV是由Right Hemisphere公司開發用於UV展開貼圖專業軟件.即把重設UV座標然後在3D模型文檔中更新.但其網站http://www.righthemisphere.com以無法訪問.因為手動移動UV座好難正與紋理對齊.而DeepUV可以不管模型形狀而占開紋理座標.而唔會影響3D模型形狀
| 3D格式 | 簡介 |
| Autodesk 3D Studio(*.3ds) | 3D Studio MAX所用格式 |
| Lightwave 3D and Binary Object(*.lwo,*lw) | LightWave3D所用格式 |
| Wavefront Object(*.obj) | Maya所用格式 |
| RH3 Scene File(*.rh3) | |
| Right Hemisphere Binary(*.rh) | |
| SOFTIMAGE XSL(*.xsi) | SOFTIMAGE 3D已停止開發 |

trueSpace建模與鍵盤配合『快捷鍵』.特別投影切換小鍵盤非常好記出中心5是透視,其它數字按方向排列.不過trueSpace『快捷鍵』有Bug它並非是全域鍵.若焦點位於某面板輸入框中『快捷鍵』便失效,此時最好用滑鼠點擊『對象工具』Object tool
| 工具 | 快捷鍵 |
| 旋轉對象『Object Rotae』 | R |
| 移動工具 | G |
| 縮放對象 | X |
| 啟用/禁用X軸導航 | A |
| 啟用/禁用Y軸導航 | Y |
| 啟用/禁用Z軸導航 | Z |
| 『對象工具』Object tool | Space空格 |
| 『將視點對齊對象模型』Look at Current Object | V |
| 關閉所有面板 | Backspace退格 |
| 彈所有出面板 | Tab |
| 拷貝所選模型 | Ctrl+C |
| 撤消操作 | Ctrl+Z |
| 刪除所選模型 | Del |
| 選擇/切換前後對象 | Left/Right |
| 使用箭頭鍵在層次結構內移動 | Up/Down |
| 頂正交投影視圖 | 小鍵盤0 |
| 前正交投影視圖 | 小鍵盤1 |
| 前正交投影視圖 | 小鍵盤2 |
| 後背正交投影視圖 | 小鍵盤3 |
| 左正交投影視圖 | 小鍵盤4 |
| 透視投影視圖 | 小鍵盤5 |
| 右正交投影視圖 | 小鍵盤6 |
| 頂正交投影視圖 | 小鍵盤7 |

『月光光 照地塘 年卅晚 摘檳榔 檳榔香 買紫薑 紫薑辣 買芙薘 芙薘苦 買豬肚 豬肚肥 買牛皮 牛皮薄 買菱角 菱角尖 買馬鞭 馬鞭長 起屋樑 屋樑高 買張刀 刀切菜 切死兩個紅毛番鬼仔』
整首歌謠大部分以接龍形式,所出現之物大都並無關聯.但最後卻現殺機.它與廣州城一段歷史有關.1840年第一次鴉片戰爭.後再在1842年簽訂南京條約,清答應英國五口通商.之一就是進入廣州城.而到1858年廣州城依然唔肯讓英人進入廣州城內.英人只能在十三行居住.原因是兩廣總督葉名琛以廣州城內有刁民見紅毛番鬼就摞刀劈.為保障英人安全不讓進廣州城內.其實英人早已得知是葉名琛給錢地痞.讓其只止要見鬼頭就摞刀劈.最後葉名琛於1857年被英軍俘虜.

使用trueSpace怪獸建模.因為最後應用手遊.應儘量減小多邊形.最好在超過2千個多邊形左右.因為模型左右對稱.只需要創建一半模型之後再鏡像.可以減小紋理尺寸.怪獸分3部分建造.

trueSpace使用NURBS完成建模後,要將其組合或導出時.需將NURBS面片轉化為多面體.其實就是將由『曲線』組成面片轉為『直線』組成多邊形
| Patch Options『面片選項』 | 簡介 |
| Static res『靜態分辨率』 | 生成NURBS分辨率,默認值為0.67.修改為0.2~0.3 |
| Manipulation res『操縱分辨率』 | 改變模型形式多邊形顯示分辨率. 默認值為0.3若建模過程遲緩不連貫.請降低此值. |


怪獸『手臂』要比『大腿』複雜.而且戴上腕箍暗示其『無性』.手指長長指甲暗示其具有『攻擊性』.

之前介紹trueSpace使用NURBS擠壓模型.但還有一種繪畫『樣條曲線』然後拉伸位NURBS模型方式

NURBS全稱Non-Uniform Rational B-Splines.漢譯為『非均勻有理B樣條』.『B樣條』其實就是『貝賽爾(Bezier)曲線』, NURBS為高密度網格但呈現出平滑外型.trueSpace作為與『3D Studio MAX』與『Maya』同級別三維軟件當然也支持NURBS.建模過程與使用『橡皮泥』手工製作模型非常類似.剛開此使用時你可能不習慣.但好快會喜歡它. NURBS建模缺點是顯著增加多邊形量.但NURBS建模速度快.將會是未來遊戲建模方式.但現役手機GPU圖形處理能力還差一大截.控制多邊形數量還很必要.
| Patch Options『面片選項』 | 簡介 |
| Static res『靜態分辨率』 | 生成NURBS分辨率,默認值為0.67.修改為0.3 |
| Manipulation res『操縱分辨率』 | 改變模型形式多邊形顯示分辨率. 默認值為0.3若建模過程遲緩不連貫.請降低此值. |

NURBS模型在建模過程中可能會產生多餘『曲線』.模型曲線越多最終多邊形轉換越多.刪除曲線是trueSpace優化模型最重要手段.

NURBS全稱Non-Uniform Rational B-Splines.漢譯為『非均勻有理B樣條』.『B樣條』其實就是『貝賽爾(Bezier)曲線』, NURBS為高密度網格但呈現出平滑外型.trueSpace作為與『3D Studio MAX』與『Maya』同級別三維軟件當然也支持NURBS.剛開此使用NURBS建模可能不習慣.但你好快會喜歡它. NURBS建模缺點是顯著增加多邊形量.NURBS建模將會是未來遊戲建模方式.但現役手機GPU圖形處理能力還差一大截.控制多邊形數量還很必要.
| Patch Options『面片選項』 | 簡介 |
| Static res『靜態分辨率』 | 生成NURBS分辨率,默認值為0.67 |
| Manipulation res『操縱分辨率』 | 改變模型形式多邊形顯示分辨率. 默認值為0.3若建模過程遲緩不連貫.請降低此值. |

trueSpace『3D控制器』它可以幫助你.對『模型』『點』『邊』『面』進行操作. 『3D控制器』包裹著模型,每條邊分為三段『中間』移動與『兩則』縮放『圓球』旋轉.操作時切換到『正交投影』已保正沿某軸完成操作.要顯示『視點控制器』必須打開『3D控制器』.打開『設置』preferences勾選3D Controls.要隱藏即取消勾選.
| 3D控制器 | 簡介 |
| 中間 | 沿某軸『移動』MOVE模型 |
| 兩則 | 沿某軸『縮放』SCALE模型 |
| 軸旋轉球 | 左鍵點擊『圓球』彈出. 繞某軸『旋轉』ROTATE模型 |





3D建模之前需先確定模形尺寸,等離子槍給4米高史前食人怪使用武器.而等離子槍長約身高一半大約2米.槍托到彈夾則大約1米.多邊形數量應儘量低,其外觀主要依靠紋理.你需要一張建模用參考圖.長寬比為2比1.
1. 設置等離子槍參考圖平面
2. 構建槍口,其實是兩個相連蛋形球體.在場景中添加球體,緯度與經度均為12.然後旋轉90度,通過縮放移動拉伸與參考圖相近.選擇並刪除前四個分段.
3. Ctrl+C複製球體,並刪除前兩個分段.通過縮放移動拉伸與參考圖相近.切換到正交視圖並鎖定X軸.選擇最前端頂點,通過移動移下方球體拼排.
4. 將兩個球體補洞,使用『合拼對象』工具,將兩個球體結合為槍口.如果兩面完全對齊.則會只有一個前側面.選擇槍口『面』通過 『拉伸』與 拉伸縮放 形成喇叭口.
5. 在喇叭口中添加10邊『全柱體』或『圓錐體』, 通過『銑削』挖空槍口.
6. 等離子槍管其實就八面『全柱體』並旋轉90度.選擇槍管背『面』通過 『拉伸』形成槍托.對槍托頂部與背面使用『倒角』工具.通過『銑削』偷空槍管,
7. 添加十面『全柱體』置於槍管中心,然後『合拼對象』.
8. 創建12分段『圓環』作為彈夾置於槍托之上『合拼對象』.
9. 添加十面『全柱體』然後橫向擠壓形成槍把.通果『球體』作為『銑削』工具形成握手
10. 在槍把下方通過『拉伸』形成兩把刀片,刀尖通過『尖端』工具生成
11. 在握手前方安裝板機,生成六面『全柱體』.選擇最前端『邊』通過移動形成內凹體.選擇最底『面』通過『拉伸』與旋則形成板機.與槍管『合拼』
12. 在槍口上則製作環形吊帶.生成12分段『圓環』與『圓形圓柱體』槍口上方,並與槍口『合拼』
13. 在槍口下方製作能量傳送管. 生成兩個六面『全柱體』作為管箍.分別置於槍口與槍管下方並『合拼』.對管箍進行『倒角』後使用『拉伸』拉出軟管.最後使用『連接邊』工具連接軟管.
14. 優化模型.縮減多邊形量
15. 對模型進行三角剖分.
16. 導出STL模型

『添加面』Add Face工具可以對環形邊進行『補洞』.但若像上圖鏈接環形管.則trueSpace需要使用『添加邊』Add Edges工具.

當模型完成『三角剖分』後.需要把模型導出為Maya與3D Studio Max等程序可以理解STL文檔格式.STL格式全稱為stereo lithography『立體』.最早用於模具製作.3D打印切片軟件正是使用這種格式.當你導出STL格式後.就可為生成『紋理』圖作準備,即展開UV紋理座標,將它地平鋪.從而在PhotoShop中處理『紋理』圖.將模型導出為STL格式:


大部分3D建模書籍都會在建模完成對模性進行優化.其實在最後才發現模型有致命缺陷,最好是在建模過程中進行優化.因為要儘量降低多變形量.無需百分百按照原圖建模.降低模型多邊形量其實就清除多餘『面』,『邊』,『點』

trueSpace『焊接點』Wled Vertices工具即是將兩個或多個『點』合拼為一個.合併『點』其實也是刪除『邊』與『面』.選擇要合拼兩個相鄰『點』之間『邊』.然後使用『焊接點』工具合併『點』但此方法經常出現奇怪問題.正確方法如下
| Point edit點編 | 簡介 |
| Highlight | 對以選『面』『點』『邊』高亮顯示 |
| Coincident Weld | 重合焊縫距離,默認為0.05,可適當調整此值 |

若把模型削尖,可先『伸展』後將頂『點』合併. 而trueSpace提供『尖端』Tip工具.若想生成多個分段可右擊『尖端』Tip工具,編輯Segments屬性.
| Tip屬性 | 簡介 |
| Segments | 尖端分段,最小默認1 |
| X | X軸偏移量,默認值0 |
| Y | Y軸偏移量,默認值0 |
| Z | 尖端長度默認值0.5 |
| Bend | 彎曲未知用途 |

在添加『邊』Edge後, trueSpace有可能生成非封閉面.若無產生新著色區域,需使用『添加面』Add Face工具.此工藝稱為『補洞』.滑鼠右擊『添加面』Add Face工具打開Point edit屬性,一定要啟用高亮顯示.否則無法睇到是否正確選中.
| Point edit屬性 | 簡介 |
| Quadrangles | 動態細分四邊形,默認禁用 |
| Triangles | 動態細分三角形,默認禁用 |
| No DynDiv | 點編輯時無動態細分,默認啟用 |
| Highlight | 高亮顯示,默認啟用 |
| Coincident Weld | 重合焊接,用於移除焊接點.默認為0.05.相當於點焊接容差值.默認啟用 |

在trueSpace中『倒角』Bevel工具,它並不是直接修改模型.而是伸展『面』Face後『縮小』後形成『倒角』.
| 倒角屬性 | 簡介 |
| Bevel | 斜角拉伸高度 |
| Angle | 斜角角度 |

所有3D模型均有多個『面』所組成,trueSpace刪除工具有兩款.
| 刪除工具 | 簡介 |
| Delete Face | 選擇『刪除面』工具,然後直接點選『面』Face |
| Erase Vertices | 與『選擇工具』相配合先選擇『面』『點』『邊』然後再點擊『刪除點』工具 |

trueSpace有四個選擇工具,支持『面』Face『點』Vertex『邊』Edge.與『點編輯』工具相配合使用.滑鼠右鍵點擊彈出Selection勾選『背面』和『高亮』.選擇時按CTRL鍵隱藏『3D控制器』,而且最好在『正交投影』下操作
| 選擇工具 | 簡介 |
| Select using Rectangle | 滑鼠拖放多選 |
| Select using Free-Hand | 滑鼠移動多選 |
| Select using Lasso | 滑鼠繪畫套索多選 |
| Named selection | 對『面』『點』『邊』進行命名,按『名』Named選擇 |
| 點編輯工具需與選擇工具相配合使用. | 簡介 |
| Point Edit:Vertices | 選擇『點』Vertex |
| Point Edit:Edges | 選擇『邊』Edge |
| Point Edit:Faces | 選擇『面』Face |
| Point Edit:Context | 自動選擇 |
| 按鍵 | 簡介 |
| CTRL | 多選並隱藏『3D控制器』3D Controls |
| SHIFT | 取消選擇 |
| Selection設定 | 簡介 |
| Backside | 選擇時連背『面』FACE也自動選擇 |
| highlight. | 對以選『面』『點』『邊』高亮顯示 |
| 選擇 | 簡介 |
| Face | 面 |
| Vertex | 點 |
| Edge. | 邊 |

你明明沒有在C盤安裝很多『軟件』或『遊戲』.卻Windows10經常報『磁盤空間不足』首先刪除所有『臨時文檔』和『禁用休眠』
若依然如事.可以嘗試查找大於128M

在Maya建模使用『擠壓』Extrude工具進行『盒式』建模.而trueSpace這工具稱為『伸展』Sweep.

trueSpace在新建模型時會彈出『魔力環』,單按右鍵『魔力環』則會消失再無法調出.但可通過左鍵點擊物體彈出『3D控制器』.務必要熟識此工具.否則建模會很困難. 『3D控制器』包裹著模型,每條邊分為三段『中間』與『兩則』.操作時切換到『正交投影』已保正沿某軸完成操作. 若沒有彈出『3D控制器』可打開 preferences 勾選『3D Controls』
若單純通過『移動』『縮放』『旋轉』工具操作.可先隱藏『3D控制器』打開『設置』preferences.取消勾選3D Controls.然後選擇『模型』『點』『邊』『面』進行操作.若鎖定某軸『移動』『縮放』『旋轉』只對某軸進行,可禁用其它兩軸,在『透視投影』X軸橫向Y軸縱向Z垂直於網格.而『正交投影』則有點不同X軸橫行Y軸垂直.
| 3D控制器 | 簡介 |
| 中間 | 沿某軸『移動』MOVE模型 |
| 兩則 | 沿某軸『縮放』SCALE模型 |
| 軸旋轉球 | 左鍵點擊『圓球』彈出. 繞某軸『旋轉』ROTATE模型 |
你也可以通過『object info』精確控制模型,滑鼠右擊白色箭頭彈出
| 對像信息 | 簡介 |
| Location | 位置 |
| Rotation | 旋轉 |
| Size | 尺寸 |
| Name | 模型名 |

學習trueSpace建模需要理解其座標系統.而trueSpace座標軸與OpenGL座標軸有所不同.
| 座標軸 | 簡介 |
| X軸 | 網格橫向為X軸,左則為負右則為正 |
| Y軸 | 網格縱向為Y軸,指向屏幕為負反向則為正 |
| Z軸 | Z軸則垂直於網格下負上正. |
要在3D場景中移動『位置』與『視點』可通過『view control』巡覽指示器.它通常在屏幕右下角.但有個更好用工具,按V鍵將視點對齊對象模型『Look at Current Object』
| 工具 | 簡介 |
| 半圓 | 繞『對象/視點』旋轉 |
| 圓套 | 拖動則移動控制器,而點擊則修改屬性 |
| 橫軸 | 左鍵繞Y軸旋轉,右鍵繞XZ軸旋轉 |
| 橫軸箭頭 | 視點沿X軸移動 |
| 縱軸 | 左鍵繞X軸旋轉,右鍵繞ZY軸旋轉 |
| 縱軸箭頭 | 視點沿Z軸移動 |
| 垂直軸 | 左鍵繞Z軸旋轉,右鍵繞XY軸旋轉 |
| 垂直箭頭 | 視點沿Y軸移動 |
| 步行 | 按滑鼠移動方向移動 |
| XY平面 | 左鍵XY平面移動,沿Z軸移動 |
| YZ平面 | 左鍵YZ平面移動,沿X軸移動 |
| XZ平面 | 左鍵XZ平面移動,沿Y軸移動 |
| 雙下箭頭 | 飛行模式 |
| 單下箭頭 | 步行模式 |
| 十字箭頭 | 拖放實現控制器縮放 |
| 軸 | 關閉『view control』巡覽指示器 |
| 關閉 | 關閉屬性 |

3D建模你需要『素描圖』或『照片』作為參考圖,最常用方法是創建『plane』平面.然後貼上參考圖

在3D建模過程中你可能會忽略模型尺寸.雖然你可以通過『縮放』與遊戲相適配.良好建模習慣是預先計算模型尺寸.『3D建模環境』中1米等於『3D遊戲世界』中1米.准好使用公制標準,因為大部分遊戲引擎都以此為標準.
經設定後trueSpace網格單位為1m2.其網格長寬均24格.亦即長寬均24米『Meters』.而『object info』面板也應將其保留.可以隨時睇到『#vertices』頂點量與『#faces』三角量.
設定對象尺寸與角度,位置.
| Location | 『位置』 |
| Rotation | 『旋轉』角(0~360) |
| Size | 『尺寸』以『Obj.Units』為單位 |

『跟隨相機』與『歐拉相機』喂一區別在於屬性設置不同.跟隨相機常將它固定在移動物體上.它需要以下屬性:
3D空間位置position
向上向量.相當於在相機上貼上一個向上箭頭up
視點向量即相機視口朝向目標lookAt
遠裁剪面far
近裁剪面near
『視場』即視口角度fieldOfView
視口縱橫比aspectRatio
再移動相機時你需要分被相機『位置』與『視點』.『跟隨相機』生成代碼:
設定相機視口,寬與高為屏幕分辨率
gl.glViewport(0,0,width,height);
設置相機矩陣,將當前堆棧設為投影矩陣
gl.glMatrixMode(GL10.GL_PROJECTION);
棧頂載入單位矩陣
gl.glLoadIdentity();
設置透視投影矩陣.定義視錐體參數.『視口角度』『視口縱橫比』『遠近裁剪面』
GLU.gluPerspective(gl, fieldOfView, aspectRation, near, far);
將當前堆棧設為模型視圖矩陣
gl.glMatrixMode(GL10.GL_MODELVIEW);
棧頂載入單位矩陣
gl.glLoadIdentity();
生成方位矩陣,好處在於能防止出現弄反位置或角度
GLU.gluLookAt(gl, position.x,position.y,position.z,
lookAt.x, lookAt.y, lookAt.z,
up.x, up.y, up.z);

歐拉相機即第一稱射擊遊戲中時用相機.你需要定義以下屬性
『視場』即視口角度fieldOfView
視口縱橫比aspectRatio
遠裁剪面far
近裁剪面near
3D空間位置position
繞y軸旋轉角yaw
繞x軸旋轉角pitch.角度值-90~+90度之間.這類似於頭部傾斜角.若超過這角度則造成骨折.
設定歐拉相機視口,寬與高為屏幕分辨率
gl.glViewport(0,0,width,height);
設置歐拉相機矩陣.首先將當前堆棧設為投影矩陣
gl.glMatrixMode(GL10.GL_PROJECTION);
棧頂載入單位矩陣
gl.glLoadIdentity();
設置投影矩陣.你需定義要視錐體
GLU.gluPerspective(gl, fieldOfView, aspectRatio, near, far);
設為模型視圖矩陣
gl.glMatrixMode(GL10.GL_MODELVIEW);
棧頂載入單位矩陣
gl.glLoadIdentity();
繞x軸旋轉角度
gl.glRotatef(-pitch,1,0,0);
繞Y軸旋轉角度
gl.glRotatef(-yaw,0,1,0);
移動相機,相機位於原點且鏡頭指向z軸負方向
gl.glTranslatef(-position.x,-position.y,-position.z);
獲取相機方向
相機未旋轉時指向z軸負向
float[] inVec = {0,0,-1,1};
float[] outVec = {0,0,0,0};
設置單位矩陣
float[] matrix = {0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0};
Matrix.setIdentityM(matrix,0);
繞x軸俯仰
Matrix.rotateM(matrix, 0, yaw,0,1,0);
繞Y軸旋轉
Matrix.rotateM(matrix, 0, pitch,1,0,0);
將矩陣和向量相乘的單位方向向量
Matrix.multiplyMV(outVec, 0, matrix, 0, inVec, 0);
direcion.set(outVec[0],outVec[1],outVec[2]);

當相機遠離模型時,模型也會變小.其紋理渲染採樣時會顆粒狀失真.解決失真問題關鍵在於讓屏幕中體積較小物體或遠離視點時.使用低分辯率『紋理Texture』圖像.稱值為紋理鏈.首先獲取紋理尺寸.然後創建比小分辨率紋理圖,把分辨率縮小一半.重複這一過程直到分辨率為1.為了在OpenGL ES使用紋理鏈,需要執行以下兩步
紋理位圖載入與綁定.在其基礎加入紋理鏈代碼
GL10 gl = GRAPHICS.gl;
生成空的紋理對象,並獲取id
gl.glGenTextures(1, ids,0);
int id = ids[0];
讀取紋理圖
Bitmap bitmap = BITMAP.Read(file_name);
綁定紋理ID
gl.glBindTexture(GL10.GL_TEXTURE_2D, id);
設置紋理屬性,紋理過濾器,指定縮小倍數過濾器
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR_MIPMAP_NEAREST);
指定放大倍數過濾器
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
獲取紋理寬度與高度
int width = bitmap.getWidth();
int height = bitmap.getHeight();
原始圖層索引為0
int level = 0;
while(true) {
上傳紋理
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, level, bitmap, 0);
圖層索引號加一
++level;
計算縮小一半紋理位圖寬與高
int newWidth = bitmap.getWidth()/2;
int newHeight = bitmap.getHeight()/2;
寬與高等於零時跳出循環
if(newWidth == 0)
break;
創建縮小一半紋理位圖
Bitmap newBitmap = Bitmap.createBitmap(newWidth,newHeight,bitmap.getConfig());
Canvas canvas = new Canvas(newBitmap);
canvas.drawBitmap(bitmap,
new Rect(0,0,bitmap.getWidth(),bitmap.getHeight()),
new Rect(0,0,newWidth,newHeight),
null);
回收位圖資源
bitmap.recycle();
bitmap = newBitmap;
}
取消邦定紋理ID
gl.glBindTexture(GL10.GL_TEXTURE_2D, 0);
回收位圖資源
bitmap.recycle();

射燈是OpenGL ES中燈光中最耗GPU資源燈光.但其3D效果十分逼真.睇上圖.射燈有多個參數需指定.『位置』『照射方向』『照射角度』小於180度、『衰減指數』小於零,若設為0則亮度不衰減,實制『衰減值』與遊戲3D空間尺寸有關.這裡設為0.01.以及三個光照顏色『環境色』『漫反射色』『鏡面反射色』
public class LightSpot {
private float[] ambient = {1.0f, 1.0f, 1.0f, 1.0f};// 射燈-環境色
private float[] diffuse = {1.0f, 1.0f, 1.0f, 1.0f};// 射燈-漫反射色
private float[] specular = {0.0f, 0.0f, 0.0f, 1.0f};// 射燈-高光顏色
private float[] position = {0.0f, 3.0f, 0.0f, 1.0f};// 射燈-位置
private float[] direction = {0.0f, -1.0f, 0.0f, 0.0f};// 射燈-方向
private float cutoff = 45;// 射燈-角度範圍,缺省為180.0
private float exponent = 0.01f;// 衰減指數,缺省為0.0f
int id = 0;// 光照ID
// 射燈ID 輸入:GL10.GL_LIGHT0至GL10.GL_LIGHT7
LightSpot(int ID){
this.id = ID;
}
//設置射燈方向,轉換為方向向量.最後w分量設0,代表方向
public void setDirection(float x,float y,float z){
direction[0] = x – position[0];
direction[1] = y – position[1];
direction[2] = z – position[2];
direction[3] = 0;
}
// 設定射燈位置.w分量設為1代表位置向量
public void setPosition(float x,float y,float z){
position[0] = x;
position[1] = y;
position[2] = z;
position[3] = 1;
}
// 設定射燈顏色
public void setColor(float r,float g,float b){
ambient[0] = r;
ambient[1] = g;
ambient[2] = b;
ambient[3] = 1;
}
//使能射燈
public void enable(){
GL10 gl = GRAPHICS.gl;
gl.glEnable(id);//使能
gl.glLightfv(id,GL10.GL_AMBIENT, ambient, 0);// 射燈-環境色
gl.glLightfv(id,GL10.GL_DIFFUSE, diffuse, 0);// 射燈-漫反射色
gl.glLightfv(id,GL10.GL_SPECULAR, specular, 0);// 射燈-高光顏色
gl.glLightfv(id,GL10.GL_POSITION, position, 0);// 位置
gl.glLightfv(id,GL10.GL_SPOT_DIRECTION, direction, 0);// 方向
gl.glLightf(id,GL10.GL_SPOT_CUTOFF,cutoff); // 角度範圍
gl.glLightf(id,GL10.GL_SPOT_EXPONENT,exponent);// 衰減指數
}
// 屏蔽射燈
public void disable(){
GL10 gl = GRAPHICS.gl;
gl.glDisable(id);
}
}

物體都由特定材質構成.材質決定照射在物體上光返射方式並會改變反射光顏色.材質為多邊形設置材質屬性用於光照計算,它是全域性影響所有繪製多邊形,直到它在次調用.OpenGL ES中需要為每種材質指定『環境色』『漫反射色』『鏡面反射色』RGBA顏色值.此材質吸收光.只有『光源顏色』與『材質顏色』最小值運算(RGB值)得到『反射光顏色』
| 材質顏色 | 光源顏色 | 反射光顏色『最小值運算』 |
| (0,1,0) | (1,1,1) | (0,1,0) |
| (0,1,0) | (1,0,0) | (0,0,0) |
| (0,0,1) | (1,1,1) | (0,0,1) |
材質類代碼
public class Material {
private float[] ambient = {1.0f, 1.0f, 1.0f, 1.0f};// 材質-環境色
private float[] diffuse = {1.0f, 1.0f, 1.0f, 1.0f};// 材質-漫反射色
private float[] specular = {1.0f, 1.0f, 1.0f, 1.0f};// 材質-鏡面顏色
//設置材質環境色
public void setAmbient(float r,float g,float b){
ambient[0]=r;
ambient[1]=g;
ambient[2]=b;
ambient[3]=1;
}
// 設置材質漫反射色
public void setDiffuse(float r,float g,float b){
diffuse[0]=r;
diffuse[1]=g;
diffuse[2]=b;
diffuse[3]=1;
}
// 設置材質鏡面反射色
public void setSpecular(float r,float g,float b){
specular[0]=r;
specular[1]=g;
specular[2]=b;
specular[3]=1;
}
//使能材質
public void enable(){
GL10 gl = GRAPHICS.gl;
gl.glMaterialfv(GL10.GL_FRONT_AND_BACK,GL10.GL_AMBIENT, ambient, 0);// 環境色
gl.glMaterialfv(GL10.GL_FRONT_AND_BACK,GL10.GL_DIFFUSE, diffuse, 0);// 漫反射色
gl.glMaterialfv(GL10.GL_FRONT_AND_BACK,GL10.GL_SPECULAR, specular,0);// 鏡面反射色
}
}

定向光具有方向但沒有位置.首先在3D空間定義一個點,而『方向』表示為該點指向『原點』向量之方向.若在3D空間右則射過來定向光
public class LightDirectional {
private float[] ambient = {1.0f, 1.0f, 1.0f, 1.0f};// 點光-環境色
private float[] diffuse = {1.0f, 1.0f, 1.0f, 1.0f};// 點光-漫反射色
private float[] specular = {0.0f, 0.0f, 0.0f, 1.0f};// 點光-高光顏色
private float[] direction = {0, 0, -1, 0};// 定向光-方向
int id = 0;// 燈光ID
構造定向光.燈光id:GL10.GL_LIGHT0至GL10.GL_LIGHT7
public LightDirectional(int ID){
this.id = ID;// 燈光ID
}
設置方向,將位置轉換為方向.將w分量設0,代表方向
public void setDirection(float x,float y,float z){
direction[0] = -x;
direction[1] = -y;
direction[2] = -z;
direction[3] = 0;
}
設置定向光顏色
public void setColor(float r,float g,float b){
ambient[0] = r;
ambient[1] = g;
ambient[2] = b;
ambient[3] = 1;
}
使能設置頂向光顏色環境色、漫反射色、高光顏色、位置.最後一個參數是數組索引
public void enable(){
GL10 gl = GRAPHICS.gl;
gl.glEnable(id);//使能
gl.glLightfv(id,GL10.GL_AMBIENT, ambient, 0);
gl.glLightfv(id,GL10.GL_DIFFUSE, diffuse, 0);
gl.glLightfv(id,GL10.GL_SPECULAR, specular, 0);
gl.glLightfv(id,GL10.GL_POSITION, direction, 0);
}
定向光屏蔽
public void disable(GL10 gl){
gl.glDisable(id);
}
}

點光(燈光)特點是有固定3D位置.首先你需要啟用0號燈光
gl.glEnable(GL10.GL_LIGHT0);
設定燈光顏色,燈光索引為0,最後一個參數是顏色數組偏移量.
float[] ambient = {1.0f, 1.0f, 1.0f, 1.0f};// 點光-環境色
float[] diffuse = {1.0f, 1.0f, 1.0f, 1.0f};// 點光-漫反射色
float[] specular = {0.0f, 0.0f, 0.0f, 1.0f};// 點光-高亮顏色
gl.glLightfv(GL10.GL_LIGHT0,GL10.GL_AMBIENT, ambient, 0);
gl.glLightfv(GL10.GL_LIGHT0,GL10.GL_DIFFUSE, diffuse, 0);
gl.glLightfv(GL10.GL_LIGHT0,GL10.GL_SPECULAR, specular, 0);
設定燈光位置,設定3D空間xyz座標,第四個元素必須設置為1,即光源有位置.
float[] position = {0, 0, 0, 1};// 點光-位置
gl.glLightfv(GL10.GL_LIGHT0,GL10.GL_POSITION, position, 0);
完成渲染後關閉燈光
gl.glDisable(GL10.GL_LIGHT0);
燈光類代碼
public class LightPoint {
private float[] ambient = {1.0f, 1.0f, 1.0f, 1.0f};// 點光-環境色
private float[] diffuse = {1.0f, 1.0f, 1.0f, 1.0f};// 點光-漫反射色
private float[] specular = {0.0f, 0.0f, 0.0f, 1.0f};// 點光-高亮顏色
private float[] position = {0, 0, 0, 1};// 燈光位置
int id = 0;// 燈光ID
構造點光(燈泡) 燈光ID輸入:GL10.GL_LIGHT0至GL10.GL_LIGHT7
public LightPoint(int ID ){
this.id = ID;// 燈光ID
}
設定點光(燈泡)位置
public void setPosition(float x,float y,float z){
position[0] = x;
position[1] = y;
position[2] = z;
position[3] = 1;
}
設定點光(燈泡)顏色
public void setColor(float r,float g,float b){
ambient[0] = r;
ambient[1] = g;
ambient[2] = b;
ambient[3] = 1;
}
使能點光(燈泡)
public void enable(){
GL10 gl = GRAPHICS.gl;
gl.glEnable(id);//使能
gl.glLightfv(id,GL10.GL_AMBIENT, ambient, 0);
gl.glLightfv(id,GL10.GL_DIFFUSE, diffuse, 0);
gl.glLightfv(id,GL10.GL_SPECULAR, specular, 0);
gl.glLightfv(id,GL10.GL_POSITION, position, 0);
}
屏蔽點光(燈泡)
public void disable(){
GL10 gl = GRAPHICS.gl;
gl.glDisable(id);
}
}

環境光是一種特殊光.它沒有位置和方向.它只會均勻照射3D空間中所有物體.在OpenGL ES中啟用全域環境光.
啟用光照
gl.glEnable(GL10.GL_LIGHTING);
環境光純白色,色域範圍為0~1浮點數.影射對應0~255整數
float[] color = {1.0f,1.0f,1.0f,1f};// 環境光浮點數組
設定環境光最後參數color偏移量通常設為0
gl.glLightModelfv(GL10.GL_LIGHT_MODEL_AMBIENT, color, 0);
全域環境光代碼
public class LightAmbient {
static private float[] color = {1.0f,1.0f,1.0f,1f};// 環境光
//設定環境光
static public void setColor(float r,float g,float b ){
color[0] = r;
color[1] = g;
color[2] = b;
color[3] = 1;
}
//使能環境光
static public void enable(){
GL10 gl = GRAPHICS.gl;
gl.glLightModelfv(GL10.GL_LIGHT_MODEL_AMBIENT, color, 0);
}
}

光照系統它可以令3D遊戲更加逼真.要模擬光照需要光源發射光線.和被光照照射物.最後需要一台相機捕足光源發射光以及被物體反射光.光照會改變觀察者對被觀察者物體顏色感受.取卻於以下幾個因素
被照射物體反射光強度取決於光照射到至物體平面時光與物體平面夾角.光與其所照射平面越接近垂直,物體表面反射光強度越大
一旦光照射到平面它會以兩種方式反射.鏡面反射會物體上表現出強光效果.物體鏡面反射效果取決於材質.
漫反射:大部分反射光線會因為物體不規則表面而隨機地發散開來,具有光滑表面
鏡面反射:光照射到光滑鏡面後返射回來,具有粗糙不平整表面是不可能形成
而光照射到表面時反射光顏色還取決於光源和材質顏色.
OpenGL ES可以創建出四種不同光源
環境光源:環境光本身非光源,而是由所在環境中其它光源發出光反射在周圍得到.這些環境光混合形成照明效果.環境光無方向並且被環境光照射所有物體都有共同亮度.
點光源:點光源在空間中有固定位置,並且向個方向照射.如燈泡
定向光源:定向光需要一個方向並延伸至無限遠.如太陽是標準定向光源
投射光源:在3D空間中有固定位置.並且有個照射方向.並且具有錐形照射區域.如街燈就是標準投射光.但很耗GPU資源.
OpenGL ES允許指定光顏色與強度,使用RGBA指定顏色
環境光色:被照射無體整體受到光色.物體將會接受這種顏色照射.並且與光源位置和方向無關.
漫反射色:物體反射時受到光色.背著光源另一面不會被照射到
鏡面反射色:鏡面反射色僅僅影響面向觀察者和光源面
投射色:僅影響錐型照射物
啟用光照,一旦開啟光照系統將被應用於所有渲染.你還需要指定光源與材質以及頂點法線確定最後光照效果
GL10.glEnable(GL10.GL_LIGHTING);
渲染完成必需關閉光照.否則影響之後GUI渲染.
GL10.glDisable(GL10.GL_LIGHTING);
OpenGL ES允許每個場景中最多使用8個光源,外加一個全域光源.光源ID從GL10.GL_LIGHT0至GL10.GL_LIGHT7
光源0使能並將其應用所有渲染物
GL10.glEnable(GL10.GL_LIGHT0);
若想單獨禁用某光源
GL10.glDisable(GL10.GL_LIGHT0);
| 光源實現代碼 |
| 環境光 |
| 定向光 |
| 燈光 |
| 射燈 |
| 材質 |

OpenGL ES矩陣提供以下運算能力
OpenGL ES提供有三種『矩陣』
投影矩陣:建立視錐體形狀和尺寸.它決定投影類型和睇到範圍
模型視圖矩陣:在模型空間中用該矩陣變換3D模型.並將其在3D空間中移動
紋理矩陣:用於動態操縱紋理矩陣
設置當前矩陣為『投影矩陣』
GL10.glMatrixMode(GL10.GL_PROJECTION);
在棧頂載入單位據陣
GL10.glLoadIdentity();
然後剩以正交投影矩陣
GL10.glOrthof(-1,1,-1,1, 1, -1);
設置當前矩陣『模型視圖矩陣』
GL10.glMatrixMode(GL10.GL_PROJECTION);
在棧頂載入單位據陣
GL10.glLoadIdentity();
3D空間中平移
GL10.glTranslatef(x,y,z);
繞Y軸旋轉
GL10.glRoate(angle,0,1,0);
將當前棧頂拷貝並壓入棧頂
GL10.glPushMatrix();
矩陣棧頂出棧
GL10.glPopMatrix();

OpenGL要啟用混合,要將每個頂點顏色ALPHA分量置設為0.5f.這樣模型後方對象都能透過模型睇到
1. OpenGL ES 將在『深度緩存Z-Buffer』和『幀緩存』中渲染模型
2. 當OpenGL ES結合z-buffer啟用混合時.必需確保所有透面對象跟據其距離照相機遠近按升序排序.並且從後往線渲染對象.所有非透明必須在透明對象之前被渲染,而非透明對象不需要經過排序
啟用混合:
1. 啟用深度檢測(Z軸緩存)一定要調用glEnable(GL_DEPTH_TEST);.保證光照正確繪製和模形前後正確繪製
2. 啟用混合gl.glEnable(GL10.GL_BLEND);
3. 設定混合方程式gl.glBlendFunc(GL10.GL_SRC_ALPHA,GL10.GL_ONE_MINUS_SRC_ALPHA);
4. 設定模型透明度gl.glColor4f(1.0f, 1.0f, 1.0f,0.5f);
5. 完成渲染後禁用混合glDisable(GL_BLEND);
當需要在OpenGL ES中啟用混合.需要按以下方式渲染
1. 首先渲染所有不透明對象
2. 將所有透明對象按照其與相機距離運近排序(由遠及近)
3. 渲染排好序透明對象(由遠及近)
|
混合係數 |
簡介 |
|
GL_ZERO |
將顏色設為{0,0,0,0} |
|
GL_ONE |
不改變當前顏色(r,g,b,a)*(1,1,1,1) |
|
GL_SRC_COLOR |
目標與來源相乘dest (r,g,b,a)* sour (r,g,b,a) |
|
GL_DST_COLOR |
來源與目標相乘sour (r,g,b,a)* dest (r,g,b,a) |
|
GL_ONE_MINUS_SRC_COLOR |
(r,g,b,a)*((1,1,1,1)- sour(r,g,b,a)) |
|
GL_ONE_MINUS_DST_COLOR |
(r,g,b,a)*((1,1,1,1)- dest(r,g,b,a)) |
|
GL_SRC_ALPHA |
(r,g,b,a) * sour(alpha) |
|
GL_DST_ALPHA |
(r,g,b,a) * dest(alpha) |
|
GL_ONE_MINUS_SRC_ALPHA |
(r,g,b,a) * (1- sour(alpha)) |
|
GL_ONE_MINUS_DST_ALPHA |
(r,g,b,a) * (1- dest(alpha)) |
|
GL_SRC_ALPHA_SATURATE |
(r,g,b,a) *MIN (sour(alpha),1-dest(alpha)) |

OpenGL中『幀緩存』用於儲存屏幕每個像素.而z-buffer『深度緩存』則儲存像素『深度值』.『深度值』為3D空間中對應點與相機『近裁剪面』距離.
OpenGL ES將z-buffer『深度緩存』為每個像素寫入深度值.OpenGL ES會初此『深度緩存』每個深度值為無窮大(大數).
gl.glEnable(GL10.GL_DEPTH_TEST);
每當渲染像素時將像素深度值和『深度緩存』深度值進行比較.如果深度值更小則表示它接近於『近裁剪面』則更新『幀緩存』像素與『深度緩存』深度值.這一過程稱為『深度測試』.如果未能通過『深度測試』則像素與深度值均不寫入『幀緩存』與『深度測試』
每幀渲染時『幀緩存』與『深度緩存』均需清零.否則上一幀數據會被保留下來
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
在渲染所有3D場景後需關閉『深度測試』.因為渲染2D圖形UI元素無Z軸座標.更無需深度測試.渲染順序由代碼繪製順序決定.
gl.glDisable(GL10.GL_DEPTH_TEST);

2D遊戲使用『正交投影』這意味著模型與視點距離無論多遠,其屏幕尺寸大小總為一至.而3D遊戲則使用『透視投影』模型離視點越近其屏幕尺寸越大.而模型離視點越遠其屏幕尺寸越細.
在『正交投影』就像置身於『矩形盒』.而『透視投影』就像切掉『金字塔』頂部,頂部為『近裁剪面』底部為『遠裁剪面』.而另外四面則分別為『左裁剪面』『右裁剪面』『頂裁剪面』『底裁剪面』
透視錐體由四個參數組成
1.『近裁剪面』與相機矩離
2.『遠裁剪面』與相機矩離
3.視口縱橫比,即視口『近裁剪面』寬高比
4.『視場』指定視錐體寬,也就是它所容納場景
桌面OpenGL帶有GLU輔助函式庫.而Android系統也含有GLU庫.設置投影矩陣
GLU.gluPerspective(GL10 gl,float fieldOfView,float aspectRatio,float near,flat far);
該函式將『透視投影矩陣』與當前矩陣相乘.
gl:為GL10實例
fieldOfView:視場角度,人眼視場角大約67度.加減此值可調整橫向視察範圍
aspectRatio:視口縱橫比,此值為一浮點數
near:遠裁剪面與相機距離
far:近裁剪面與相機距離
『透視投影』代碼
GL10 gl = GRAPHICS.GetGL();
清屏
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
設定視口
gl.glViewport(0,0, GRAPHICS.GetWidth(),GRAPHICS.GetHeight());
設定當前矩陣為投影矩陣
gl.glMatrixMode(GL10.GL_PROJECTION);
載入單位矩陣
gl.glLoadIdentity();
設置投視投影
GLU.gluPerspective(gl, fieldOfView, aspectRatio, near, far);
設定當前矩陣為模型視圖矩陣
gl.glMatrixMode(GL10.GL_MODELVIEW);
載入單位矩陣
gl.glLoadIdentity();

在進入豐富多彩3D世界中需要定以『視錐體』和『精靈頂點』.3D空間中頂點需有xyz座標.並且使用『透視投影』.距離相機越遠,物體越小.離相機較近對象覆蓋較進隊象.3D頂點包含『位置(xyz)』『顏色(rgba)』『法線(x,y,z)』並且完成3D頂點『模型』渲染
public class VERTICES3D {
擁有頂點顏色
boolean hasColor = false;
擁有紋理坐標
boolean hasTexCoord =false;
擁有法線
boolean hasNormal = false;
每個頂點所占空間
int vertex_size = 0;
最大頂點量
int vertex_max = 0;
最大索引量
int index_max = 0;
頂點數組
IntBuffer vertex_array = null;
索引數組
ShortBuffer index_array = null;
頂點緩存
int[] vertex_Buffer;
構造函式分配頂點記憶體,vertex_max為最大頂點量,index_max為最大索引量
VERTICES3D(int vertex_max,int index_max,boolean hasColor,boolean hasTexCoord,boolean hasNormal){
ByteBuffer buffer = null;
this.vertex_max = vertex_max;
this.index_max = index_max;
this.hasColor = hasColor;// 是否擁有頂點顏色
this.hasTexCoord = hasTexCoord;// 是否擁有紋理坐標
this.hasNormal = hasNormal;// 是否擁有法線
計算每頂點所占大小.顏色(rgbs)占4單元.紋理座標(uv)占2單元.3D座標(xyz)占3單元.每個整數占4字節
this.vertex_size = (3 + (hasColor ? 4 : 0) + (hasTexCoord ? 2 : 0) + (hasNormal ? 3: 0)) * 4;
vertex_Buffer = new int[vertex_max * vertex_size / 4];
因為OpenGL ES是以C API結口提供.無法直接使用JAVA數組.因此你需要C數組系統堆棧記憶體.而非JAVA虛擬機記憶體.需要 FloatBuffer分配頂點記憶體.vertex_max為最大頂點量.
buffer = ByteBuffer.allocateDirect(vertex_size * vertex_max);
將『網絡字節』改為『主機字節』或稱為『CPU字節』
buffer.order(ByteOrder.nativeOrder());
獲取頂點整數數組
vertex_array = buffer.asIntBuffer();
每索引占兩BYTE.即OpenGL ES每次最多渲染65536個頂點.
if(index_max > 0){
每個短整形占兩個字節.index_max為最大索引量
buffer = ByteBuffer.allocateDirect(index_max * Short.SIZE/8);
將『網絡字節』改為『主機字節』
buffer.order(ByteOrder.nativeOrder());
獲取頂點短整數數組
index_array = buffer.asShortBuffer();
}
}
將頂點提交給OpenGL數組並觸發
public void setVertices(float[] vertices,int offset,int count){
清空緩存.設定當前位置
vertex_array.clear();
int len = offset + count;
for(int i=offset, j=0; i < len; i++, j++)
vertex_Buffer[j] = Float.floatToRawIntBits(vertices[i]);
寫入數據.移動當前位置
vertex_array.put(vertex_Buffer, 0, count);
觸發
vertex_array.flip();
}
將頂點索引提交給OpenGL數組
public void setIndices(short[] indices,int offset,int count) {
清空緩存.設定當前位置
index_array.clear();
寫入數據.移動當前位置
index_array.put(indices, offset, count);
觸發
index_array.flip();
}
獲取索引量
public int getNumIndices(){
return index_array.limit();
}
獲取頂點量
public int getNumVertices(){
return vertex_array.limit() / (vertex_size/4);
}
綁定頂點數據
public void Bind(){
GL10 gl = GRAPHICS.gl;
啟用頂點數組
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
設置當前寫入位置0
vertex_array.position(0);
設置頂點指針,每個頂點包含xyz分量
gl.glVertexPointer(3, GL10.GL_FLOAT, vertex_size, vertex_array);
擁有頂點顏色
if(hasColor == true) {
啟用頂點顏色數組
gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
設置當前寫入位置3
vertex_array.position(3);
設置顏色指針,RGBA四分量
gl.glColorPointer(4, GL10.GL_FLOAT, vertex_size, vertex_array);
}
擁有紋理坐標
if(hasTexCoord == true){
啟用紋理坐標
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
設置當前寫入位置
vertex_array.position(hasColor?7:3);
設置紋理坐標指針UV分量
gl.glTexCoordPointer(2, GL10.GL_FLOAT, vertex_size, vertex_array);
}
擁有法線
if (hasNormal = true) {
啟用法線
gl.glEnableClientState(GL10.GL_NORMAL_ARRAY);
int offset = 3;
if (hasColor)
offset += 4;
if (hasTexCoord)
offset += 2;
設置當前寫入位置
vertex_array.position(offset);
設置法線指針,xyz分量
gl.glNormalPointer(GL10.GL_FLOAT, vertex_size, vertex_array);
}
}
取消綁定數據
public void Unbind(){
GL10 gl = GRAPHICS.gl;
關閉頂點紋理數組
if(hasColor)
gl.glDisableClientState(GL10.GL_COLOR_ARRAY );
關閉頂點顏色數組
if(hasTexCoord)
gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
關閉法線數組
if (hasNormal)
gl.glDisableClientState(GL10.GL_NORMAL_ARRAY);
}
繪畫3D模型.需要先綁3D頂點
public void Draw(int mode,int offset,int count){
GL10 gl = GRAPHICS.gl;
繪畫頂點
if(index_array != null) {// 繪畫
index_array.position(offset);
gl.glDrawElements(mode, count, GL10.GL_UNSIGNED_SHORT, index_array);
}
else {
gl.glDrawArrays(mode, offset, count);
}
}
}

與『2D矢量』相比『3D矢量』僅是在x,y軸座標加上z軸座標.還有『點積』和『叉積』運算.與繞軸旋轉算法. 無左計算向量角度函式.
public class VECTOR3D {
3D浮點數座標
public float x,y,z;
角度轉弧度
public static float DEGREES_TO_RADIANS = ((1.0f/180.0f)* (float)Math.PI);
弧度轉角度
public static float RADIANS_TO_DEGREES = ((1.0f/(float)Math.PI)*180.0f);
用於繞軸旋轉
private static final float[] matrix = new float[16];
private static final float[] inVec = new float[16];
private static final float[] outVec = new float[16];
購造函式並設定x,y,z
public VECTOR3D(float x, float y,float z){
this.x = x;
this.y = y;
this.z = z;
}
拷貝3D矢量
public VECTOR3D Copy(){
VECTOR3D v;
v=new VECTOR3D(x,y,z);
return v;
}
重設3D矢量數值
public VECTOR3D set(VECTOR3D v){
this.x = v.x;
this.y = v.y;
this.z = v.z;
return this;
}
3D矢量加法運算
public VECTOR3D add(VECTOR3D v){
this.x = this.x + v.x;
this.y = this.y + v.y;
this.z = this.z + v.z;
return this;
}
3D矢量減法運算
public VECTOR3D sub(VECTOR3D v){
this.x = this.x – v.x;
this.y = this.y – v.y;
this.z = this.z – v.z;
return this;
}
3D矢量乘法(即縮放)
public VECTOR3D mul(float scalar){
this.x = this.x * scalar;
this.y = this.y * scalar;
this.z = this.z * scalar;
return this;
}
計算3D矢量長度
public float Len(){
float len;
len = (float) Math.sqrt(x*x+y*y+z*z);
return len;
}
3D矢量單位化,長度為1
public VECTOR3D normer(){
float len;
len = Len();
if(len != 0){
x = x / len;
y = y / len;
z = z / len;
}
return this;
}
繞某軸旋轉,先定義3D矢量,然後設置矩陣為零,然後用rotateM()旋轉,在乘以3D向量
public VECTOR3D rotate(float angle,float axisX,float axisY,float axisZ){
inVec[0] = x;
inVec[1] = y;
inVec[2] = z;
inVec[4] = 1;
Matrix.setIdentityM(matrix, 0);
Matrix.rotateM(outVec,0, angle, axisX, axisY, axisZ);// 選轉
Matrix.multiplyMV(outVec, 0, matrix, 0, inVec, 0);
x = outVec[0];
y = outVec[1];
z = outVec[2];
return this;
}
計算兩3D矢量之間距離
public float Dist(VECTOR3D v){
float distX = this.x – v.x;
float distY = this.y – v.y;
float distZ = this.z – v.z;
float dist = (float)Math.sqrt(distX*distX + distY*distY + distZ*distZ);
return dist;
}
計算兩個3D矢量之間距離平方
public float DistSquared(VECTOR3D v){
float distX = this.x – v.x;
float distY = this.y – v.y;
float distZ = this.z – v.z;
float dist = distX*distX + distY*distY + distZ*distZ;
return dist;
}
計算兩個3D向量叉積,叉積是一個向量,它與va和vb垂直.
void cross(VECTOR3D va, VECTOR3D vb){
x = ( (va.y * vb.z) – (va.z * vb.y) );
y = -( (va.x * vb.z) – (va.z * vb.x) );
z = ( (va.x * vb.y) – (va.y * vb.x) );
}
計算3D向量點積.返回值為浮點數
float dot(VECTOR3D v){
return( (x * v.x) + (y * v.y) + (z * v.z) );
}
3D向量取反數
VECTOR3D inverse(){
this.x = -this.x ;
this.y = -this.y ;
this.z = -this.z ;
return this;
}
}

之前講解在Android遊戲種使用『點陣字體』使用Photoshop繪畫.但要對齊所有字符效果並不理想.但『Codehead’s Bitmap Font Generator』可以幫助你生成『點陣字體』.而且把非等距字體自動對齊.通過記錄每個自體寬度生成非等距文本.可以從www.codehead.co.uk/cbfg/免費獲取.以創建羅馬體為例:

最早8bit遊戲機非常適合使用芯片音(chip tunes).芯片音是一種音效通過合成器生成.而as3sfxr則是專業音效工具.可以通過www.bfxr.net下載WINDOWS或MAC版本.跳躍音效生成簡介.
| 音效 | 簡介 |
| Pickup/Coin | 拾取/硬幣 |
| Laser/Shoot | 激光/射擊 |
| Explosion | 爆炸 |
| Powerup | 加電 |
| Hit/Hurt | 命中/傷害 |
| Jump | 跳躍 |
| Blip/Select | 彈開/選擇 |
| Randomize | 隨機 |
| Mutation | 異變 |
| 合成器 | 簡介 |
| Triangle | 三角形濾波 |
| Sin | 正弦濾波 |
| Square | 平方濾波 |
| Saw | 聲表面波濾波器 |
| Breaker | 斷路 |
| Tan | 正切濾波 |
| Whistle | |
| White | |
| Pink | 粉紅噪聲 |
| 按扭 | 簡介 |
| Export Wave | 儲存單個wav『音效』 |
| Export All Waves | 儲存所有wav『音效』 |
| Clear All | 清除所有wav『音效』 |
| Duplicate Synth | 複製合成『音效』 |
| Copy Link | 複製『音效』鏈接 |

遊戲動畫由關鍵幀(keyframe)組成.將它地依次連續渲染產生運動效果.上圖每幀尺寸64*64像素一共四幀.為產生動畫效果需每隔若干毫秒渲染一幀.當渲染到最後一幀時可選擇重新播放或停止播放.通常要實現『行走』『跳躍』『攻擊』『倒下』需要定義動畫結構
public class ANIMATION {
保存紋理圖檔只能關鍵幀位置.並且其順序與動畫回放順序相同
public REGION[] frames = null;
保存每幀間隔時間,用於確定每幀切換時間.
public float duration;
動畫播放模式,分為『循環播放』與『單次播放』
public static final int LOOPING = 0;
public static final int NONLOOPING = 1;
構建動畫輸入『每幀間隔』與每幀『紋理區域』
ANIMATION(float duration,REGION … frames){
this.duration = duration;
this.frames = frames;
}
輸入時間提取關鍵幀.播放模式是單次或循環,基於『狀態時間』除以『每幀間隔』計算幀索引.
public REGION GetKeyFrame(float stateTime,int mode){
int index = (int)(stateTime/duration);
if(mode == NONLOOPING)// 單次播放
index = Math.min(frames.length-1, index);
else
index = index % frames.length;
return frames[index];
}

在遊戲中渲染字符,數字.在這裡介紹『點陣字體』技術.每個子圖表示單個字符如0~9.如上圖.『點陣字體』遊戲中渲染文本已非常古老.它通常包含ASCII碼字符圖像.此圖像程為『圖像字符』(glyph).ASCII字符集有128個字符.但只有95個可打印字符.索引號從32到126個.為節約空間『點陣字體』只包含可打印字符.95個字符每行16個字符分6行.ASCII只適用於存儲和顯示標準『拉丁字母』.但已滿足大部分單機遊戲.首先創建96個紋理區域.每區域映射到『點陣字體』中某圖像字符.
public REGION[] array = new REGION[96];
因為ASCII頭32個字符非打印字符無在『圖像字符』中渲染.首字符是空格只要講String轉為char字符減去空格
c = text.charAt(i) – ‘ ‘;
即可獲得紋理區域數組索引.要注意索引值必需在0~95之間否則會越界訪問.
region = array[c];
渲染ASCII文本.這裡簡單起見字體只時用『固定寬度』.但現代文本渲染字體寬度是非固定.可為每個字符設定不同字寬.
BATCHER.Draw(x, y, width, height, region);
上圖我使用Photoshop生成『圖像字符』.但其實有專門免費工具Codehead’s Bitmap Font Generator簡稱CBFG.可從www.codehead.co.uk/cbfg/下載

2D遊戲紋理渲染必須將『背景色』去除.JPEG格式不支持存儲像素點alpha值.將透明色alpha值設為零.需使用PNG格式.若紋理圖像沒有alpha通道時OpenGL ES自動將alpha設為1.但混合開銷很大而目前手機上GPU都不能對大量像素禁行混合.所以在需要時才啟用Blend『混合』.在OpenGL ES中啟動半透明混合處理
gl.glEnable(GL10.GL_BLEND);
設定『來源色』和『目標色』組合方程.
gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
啟用2D紋理渲染
gl.glEnable(GL10.GL_TEXTURE_2D);
渲染三角形
…
禁用2D紋理渲染
gl.glDisable(GL10.GL_TEXTURE_2D);
結束渲染後禁用混合
gl.glDisable(GL10. GL_BLEND);

遊戲主選單 『屏幕』通常有 『圖標』『選單 』.所要做是『觸摸』選單 時切換『屏幕』.計算出選單 項『矩形區域』與觸碰點重疊時切換『屏幕』在這裡設定四個選單 項.你需要繪畫『文本紋理』寬高相等並是2倍數.背景色設為透明色.並且保存為『.PNG』. 『文本紋理』好在於研發時用英文文本之後再漢化.
你需定義『2D相機』屏幕原點在左下角、VECTOR2D『點』把觸碰座標轉換屏幕座標、渲染『圖標』區域.定義四個按鈕『新遊戲』『繼續遊戲』『高分榜』『遊戲設定』每當更新屏幕update()對四個選單 區域進行overlap()『矩形碰撞測試』.而渲染『屏幕』時則先渲染『背景』後渲染『選單』
| 選單項 | 簡介 |
| NEW | 啟動新遊戲 |
| PLAY | 繼續遊戲按扭 |
| Highscores | 高分排名榜 |
| Settings | 遊戲設定 |
public class ScreenMain extends SCREEN{
private CAMERA2D camera;
private VECTOR2D touchPoint;
private RECT2D Bounds_Logo;
private RECT2D Bounds_New;
private RECT2D Bounds_Play;
private RECT2D Bounds_Highscores;
private RECT2D Bounds_Settings;
購造入口屏幕
public ScreenMain(){
camera = new CAMERA2D(320, 480);// 相機
Bounds_Logo = new RECT2D(WORLD.PIXEL_WIDTH/2, WORLD.PIXEL_HEIGHT/2 + 160 , 256, 128);
Bounds_New = new RECT2D(WORLD.PIXEL_WIDTH/2, WORLD.PIXEL_HEIGHT/2 + 64, 128, 32);
Bounds_Play = new RECT2D(WORLD.PIXEL_WIDTH/2, WORLD.PIXEL_HEIGHT/2 + 32, 128, 32);
Bounds_Highscores = new RECT2D(WORLD.PIXEL_WIDTH/2, WORLD.PIXEL_HEIGHT/2 , 256, 32);
Bounds_Settings = new RECT2D(WORLD.PIXEL_WIDTH/2, WORLD.PIXEL_HEIGHT/2 – 32, 256, 32);
touchPoint = new VECTOR2D();// 觸碰點
}
更新屏幕都觸碰座標
@Override
public void update(float deltaTime){
for(int i = 0; i < TOUCH.Point_Count; i++){
int action = TOUCH.Point_Action[i];
if(action != TOUCH.ACTION_UP)
continue;
float x = TOUCH.Point_X[i];
float y = TOUCH.Point_Y[i];
touchPoint.set(x, y);
camera.TouchToWorld(touchPoint);//觸摸坐標轉世界坐標
if(Bounds_New.overlap(touchPoint)){// 新遊戲按扭
TOUCH.clear();// 清空
SOUND.Play(ASSETS.sound_click);// 點擊
GAME.setScreen(GAME.getGameScreen());// 遊戲按扭
return;
}
else
if(Bounds_Play.overlap(touchPoint)){// 遊戲按扭
SOUND.Play(ASSETS.sound_click);// 點擊
GAME.setScreen(GAME.getGameScreen());// 遊戲按扭
TOUCH.clear();// 清空
break;
}
else
if(Bounds_Highscores.overlap(touchPoint) ){// 高分按扭
SOUND.Play(ASSETS.sound_click);// 點擊
GAME.setCurrentScreen(GAME.getScoreScreen());
TOUCH.clear();// 清空
break;
}
else
if(Bounds_Settings.overlap(touchPoint)){// 設定按鈕
SOUND.Play(ASSETS.sound_click);// 點擊
GAME.setScreen(GAME.getSettingsScreen());
TOUCH.clear();// 清空
break;
}
}
TOUCH.clear();// 清空
}
渲染屏幕
@Override
public void present(float deltaTime){
GL10 gl = GRAPHICS.gl;
GLSurfaceView view = GRAPHICS.gl_View;
gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
camera.SetViewportAndMatrices();
gl.glEnable(GL10.GL_TEXTURE_2D);
BATCHER.Begin(ASSETS.texture_background);
BATCHER.Draw(WORLD.PIXEL_WIDTH/2,WORLD.PIXEL_HEIGHT/2, WORLD.PIXEL_WIDTH, WORLD.PIXEL_HEIGHT, ASSETS.region_background);// 渲染背景
BATCHER.End();
// 設為透明
gl.glEnable(GL10.GL_BLEND);
gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
BATCHER.Begin(ASSETS.texture_text);
BATCHER.Draw(Bounds_Logo, ASSETS.region_logo);// 圖標按扭
BATCHER.Draw(Bounds_New, ASSETS.region_new);// 圖標按扭
BATCHER.Draw(Bounds_Play, ASSETS.region_play);// 主選單
BATCHER.Draw(Bounds_Highscores,ASSETS.region_highscores);// 高分按扭
BATCHER.Draw(Bounds_Settings, ASSETS.region_settings);// 設定按扭
BATCHER.End();
gl.glDisable(GL10.GL_TEXTURE_2D);// 禁用
}
暫停保存設置
@Override
public void pause(){
}
恢復
@Override
public void resume(){
}
清除/銷毀
@Override
public void dispose(){
}
返回鍵
@Override
public boolean back(){
return true;
}
}

遊戲設計中通常『更新』『渲染』放在同一線程中.在Windows可以在主線程將『消息驅動』改為『實時驅動』.把『更新』『渲染』放在主線程中.而Android卻無法做到這點.但提供GLSurfaceView可建立獨立線程在背後實現『更新』『渲染』.你需要實現監聽接口GLSurfaceView.Renderer.並註冊到GLSurfaceView中.監聽接口需要分別重寫『創建』『調整大細』『渲染』.GLSurfaceView.Renderer可獲得GL10.通過它方可訪問OpenGL ES API.而GL10中10代表OpenGL ES 1.0標準.可以將GLSurfaceView封裝成獨立控件.從而在layout『佈局』中嵌入.
public class RenderView extends GLSurfaceView implements GLSurfaceView.Renderer {
每當Activity恢復或啟動創建. EGLConfig設置Surface顏色與深度
public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig);
當view尺寸發生改變時調用,傳入寬與高
public void onSurfaceChanged(GL10 gl10, int width, int height);
調用『渲染』『更新』完成幀繪製.但每秒不超過60幀.
public void onDrawFrame(GL10 gl10);
令外還需重寫『恢復』『暫停』
『恢復』重啟渲染線程,在Activity恢復顯示時在Activity.onResume()中調用
public void onResume();
『暫停』退出渲染線程,當Activity進入後臺時在Activity.onPause()中調用
public void onPause();
}
編輯layout『佈局』文檔main.xml添加
<net.bookcard.aa.RenderView
android:id=”@+id/render_view”
android:layout_width=”fill_parent”
android:layout_height=”fill_parent” />
定義view狀態
public static final int STATE_INIT = 0;// 初此
public static final int STATE_RUN = 1;// 運行
public static final int STATE_PAUSED = 2;// 暫停
public static final int STATE_FINISHED = 3;// 結束
public static final int STATE_IDLE = 4;// 閒置
int view_width,view_height;// 寬與高
int state = STATE_INIT;// 初此狀態
long startTime ;// 啟動時間
創建Surface獲取屏幕
public void onSurfaceCreated(GL10 gl, EGLConfig eglConfig){
SCREEN screen = GAME.getCurrentScreen();// 當前屏幕
if(state == STATE_INIT) {// 初此
Init(gl, this);
screen = GAME.getMainScreen();
GAME.setCurrentScreen(screen);
}
else {// 重新載入資源
ASSETS.reload();
}
screen = GAME.getCurrentScreen();
state = STATE_RUN;// 運行
startTime = System.nanoTime();// 獲取啟動時間
}
大小發生改變
public void onSurfaceChanged(GL10 gl, int width, int height){
this.view_width = width;// 寬
this.view_height = height;// 高
}
更新並渲染.System.nanoTime()返回納秒, 1000000000納秒=1秒.通過兩次時間測量計算間隔時間
public void onDrawFrame(GL10 gl){
SCREEN screen = GAME.getCurrentScreen();
if(state == STATE_RUN){// 運行
float deltaTime = (System.nanoTime()-startTime) / 1000000000.0f;
startTime = System.nanoTime();// 獲取當前時間
screen.update(deltaTime);// 更新
screen.present(deltaTime);// 渲染
}
else
if(state == STATE_PAUSED) {// 暫停
}
else
if(state == STATE_FINISHED) {// 結束
}
}
恢復渲染在Activity.onResume()中調用
public void onResume(){
super.onResume();
}
暫停渲染在Activity.onPause()中調用
public void onPause(){
state = STATE_PAUSED;// 暫停
super.onPause();
}
初此遊戲系統個部件
void Init(GL10 gl, GLSurfaceView view){
GRAPHICS.Init(gl,view);
SYSTEM.Init(context);// 系統
FileIO.Init(context);// 文件讀寫
BITMAP.Init(context);// 位圖
SOUND.Init(context,100);// 聲音
MUSIC.Init(context,100);// 音樂
TOUCH.Init(this);// 觸摸模塊
GAME.Init();// 屏幕切換系統
BATCHER.Init(1500);// 精靈批處理
ASSETS.load();// 資源
}

在遊戲啟動需從磁盤讀取遊戲選項與得分.在GLSurfaceView.Renderer.onSurfaceCreated()中讀取讀取.首先確立文檔名
String filename = “settings.dat”;
打開文檔並返回輸入流
InputStream file = FileIO.ReadFile(filename);
打開讀取器
InputStreamReader stream = new InputStreamReader(file);
打開讀取緩存
BufferedReader in = new BufferedReader(stream);
String text;
讀取遊戲前五個最高得分
int[] highscores = new int[] {0,0,0,0,0};// 高分榜
for(int i=0;i<5;++i){
讀取一行文本,並轉換為得分.
text = in.readLine();
highscores[i] = Integer.parseInt(text);
}
用戶可能關閉音量
text = in.readLine();
boolean soundEnabled = Boolean.parseBoolean(text);
讀取遊戲音量.值為浮點數(0~1)
text = in.readLine();
float soundVolume = Float.parseFloat(text);
遊戲關卡整數值大於1
text = in.readLine();
int level = Integer.parseInt(text);
道具量整數值大於或等於零
text = in.readLine();
int bombs = Integer.parseInt(text);
讀取完畢關閉讀取緩存
in.close();
在遊戲退出時需將所有選項與得分寫入磁盤.在GLSurfaceView.onPause()中保存.打開文檔並返回輸出流
OutputStream file = FileIO.WriteFile(filename);
打開寫入器
OutputStreamWriter stream = new OutputStreamWriter(file);
打開寫入緩存
BufferedWriter out = new BufferedWriter(stream);
寫入遊戲五個最高得分
for(int i=0;i<5; i++){
out.write(Integer.toString(highscores[i]));
寫入換行
out.write(“\r”);
}
寫入聲音開關
out.write(Boolean.toString(soundEnabled));
out.write(“\r”);
寫入音量值(0~1)浮點數
out.write(Float.toString(soundVolume));
out.write(“\r”);
寫入當前關卡整數值
out.write(Integer.toString(level));
out.write(“\r”);
寫入道具量整數值
out.write(Integer.toString(bombs));
out.write(“\r”);
關閉寫入緩存
out.close();
加入得分並進行高分排序
public static void addScore(int score){
for(int i=0;i<5; ++i){
if(highscores[i] < score){
for(int j=4; j>i; –j)
highscores[j] = highscores[j-1];// 往後移
highscores[i] = score;// 插入分數
break;
}
}

設計抽象『屏幕』SCREEN類.為以後屏幕設計提供模板.update()更新所有對像.而present()則負責渲染. deltaTime為每次調用時間間隔. pause()與resume()當遊戲『暫停』與『恢復』時調用. 而dispose()則在遊戲退出時銷毀所有資源.釋放『記憶體』並保存設置.
public abstract class SCREEN {
public abstract void update(float deltaTime);// 更新
public abstract void present(float deltaTime);// 渲染
public abstract void pause();// 暫停
public abstract void resume();// 恢復
public abstract void dispose();// 清除/銷毀
public abstract boolean back();//檢查返會鍵
}
另需要『屏幕切換系統』GAME.每當設換屏幕時首先『暫停』然後『銷毀』資源並保存設置.然後設定新屏幕並『恢復』與『更新』.
public class GAME {
static private SCREEN screen = null;// 當前屏幕
設置當前屏幕
static public void setCurrentScreen(SCREEN newScreen){
if(screen != null){
screen.pause();// 暫停
screen.dispose();// 銷毀
}
screen = newScreen;
screen.resume();// 恢復
screen.update(0);// 更新
}
獲取當前屏幕
static public SCREEN getCurrentScreen() {
return screen;
}
}