【Unity】Displacement Map

從外部匯入Texture實際上,大多數的情況下,Table或是Map都是offline產生的,也就是可能會先將map儲存成圖檔之類的,而不會像這樣寫死在程式碼中,再用腳本生成,因此比較直覺的想法是,不透過腳本直接將Map放到Shader裡面計算,除了讓事情比較單純都在Shader中計算之外,在GPU計算比在CPU計算還要有效率,雖然這牽涉到Map是否會頻繁更新等其他更實務的問題,這邊就不展開。

而是這麼做的好處是,不用在Unity掛腳本,可以在別的地方事先輸出好Map的圖檔,如果要替換直接丟到專案中,這樣也很容易替換

其中,Map會使用.exr之類的HDR圖片格式,而不是使用常見的png, jpg等,只是坑就在這裡。

首先,要記得把Format改成RGBA Half,值才不會跑掉,更準確地來說,要看Texture2D.format,只有RGBAHalf、RGBAFloat才會用float去存,其他格式都會把Map中的值clamp to [0, 1],也就是雖然Map原始的值有超出0~1範圍的話會被直接壓到0或1。

另外,介面上只讓你調成half,或許是部分平台不支援32bit float。

然後還沒完,Vertex Shader的部分大同小異,但是要記得做Gamma反校正,至於什麼是Gamma校正就超出本篇範圍,簡單來說就是要把值做2.2次方,其中,將Linear Space經過Gamma=2.2轉換出來的空間基本上就是所謂的sRGB。

vertex.rgb = pow(vertex.rgb, 2.2);因此,這行就是將Displacement Map的值從sRGB(為了配合API,姑且稱為Gamma Space)轉回Linear Space,因為我們定義好的Texture是在Linear Space,這個轉換的過程可以用Unity提供的GammaToLinearSpaceExact。

v2f vert(appdata v){ v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = v.vertex.xy; // predefine: grid in model space == uv space sampler2D map0 = _Warping < 0.5 ? _LowerMap : _MiddleMap; sampler2D map1 = _Warping < 0.5 ? _MiddleMap : _UpperMap; float2 uv = v.vertex.xy; float4 vertex = tex2D(_DisplacementMap, _DisplacementMap_TexelSize, uv); // for Linear HDR texture, if project setting is GAMMA SPACE, we have to transform texel to linear space // Because Unity would Linear HDR Texture transform to Gamma(2.2) Texture, automatically // vertex.rgb = pow(vertex.rgb, 2.2) // vertex.rgb = GammaToLinearSpace(vertex.rgb); vertex.r = GammaToLinearSpaceExact(vertex.r); vertex.g = GammaToLinearSpaceExact(vertex.g); vertex.b = GammaToLinearSpaceExact(vertex.b); // uv space == model space o.vertex = UnityObjectToClipPos(vertex); return o;}這邊一定會很疑惑,為什麼我要做反校正?我甚麼時候做轉換的?答案是,Unity幫你做轉換的,當你把HDR圖檔import到專案的時候Unity會偵測到是HDR檔案,會幫你把值從Linear Space轉換到Gamma Space方便後續檢視,但是如果你是在腳本自己建立HDR Map的話,那Unity就不會這麼貼心的去竄改你的值。因此從外部匯入Texture的壞處是,要記得在多寫一些轉換。

那有沒有辦法去讓Unity不要竄改你的值呢?參考這篇,可以,告訴Unity整個專案都是在Linear Space下計算,這樣自然不會幫你轉換到Gamma Space,但這樣的設置傷筋動骨、茲事體大,因此比較簡單的做法還是在自己做反校正。

Working with linear TexturessRGB sampling allows the Unity Editor to render in linear color space when Textures are in gamma color space. When you…docs.unity3d.com

Linear or gamma workflowThe Unity Editor offers both linear and gamma workflows. The linear workflow has a color space crossover where An image…docs.unity3d.com

但是要注意的是,實際應用上,這兩者都是在Runtime動態調整位移程度,藉由三個不同變形程度的Mesh,差異在於,前者會在腳本中會內插出結果的Mesh,再建立出HDR Texture的Map,然後assign到shader,後者則是直接匯入圖檔,接著全部assign到shader,然後在shader計算內插結果。