Unty中經過鏡像優化HDRI全景圖體積

全景圖即HDRI貼圖,能夠代替6面cubemap,傳統3D軟件運用較爲普遍。通常反射探針,天空盒等都會用到。git

可是體積過大是個問題,特別是移動端會對包體大小進行控制,雖然說能夠經過球面貼圖替換掉部分環境類貼圖,但適用範圍依然有限。github

而相較金字塔貼圖又能夠彌補圖像質量低的問題,所需的分辨率卻差很少。缺點是會產生接縫,穿幫問題,及鏡像判斷對GPU負擔的增長。api

這裏經過鏡像的方式來作貼圖大小的優化,可將貼圖優化到原先的一半大小。網絡

 

 

原圖以下(網絡收集):app

 

最終效果(左右上下鏡像):函數

 

github上有一些Equirectangular map的轉換函數,相似球面座標,直接拿來主義了。測試

參考:優化

https://github.com/tolotratlt/UnityPhotosphericViewui

https://github.com/Mapiarz/CubemapToEquirectangularspa

 

通過測試是能夠x,y軸鏡像的,首先需裁剪原始HDRI圖片。直接用Texture2D的Resize裁一下便可。ConvShader就是兩個鏡像函數的轉換shader,不貼出來了

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

相關文章
相關標籤/搜索