Ptex是Walt Disney Animation Studios開發的紋理映射工具。在看一個叫appleseed的渲染器時看到他支持這種紋理,因此就查看一下,發現比較輕量,因此就想趁此機會學習下。html
Ptex開源代碼主要包含該格式的定義、IO、緩存和一些常見filter。頭文件Ptexture.h定義了紋理的數據結構和全部操做的API,類定義都是抽象類,ctor和dtor都不是public,生成對象須要經過其餘接口來獲取實例,或者調用靜態函數進行操做,釋放對象不能直接調用dtor,統一由release函數delete或返回緩存,大多數對象能夠跨線程共用,因此須要ref count,release時大多隻是減ref。另外定義了一個用來管理對象的指針類PtexPtr,能夠封裝全部對象,並在超出做用域後自動release。
ios
主要數據結構:api
- struct Res,定義紋理的分辨率,按像素的log2存儲,長度爲8位int,其ctor能夠用16位int做爲參數,分高低8位儲存uv,最小單元(texel)分辨率爲1*1(ulog2 = vlog2 = 0),支持tile劃分。
- FaceInfo,定義面信息,主要包括面的分辨率、相鄰面和相鄰邊(對應相鄰面的邊ID),相鄰面爲32位int數組,一個面有4個相鄰面,-1表示該相鄰面爲空,相鄰邊爲8位uint,每一個長度爲2位,合併爲8位保存,相鄰面的遍歷按邊的ID從底部逆時針進行,邊的左右頂底在該頭文件的enum定義中(三角形沒定義),若是相鄰面是細分面(有多個面在當前面的同一邊),則以逆時針遍歷第一個遇到的面爲整個相鄰面的ID。
讀取header信息:數組
紋理信息保存在PtexTexture對象中,該對象不能由本身生成,只能經過靜態open函數(PtextReader)或PtexCache接口生成。首先,鎖mutex,將ptex文件以二進制形式打開(不轉譯回車和換行字符)並設置buffer,而後讀取header,保存有各類校驗信息和大小數目參數,以此對讀取的文件進行校驗,而後讀取extended header,header中能夠定義extended header的大小,struct ExtHeader的大小爲40字節,因此在讀取時應該以最小的爲準,而後計算各個數據的起始位置,調用讀取函數,包括:緩存
- 讀取面信息。讀取過程分三步,首先從壓縮的文件中讀取FaceInfo,每次讀取header中定義的faceinfosize或着16384字節(faceinfosize > BlockSize)爲一個block,而後生成rfaceid(先按面在內存中的儲存順序分配id,而後以面的uv分辨率最小的那個做爲依據降序排列面的順序,該順序就是rfaceid,面爲常量則分辨率看成1),並創建rfaceid和faceid的映射關係,最後更新已使用的內存數。
- 讀取面的常量像素值。讀取過程分三步,第一和三步同上,第二步爲alpha通道的預乘處理,先判斷alpha通道的位置來肯定須要預乘的通道,而後根據不一樣的像素存儲類型歸一化alpha值,最後將歸一化的alpha值與須要預乘的通道的像素值相乘。
- 讀取層次信息。讀取過程分三步,第一和三步同上,第二步爲層次定位,依次儲存每一層次的開始位置。
- 讀取編輯數據。讀取過程分三步,首先肯定編輯位置,而後根據編輯的類型讀取,分爲面數據和元數據兩類,先讀取對應的編輯header,而後保存編輯內容,編輯面數據還須要讀取面的常數像素值,最後更新內存使用記錄。
獲取紋理數據:數據結構
大部分數據能夠經過簡單返回得到,下面主要看幾個比較複雜的數據:app
- 獲取元數據。元數據分meta data和large meta data兩類,其大小分別定義在header和extheader中。二者的獲取相似,都是創建kv映射,key爲string表示名字,value爲entry存儲元數據,entry內保存了數據位置做爲key,直接從內存將所需數據拷貝到指定位置,區別在於lmd的entry沒有保存解壓後的數據,而是數據的位置,只有在載入並引用後才能訪問,有一個flag標識lmd。
- 獲取一個面的紋理數據。有四種獲取方式,獲取最高分辨率的紋理、獲取指定分辨率的紋理、獲取硬盤上保存的最高分辨率的紋理、獲取硬盤上保存的指定分辨率的紋理。前兩種是把數據拷貝到指定buffer裏,後兩種是返回facedata的指針,在獲取指定分辨率的紋理時,若是mipmap有對應的分辨率就直接從mipmap獲取,不然就用最近的分辨率生存新的紋理。第一種:首先獲取面的分辨率,而後調用第二種函數。第二種:使用PtexPtr管理對象,調用第四種函數獲取facedata指針,若是是常數,則直接用該像素充填buffer;若是不是常數且分tile,則按tile(v major遍歷)充填buffer,若是tile爲常數,則直接用該像素充填tile的第一行,而後用第一行充填其餘行,若是tile不是常數,則根據stride大小決定拷貝方式,若是stride等於行長度,則直接總體拷貝相應數據到tile,不然按行拷貝,若是不是常數且不分tile,則根據stride大小決定拷貝方式。第三種:獲取level 0後根據level獲取facedata,若是level沒有被讀取過,則初始化level,若是face沒有和level創建關係,則根據face data header的編碼方式初始化指定facedata並保存。第四種:主要處理mipmap,若是設置的分辨率不須要reduction則直接獲取level 0的facedata,若是是對稱reduction,則用u方向的reduction做爲levelid直接查找facedata,若是不能查到,則須要動態reduction,首先在cache裏查,若是沒有則生存新的reduction,根據reduction的方向(u或v)來reduce,生存後存入reduction cache並返回facedata。
- 獲取一個面的紋理的一個texel數據。有兩種獲取方式,獲取最高分辨率紋理的texel、獲取指定分辨率紋理的texel。兩種方式基本相似,首先獲取實際通道數,而後獲取facedata,而後就能夠獲得像素值,而後根據第一通道位置來設置像素保存的偏移量,最後保存,若是像素值不是float,則轉成float保存。兩者的區別在獲取facedata,第二種須要根據reduction來肯定mipmap的level獲取相應facedata。