6

I am making a 360 viewer in unity, to view a 360 photo I used to have a cubemap attached to a skybox, and it worked great. But the weight of the cubemaps forced me to switch to textures.

All of the 360 viewer tutorials say to just put a sphere with a shader on it, and put the camera inside. When I do this, it doesn't work very well, because when I look to the top or bottom, I see the image warped like so: (The chairs are suppossed to look normal) enter image description here

It did not happen when I used a skybox.

Does any one know why is this happening?

Thank you very much!

Yuval3210
  • 193
  • 4
  • 14

3 Answers3

8

The shader you choose does not handle equirectangular distortion very well. At the poles (top and bottom) of the sphere much image information has to be mapped on very small space which leads to the artifacts you are seeing.

You can write a specialized shader to improve the coordinate mapping from your equirectangular image onto the sphere. At the Unity forums a specialized shader has been posted.

Shader "Custom/Equirectangular" {
    Properties {
        _Color ("Main Color", Color) = (1,1,1,1)
        _MainTex ("Diffuse (RGB) Alpha (A)", 2D) = "gray" {}
    }

    SubShader{
        Pass {
            Tags {"LightMode" = "Always"}

            CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag
                #pragma fragmentoption ARB_precision_hint_fastest
                #pragma glsl
                #pragma target 3.0

                #include "UnityCG.cginc"

                struct appdata {
                   float4 vertex : POSITION;
                   float3 normal : NORMAL;
                };

                struct v2f
                {
                    float4    pos : SV_POSITION;
                    float3    normal : TEXCOORD0;
                };

                v2f vert (appdata v)
                {
                    v2f o;
                    o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
                    o.normal = v.normal;
                    return o;
                }

                sampler2D _MainTex;

                #define PI 3.141592653589793

                inline float2 RadialCoords(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 / PI);
                    return float2(sphereCoords.x * 0.5 + 0.5, 1 - sphereCoords.y);
                }

                float4 frag(v2f IN) : COLOR
                {
                    float2 equiUV = RadialCoords(IN.normal);
                    return tex2D(_MainTex, equiUV);
                }
            ENDCG
        }
    }
    FallBack "VertexLit"
}

Again, it's not my own code but I tested it on android devices and as a standalone PC version. It results in very smooth poles.

Please note: This shader does not flip normals of your sphere. So if you want your camera to sit inside the sphere you have to invert its normals with a 3d program or with the shader. Try adding Cull Front after line 9 above and the shader will apply its texture to the "wrong" side of the model.

miracula
  • 527
  • 7
  • 10
  • Thank you, What I've ended p doing is creating a big 3d sphere with a lot of vertices, and the textures was great after that. I have looked at a shader solution before that so Im sure this is great! – Yuval3210 May 10 '16 at 10:39
  • I have the exact same problem as OP, but this shader (above) does not render at all. I mean it does not give an error but it renders clear (invisible). On iPhone, using GVR. – Ali Dec 20 '16 at 18:50
  • @Ali you most likely have issues with your spheres normals. Please see my edited answer above. – miracula Dec 22 '16 at 09:36
  • @miracula thanks, adding `Cull Front` solved the problem. works perfectly in `GVR` on iOS. – Ali Dec 22 '16 at 12:29
  • looks perfect, but x-axis is inverted! (Image is vertically flipped) How do I solve it? – Saravanabalagi Ramachandran Apr 24 '17 at 11:03
  • using `1-x` in place of `x` solves the problem: Use this line `return float2(1 - sphereCoords.x * 0.5 + 0.5, 1 - sphereCoords.y);` – Saravanabalagi Ramachandran Apr 24 '17 at 11:05
  • I had to use `return float2(1 - (sphereCoords.x * 0.5 + 0.5), 1 - sphereCoords.y);` to avoid distortion issues. – miracula May 05 '17 at 10:44
3

I'm a beginner and I had to do a lot just to understand this thread. This is what worked for me. I just combined the answers and put it in one script. I'm pretty sure I will forget this in a few weeks time, so putting it here for posterity.

Shader "Custom/Equirectangular" {
    Properties {
        _Color ("Main Color", Color) = (1,1,1,1)
        _MainTex ("Diffuse (RGB) Alpha (A)", 2D) = "gray" {}
    }

    SubShader{
        Pass {
            Tags {"LightMode" = "Always"}
            Cull Front

            CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag
                #pragma fragmentoption ARB_precision_hint_fastest
                #pragma glsl
                #pragma target 3.0

                #include "UnityCG.cginc"

                struct appdata {
                   float4 vertex : POSITION;
                   float3 normal : NORMAL;
                };

                struct v2f
                {
                    float4    pos : SV_POSITION;
                    float3    normal : TEXCOORD0;
                };

                v2f vert (appdata v)
                {
                    v2f o;
                    o.pos = UnityObjectToClipPos(v.vertex);
                    o.normal = v.normal;
                    return o;
                }

                sampler2D _MainTex;

                #define PI 3.141592653589793

                inline float2 RadialCoords(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 / PI);
                    return float2(1 - (sphereCoords.x * 0.5 + 0.5), 1 - sphereCoords.y);
                }

                float4 frag(v2f IN) : COLOR
                {
                    float2 equiUV = RadialCoords(IN.normal);
                    return tex2D(_MainTex, equiUV);
                }
            ENDCG
        }
    }
    FallBack "VertexLit"
}
Milo Timbol
  • 141
  • 1
  • 4
-1

This is another shader code.

'Shader "Flip Normals" {
          Properties {
             _MainTex ("Base (RGB)", 2D) = "white" {}
         }
          SubShader {

            Tags { "RenderType" = "Opaque" }

            Cull Front

            CGPROGRAM

            #pragma surface surf Lambert vertex:vert
            sampler2D _MainTex;

             struct Input {
                 float2 uv_MainTex;
                 float4 color : COLOR;
             };


            void vert(inout appdata_full v)
            {
                v.normal.xyz = v.normal * -1;
            }

            void surf (Input IN, inout SurfaceOutput o) {
                      fixed3 result = tex2D(_MainTex, IN.uv_MainTex);
                 o.Albedo = result.rgb;
                 o.Alpha = 1;
            }

            ENDCG

          }

          Fallback "Diffuse"
     }`
U.Vishnu
  • 1
  • 1