Equirectangular background shader

Does anyone know of a good way to use an equiractangular (or lat-lon) image as a background in Unity?

https://www.google.com/search?q=equirectangular+environment+map&espv=2&biw=1672&bih=731&tbm=isch&tbo=u&source=univ&sa=X&ved=0CB8QsARqFQoTCKL5z5zp4sgCFUd5JgodAycPdQ

I really do want to use this format. I don’t want a cubemap. I am aware that it won’t be as fast. That doesn’t concern me. I am only concerned with displaying this type of map, which is standard in the CG field, in Unity.

Thanks for any help.

Warren

The easy way is to have Unity convert it into a cubemap for you: Select your texture, set the texture type to Cubemap, and mapping to Lat-Long. Unity - Manual: Cubemaps

Then make a new material and set it to use the skybox/cubemap shader and assign the new cubemap to it.

Alternatively you can write your own skybox shader that takes a regular texture and assumes it’s a lat-long.

edit: Or use this one I wrote and see why people don’t use equirectangular in real time (nasty seam). Cubemaps have special hardware level handling that deals with seams cleanly, where as there’s no hardware handling of equirectangular so you can get weird seams like this. There are ways to fix it, but that’s something someone else can deal with if you really don’t want to use the cubemap conversion.

Shader "Skybox/Equirectangular" {
Properties {
    _Tint ("Tint Color", Color) = (.5, .5, .5, .5)
    [Gamma] _Exposure ("Exposure", Range(0, 8)) = 1.0
    _Rotation ("Rotation", Range(0, 360)) = 0
    [NoScaleOffset] _Tex ("Panorama (HDR)", 2D) = "grey" {}
}

SubShader {
    Tags { "Queue"="Background" "RenderType"="Background" "PreviewType"="Skybox" }
    Cull Off ZWrite Off

    Pass {
      
        CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag

        #include "UnityCG.cginc"

        sampler2D _Tex;
        half4 _Tex_HDR;
        half4 _Tint;
        half _Exposure;
        float _Rotation;

        float4 RotateAroundYInDegrees (float4 vertex, float degrees)
        {
            float alpha = degrees * UNITY_PI / 180.0;
            float sina, cosa;
            sincos(alpha, sina, cosa);
            float2x2 m = float2x2(cosa, -sina, sina, cosa);
            return float4(mul(m, vertex.xz), vertex.yw).xzyw;
        }
      
        struct appdata_t {
            float4 vertex : POSITION;
        };

        struct v2f {
            float4 vertex : SV_POSITION;
            float3 texcoord : TEXCOORD0;
        };

        v2f vert (appdata_t v)
        {
            v2f o;
            o.vertex = mul(UNITY_MATRIX_MVP, RotateAroundYInDegrees(v.vertex, _Rotation));
            o.texcoord = v.vertex.xyz;
            return o;
        }

        fixed4 frag (v2f i) : SV_Target
        {
            float3 dir = normalize(i.texcoord);
            float2 longlat = float2(atan2(dir.x, dir.z) + UNITY_PI, acos(-dir.y));
            float2 uv = longlat / float2(2.0 * UNITY_PI, UNITY_PI);
            half4 tex = tex2D (_Tex, uv);
            half3 c = DecodeHDR (tex, _Tex_HDR);
            c = c * _Tint.rgb * unity_ColorSpaceDouble.rgb;
            c *= _Exposure;

            return half4(c, 1);
        }
        ENDCG
    }
}   


Fallback Off

}
4 Likes

Thank you bgolus!

Hi I tried using this shader and it worked well when i use this in a new scene.
i.e From scene1 i make a selection of image and in scene2 during update i create a new material with this shader code and the texture i create a new texture with the imported photosphere image.
But later i use a button next to navigate to next image. i dont see the texture change in the skybox material
why is this.

newmaterial =newMaterial(Shader.Find(“Skybox/Equirectangular”));
newtexture=Resources.Load(“img6”,typeof(Texture))asTexture;
newmaterial.SetTexture(“_Tex”,newtexture);

GameObjectcamera=Camera.main.gameObject;
Skyboxskybox=camera.GetComponent();
skybox.material=newmaterial;

Can you please let me know what the issue is.

No idea, I’ve never tried to use the skybox component script and there’s very little information on how it works. From the issues you’re having you might need to toggle the skybox component off and on, or set the material to null before setting it to the new skybox. It could be that component, which has all it’s functionality hidden in native code, only works once at runtime and never again, so you need to remove it and make a new one each time … who knows.

If I want to change the skybox I just change the RenderSettings.skybox directly.

Thanks for the shader Ben - I think a way to deal with a vertical seam may be to set the source texture to “Wrap Mode” = “Repeat”, as I didn’t see any seams, but I may be missing something.

Cheers

There’s a built in equirectangular skybox shader included with Unity these days. There’s no need to use this shader.

The repeat wrap mode should be the default for any texture, and won’t solve the seam problem (though using clamp or mirror will make the seam worse). The “easy” solution to the seam is to disable Generate Mipmaps on the texture import settings, as the seam is caused by the sudden change in UVs causing the GPU to drop to the lowest mip level for a few pixels. That can be fixed with a more complicated shader, but generally mip maps aren’t needed for skyboxes.

Cubemaps are still preferable, as they’re a more efficient use of texture memory than equirectangular maps.

1 Like