How to make a 360º image viewer with Unity3D

Recently, I needed to implement a 360º image viewer in the project I’m currently working on, as I didn’t find much information or tutorials related with this feature, I’ve decided to post my implementation and the few sources that helped me. My post covers from how to create the image viewer to how to control it with the mouse using Unity 5.3.3.

To sum up, the process we’re going to follow is to create a material with a custom shader and the image we need, to apply it to a sphere. Let’s begin with some tips for the image.

STEP 1: Prepare a suitable image

The image to be used in the viewer must have a 2:1 aspect ratio and equirectangular projection, this means the top and bottom part of the image have to be stretched in order to render correctly inside the sphere:

Equirectangular projection

If the image has a more panoramic aspect ratio, it will look very constrained when we create the material and put it on the sphere. In my case, this is the test image I used:

STEP 2: Create the shader

Now we need to create a custom shader which changes the projection of the material to the sphere’s inside instead of the outside, and due to this, apply a mirroring to the image so it doesn’t look reflexed. Here is the code of the shader:

Shader “Unlit/Pano360Shader”
{
Properties
{
_MainTex (“Base (RGB)”, 2D) = “white” {}
_Color (“Main Color”, Color) = (1,1,1,0.5)
}
   SubShader 
{
Tags { “RenderType” = “Opaque” }
      //This is used to print the texture inside of the sphere
Cull Front
      CGPROGRAM
#pragma surface surf SimpleLambert
half4 LightingSimpleLambert (SurfaceOutput s, half3 lightDir, half atten)
{
half4 c;
c.rgb = s.Albedo;
return c;
}

sampler2D _MainTex;
struct Input
{
float2 uv_MainTex;
float4 myColor : COLOR;
};

fixed3 _Color;
void surf (Input IN, inout SurfaceOutput o)
{
//This is used to mirror the image correctly when printing it inside of the sphere
IN.uv_MainTex.x = 1 — IN.uv_MainTex.x;
fixed3 result = tex2D(_MainTex, IN.uv_MainTex)*_Color;
o.Albedo = result.rgb;
o.Alpha = 1;
}
ENDCG
}
Fallback “Diffuse”
}

STEP 3: Create the sphere and set the camera

Create a gameobject which will contain the camera, that is to say the camera will be the child of the cameraContainer gameobject.

The cameraContainer GO has to be placed in the middle of the sphere, like this:

STEP 4: Create the material and apply it to the sphere

Create and apply the material using the shader that was created before and attaching the image, it will look like this:

Try to move inside the sphere and take a look to the image:

As you can see, the buildings distort a bit, I fixed this by changing the y tiling value of the material from 1 to 1.15, this distorsion can happen if the image’s equirectangular projection is not perfect:

Now the buildings are looking good :)

STEP 5: Add mouse control to move inside the sphere

You need to create a script where the camera rotates according to the mouse position, this is the code attached to the camera:

float horizontal;
float vertical;
Transform container;
void LateUpdate () 
{
//Using mouse
horizontal = Input.GetAxis(“Mouse X”);
vertical= Input.GetAxis(“Mouse Y”);

//This is made in order to avoid rotation on Z, just by typing 0 on Zcoord isn’t enough

//so the container is rotated around Y and the camera around X separately
container.Rotate(new Vector3(0, horizontal*(-1), 0f)*Time.deltaTime*turnSpeedMouse);
transform.Rotate(new Vector3(vertical, 0, 0)*Time.deltaTime*turnSpeedMouse);
}

And that’s all, now the 360º image viewer is ready! Below, I attach the sources that I found helpful on my way to add this feature :)