Search code examples
c#unity-game-engineprojection360-degrees360-panorama

Try using Equirectangular Projection to convert 2D coordinates to 3D coordinates. But failed. What am I doing wrong?


Currently, I am making a 360 video app in Unity. I successfully make the video to render by using the Unity Skybox Panoramic shader that does equirectangular projection. Now I am trying to set some buttons on the video by some 2D points given. The 2D point system is a bit tricky and is different from the Cartesian one (my boss requested it...). The range of x-axis is from -90 (top) to 90 (bottom), and is used as vertical axis. The range of y-axis is from -180 to 180, and is used as horizontal axis. Now on the topic of calculating the 3D coordinates, I read the wiki page of equirectangular projection and saw the equations. I assume φ1 and λ0 to be zero since there is no rotation? So the λ (longitude) and φ (latitude) are just y (horizontal coordinate) and -x (vertical coordinate) in the calculation. Since longitude and latitude are also just θ and φ in spherical coordinates, I calculate the coordinates using this C# program:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CreateButtons : MonoBehaviour {

    public GameObject testObject;
    public Vector2 generatePositionStart;
    public Vector2 generatePositionEnd;
    public float radius = 50f;
    const float LONPERPIXEL = 3840f / 360f;

    // Use this for initialization
    void Start () {
        Vector3 pointS = GetPoint (radius, generatePositionStart.y * LONPERPIXEL, -generatePositionStart.x * LONPERPIXEL);
        GameObject start = Instantiate (testObject, pointS, Quaternion.identity) as GameObject;
        start.name = "Start";
        Vector3 pointE = GetPoint (radius, generatePositionEnd.y * LONPERPIXEL, -generatePositionEnd.x * LONPERPIXEL);
        GameObject end = Instantiate (testObject, pointE, Quaternion.identity) as GameObject;
        end.name = "End";
    }

    Vector3 GetPoint (float rho, float theta, float phi) {
        float x = rho * Mathf.Sin (theta) * Mathf.Cos (phi);
        float y = rho * Mathf.Sin (theta) * Mathf.Sin (phi);
        float z = rho * Mathf.Cos (theta);
        return new Vector3 (x, y, z);
    }
}

The program will calculate two 2D coordinates (left top corner and right bottom corner) of the button. Then generates two sphere on the 3D positions. However, the 3D coordinates calculated are not on the correct position. Did I do something wrong?

P.S.: I found another equations that it said it is an inverse mapping one. I tried but still got wrong positions. The equations look like these:

float x = rho * Mathf.Cos (theta) * Mathf.Cos (phi);
float y = rho * Mathf.Sin (theta) * Mathf.Cos (phi);
float z = rho * Mathf.Sin (theta);

Solution

  • Now, I am going to answer myself. There are several issues in this code:

    1. The conversion does not need LONPERPIXEL.
    2. Unity Cos and Sin functions use radian, so the theta and phi have to multiply Mathf.Deg2Rad.
    3. The equations in GetPoint() are completely wrong.

    So here are the correct code:

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class CreateButtons : MonoBehaviour {
    
        public GameObject testObject;
        public Vector2 generatePositionStart;
        public Vector2 generatePositionEnd;
        public float radius = 50f;
    
        // Use this for initialization
        void Start () {
            Vector3 pointS = GetPoint (radius, generatePositionStart.y * Mathf.Deg2Rad, -generatePositionStart.x * Mathf.Deg2Rad);
            GameObject start = Instantiate (testObject, pointS, Quaternion.identity) as GameObject;
            start.name = "Start";
            Vector3 pointE = GetPoint (radius, generatePositionEnd.y * Mathf.Deg2Rad, -generatePositionEnd.x * Mathf.Deg2Rad);
            GameObject end = Instantiate (testObject, pointE, Quaternion.identity) as GameObject;
            end.name = "End";
        }
    
        // Update is called once per frame
        void Update () {
    
        }
    
        Vector3 GetPoint (float rho, float theta, float phi) {
            float x = rho * Mathf.Sin (theta) * Mathf.Cos (phi);
            float y = rho * Mathf.Sin (phi);
            float z = rho * Mathf.Cos (theta) * Mathf.Cos (phi);
            return new Vector3 (x, y, z);
        }
    }