Unty中通過鏡像優化HDRI全景圖體積
阿新 • • 發佈:2018-10-13
鏡像 github上 體積 org scrip atl 比較 code phi
全景圖即HDRI貼圖,可以代替6面cubemap,傳統3D軟件運用比較廣。一般反射探針,天空盒等都會用到。
但是體積過大是個問題,特別是移動端會對包體大小進行控制,雖說可以通過球面貼圖替換掉部分環境類貼圖,但適用範圍依然有限。
這裏通過鏡像的方式來做貼圖大小的優化,可以將貼圖優化到一半大小,缺點是會產生接縫。
原圖如下(網絡收集):
最終效果:
github上有一些Equirectangular map的轉換函數,類似球面坐標,直接拿來主義了。
參考:
https://github.com/tolotratlt/UnityPhotosphericView
https://github.com/Mapiarz/CubemapToEquirectangular
經過測試是可以x,y軸鏡像的。首先需要裁剪原始HDRI圖片。直接用Texture2D的Resize裁一下即可。
Material mat = new Material(Shader.Find("Hidden/ConvShader")); var rt = RenderTexture.GetTemporary(new RenderTextureDescriptor(tex.width, tex.height, RenderTextureFormat.ARGB32)); Graphics.Blit(tex, rt, mat); var instanceTex = Instantiate(tex); instanceTex.Resize(instanceTex.width, instanceTex.height/ 2); instanceTex.ReadPixels(new Rect(0, 0, instanceTex.width, instanceTex.height), 0, 0); instanceTex.Apply(); ...
需要註意的是轉換全景圖的兩個函數,參考了github上的內容,順帶把常量改成了內置的UNITY_PI。
float3 UvToDir(float2 uv) { uv *= float2(UNITY_TWO_PI, UNITY_PI); float theta = uv.y; float phi = uv.x; float3 dir = float3(0, 0, 0); dir.x = sin(phi) * sin(theta) * -1; dir.y = cos(theta) * -1; dir.z = cos(phi) * sin(theta) * -1; return dir; } float2 DirToUV(float3 a_coords) { float3 a_coords_n = normalize(a_coords); float lon = atan2(a_coords_n.z, a_coords_n.x); float lat = acos(a_coords_n.y); float2 sphereCoords = float2(lon, lat) * (1.0 / UNITY_PI); return float2(1 - (sphereCoords.x * 0.5 - 0.5), 1 - sphereCoords.y); //must flip x }
轉換之後就是在顯示部分做修改,傳入一個方向向量輸出全景圖的UV,在內部做一個鏡像圖片修復
需要註意輸出的x並非0-1區間,而是0-2,估計由於全景圖寬高2:1造成的,這裏簡單修復了下。
y軸接縫比較明顯,手動調節了一下誤差。設置好貼圖的壓縮關閉mipmap等,接縫會緩解不少。
float2 DirToUV(float3 a_coords) { float3 a_coords_n = normalize(a_coords); float lon = atan2(a_coords_n.z, a_coords_n.x); float lat = acos(a_coords_n.y); float2 sphereCoords = float2(lon, lat) * (1.0 / UNITY_PI); float2 uv = float2(1 - (sphereCoords.x * 0.5 - 0.5), 1 - sphereCoords.y); //---------------------------- uv.x -= 1; if (uv.x > 0.5) uv.x = 0.5 - (uv.x - 0.5); uv.x *= 2; //----------------------------Mirror X. //---------------------------- uv.y *= 1.999; if (uv.y < 1) uv.y *= -0.97; else uv.y *= 1.03; //----------------------------Mirror Y. return uv; }
基本上就是這樣,另外很多情況下需要Cubemap轉HDRI全景圖,可以直接參考維基百科上的Cubemaping映射函數:
https://en.wikipedia.org/wiki/Cube_mapping
Unty中通過鏡像優化HDRI全景圖體積