gpt4 book ai didi

c# - Unity - 使用从网络加载的 64 多个单独的地球卫星图 block 完全包裹球体

转载 作者:行者123 更新时间:2023-12-05 04:41:02 26 4
gpt4 key购买 nike

场景

我正在使用 unity c# 重新发明一个类似 google-earth 的项目体验。当用户在全局范围内平移相机时,会从网络异步加载新的图 block 。到目前为止,我能够加载所有 TMS tiles基于它们的 x & y 坐标和缩放级别。目前我正在使用图 block x,y 来尝试找出图 block 应该出现在我的地球“球体”上的什么位置,这变得非常乏味,我认为是因为欧拉角和四元数之间的差异。

  • 我正在使用 Camera.main 的角度来确定应该随时查看哪些图 block (似乎工作正常)
  • 我必须为内存管理加载/卸载图 block ,因为第 10 级可以接收超过 100 万个 512x512 图 block
  • 我正在尝试将下载的图 block 的 x、y 坐标 (2d) 转换为 3d 位置和旋转

问题

仅使用瓷砖的 TMS 坐标 (0,0 - 63,63) 如何计算瓷砖的 xyz“地球”位置及其 xyz 旋转?

额外

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

enter image description here

** 编辑 **

我已经弄清楚如何使图 block 位置正确。现在我被卡在正确的方 block 旋转上了。

对我帮助最大的代码是关于 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;

enter image description here

** 编辑 2 **

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

tiles with correct facing

** 编辑 3 **

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

tiles with Ruzihm's shader

最佳答案

如果您为每个顶点分配纬度和经度,并分配球体中心和半径,您可以让着色器分配它们的位置和方向,而不是在 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。

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


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

Example by Jacksonkr


为了快速演示,创建一个新项目,将其放在相机上,并为其分配一个具有上述着色器的 Material 和一个要平铺的纹理。

它将创建 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;
}
}

关于c# - Unity - 使用从网络加载的 64 多个单独的地球卫星图 block 完全包裹球体,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70162448/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com