本文介绍了Unity-使用从Web加载的64+个单独的地球卫星瓷砖完全包裹球体的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

场景

我正在使用unity c#将类似Google-Earth的体验重新发明为一个项目。当用户在全球范围内摇摄相机时,从网络异步加载新的瓷砖。到目前为止,我可以根据TMS tiles的x&;y坐标和缩放级别加载所有的TMS tiles。目前,我正在使用瓷砖x,y来尝试计算瓷砖应该出现在我的地球上的哪个位置&球面&我想是因为欧拉角和四元数之间的不同,它变得相当单调乏味。

  • 我正在使用Camera.main的角度来确定应该随时查看哪些磁贴(看起来工作正常)
  • 我必须加载/卸载分片以进行内存管理,因为级别10可以接收超过1,000,512x512个分片
  • 我正在尝试将下载的磁贴的x,y坐标(2D)转换为3D位置旋转(&A;P;R)

问题

仅使用我的磁贴的TMS坐标(0,0-63,63),我如何计算磁贴的xyz地球位置及其xyz旋转?

额外

  • 在所附屏幕截图中,我处于缩放级别4(64个平铺)
  • y轴0是地球的底部,y轴15是顶部
  • 到目前为止,我主要使用Mathf.SinMathf.Cos来计算位置和旋转

**编辑**

我已经想好怎么把瓷砖的位置弄对了。现在我卡在瓷砖的正确旋转上了。

通过有关generating a sphere in python的问题找到对我帮助最大的代码。

我将代码修改为如下所示:

    // convenience helpers @jkr
    float ti = tilesInfo["tilesXY"]; // basically the amount of tiles across either axis @jkr
    float ti2 = ti / 2;
    float pi = Mathf.PI;
    float pi2 = pi / 2;
    float pipi = pi * 2;

    // position for 3d tiles @jkr
    float phi = keyY / ti * pi;
    float theta = keyX / ti * pipi;
    x = Mathf.Sin(phi) * Mathf.Cos(theta) * ER;
    y = Mathf.Sin(phi) * Mathf.Sin(theta) * ER;
    z = Mathf.Cos(phi) * ER;

**编辑2**

添加@Ruzihm的答案计算法线后

**编辑3**

添加@Ruzihm的着色器之后。我继续做了一些调整,让事情变得更合适,还有很长的路要走,但至少这是一个很大的进步。

推荐答案

如果您为每个顶点指定纬度和经度,并指定球体中心和半径,则可以让着色器指定它们的位置和方向,而不是在C#中定位和定向平面:

Shader "Custom/SquareBender" {
    Properties{
        _MainTex("Tex", 2D) = "" {}
        _SphereCenter("SphereCenter", Vector) = (0, 0, 0, 1)
        _SphereRadius("SphereRadius", Float) = 5
    }

    SubShader{
        Pass {
            CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag

                #include "UnityCG.cginc"

                struct appdata {
                   float2 uv     : TEXCOORD0;
                   float2 lonLat : TEXCOORD1;
                };

                struct v2f
                {
                    float4 pos  : SV_POSITION;
                    float3 norm : NORMAL;
                    float2 uv   : TEXCOORD0;
                };

                float4 _SphereCenter;
                float _SphereRadius;

                v2f vert(appdata v)
                {
                    v2f o;
                    float lon = v.lonLat.x;
                    float lat = v.lonLat.y;

                    fixed4 posOffsetWorld = fixed4(
                        _SphereRadius*cos(lat)*cos(lon),
                        _SphereRadius*sin(lat),
                        _SphereRadius*cos(lat)*sin(lon), 0);

                    float4 posObj = mul(unity_WorldToObject,
                            posOffsetWorld + _SphereCenter);

                    o.pos = UnityObjectToClipPos(posObj);
                    o.uv = v.uv;
                    o.norm = mul(unity_WorldToObject, posOffsetWorld);
                    return o;
                }

                sampler2D _MainTex;

                float4 frag(v2f IN) : COLOR
                {
                    fixed4 col = tex2D(_MainTex, IN.uv);
                    return col;
                }
            ENDCG
        }
    }
    FallBack "VertexLit"
}

您可以将数据分配给顶点,如下所示:

// tileIndex is column/row/zoom of current tile
// uv is relative postion within tile
//   (0,0) for bottom left, (1,1) top right
Vector2 GetLonLatOfVertex(Vector3Int tileIndex, Vector2 uv)
{
    float lon, lat;

    // Use tileIndex and uv to calculate lon, lat (in RADIANS)
    // Exactly how you could do this depends on your tiling API...

    return new Vector2(lon, lat);
}

// Call after plane mesh is created, and any additional vertices/uvs are set
// tileIndex is column/row/zoom of current tile
void SetUpTileLonLats(Mesh mesh, Vector3Int tileIndex)
{
    Vector2[] uvs = mesh.uv;
    Vector2[] lonLats= new Vector2[uvs.Length];

    for (int i = 0; i < lonLats.Length; i++)
    {
        lonLats[i] = GetLonLatOfVertex(tileIndex, uvs[i]);
    }

    mesh.uv2 = lonLats;
}

平面的顶点越多,球体就会出现圆化,尽管这会导致瓷砖上的纹理发生更多扭曲。取舍取决于您。只需确保如果您按程序添加更多顶点/三角形,则为它们指定适当的UV即可。

请注意,顶点的位置是根据经度/经度在着色器中指定的,与对象的变换无关。如果启用了平锥剔除(默认情况下处于启用状态),请确保网格组件(位于变换的中心,您可以在场景视图中看到的是线框)在相机中可见,否则为了提高效率,unity将停止渲染它。


使用此过程使用瓷砖绘制完整球体的示例:


若要快速演示,请创建一个新项目,将其放在相机上,并为其指定具有上述着色器的材质和要平铺的纹理。

它将创建16个具有相同图像的平面,每个平面包含45度纬度和90度经度,并将它们包裹在一个球体周围。为每个平面分配不同的图像留给读者作为练习。

public class test : MonoBehaviour
{
    [SerializeField] Material mat;

    private void Start()
    {
        for (int i = 0 ; i < 16 ; i++)
        {
            int lonIndex = i % 4; // 0, 1, ..., 2, 3
            int latIndex = i / 4 - 2; // -2, -2, ..., 1, 1

            GameObject plane = GameObject.CreatePrimitive(PrimitiveType.Plane);

            plane.GetComponent<MeshRenderer>().material = mat;
            Vector3Int index = new Vector3Int(lonIndex, latIndex, 0);
            SetUpTileLonLats(plane.GetComponent<MeshFilter>().mesh, index);
        }
    }

    Vector2 GetLonLatOfVertex(Vector3Int tileIndex, Vector2 uv)
    {
        // Reminder: tileIndex goes from (0,-2) to (3,1)
        // Needs to go from (0, -.5pi) to (2pi, .5pi) depending on uv & index
        float lon = (tileIndex.x + uv.x) * 0.5f * Mathf.PI;
        float lat = (tileIndex.y + uv.y) * 0.25f * Mathf.PI;

        return new Vector2(lon, lat);
    }

    void SetUpTileLonLats(Mesh mesh, Vector3Int tileIndex)
    {
        Vector2[] uvs = mesh.uv;
        Vector2[] lonLats = new Vector2[uvs.Length];

        for (int i = 0; i < lonLats.Length; i++)
        {
            lonLats[i] = GetLonLatOfVertex(tileIndex, uvs[i]);
        }

        mesh.uv2 = lonLats;
    }
}

这篇关于Unity-使用从Web加载的64+个单独的地球卫星瓷砖完全包裹球体的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

05-17 17:15