在 Unity 中制作鱼眼天空盒着色器

Making a Fisheye Skybox Shader in Unity

Unity 有一个内置的天空盒着色器,它采用立方体贴图纹理或像这样的等距柱状纹理

加载它并跟随 the instructions 将其用作天空盒

我想扩展它来处理这样的鱼眼图像

着色器的代码可从 built in shaders and a beta version (which at a glance seems the same) is available here

查看着色器代码,在顶点着色器中计算 3D 方向并将其传递给片段着色器。然后片段着色器应该采用该 3D 方向并生成纹理坐标。

这是等距柱状图像的代码

inline float2 ToRadialCoords(float3 coords)
{
  float3 normalizedCoords = normalize(coords);
  float latitude = acos(normalizedCoords.y);
  float longitude = atan2(normalizedCoords.z, normalizedCoords.x);
  float2 sphereCoords = float2(longitude, latitude) * float2(0.5/UNITY_PI, 1.0/UNITY_PI);
  return float2(0.5,1.0) - sphereCoords;
}

这是我尝试将其更改为鱼眼图像的代码

inline float2 ToFisheyeCoords(float3 coords)
  float3 normalizedCoords = normalize(coords);

  float r = 2.0 * atan2(length(normalizedCoords.xy), abs(normalizedCoords.z)) / UNITY_PI;
  float theta = atan2(normalizedCoords.y, normalizedCoords.x * sign(normalizedCoords.z));
  float2 uv = float2(cos(theta), sin(theta)) * r * 0.5 + 0.5;
  return frac(uv * float2(-1, 1));
}

但它不起作用。

我觉得我忽略了一些显而易见的事情。

整个项目是here。要在鱼眼示例和等距柱状示例之间切换,您需要打开 Window->Rendering->Light Settings,然后将 SkyboxMaterialEquirectangular 拖动到 Lighting window.

中的 Skybox Material 插槽中

再多花点时间,这似乎行得通

inline float2 ToFisheyeCoords(float3 coords)
{
  float3 normalizedCoords = normalize(coords);

  float r = 2.0 * atan2(length(normalizedCoords.xy), abs(normalizedCoords.z)) / UNITY_PI;
  float theta = atan2(normalizedCoords.y, normalizedCoords.x);
  float2 uv = float2(cos(theta), sin(theta)) * r * 0.5 + 0.5;
  return float2(uv.x * 0.5, uv.y);
}

我试了一下这个,我想我会 post 它。唯一要添加到您的答案中的是,在 360 度情况下,选择要对图像的哪一半进行采样。

inline float2 ToFisheyeCoords(float3 coords)
{
    float3 n = normalize(coords);        

    // u = r cos(phi) + 0.5
    // v = r sin(phi) + 0.5
    //where
    // r = atan2(sqrt(x * x + y * y), p.z) / pi
    // phi = atan2(y, x)

    float r = atan2(length(n.xy), abs(n.z)) / UNITY_PI;
    float phi = atan2(n.y, n.x * sign(n.z));
    float2 uv = float2(cos(phi), sin(phi)) * r + .5;

    uv.x *= .5;   
    //Choose image half to sample depending on sign of normal.z            
    uv.x += .25*(1 - sign(n.z));

    return uv;
}

单个(未拆分)图像的更通用解决方案似乎是:

inline float2 ToFisheyeCoords(float3 coords)
{
    float3 n = normalize(coords);
    //float FOV = UNITY_PI; // 180 degrees
    float FOV = UNITY_PI*2; // 360 degrees
    float r = atan2(length(n.xy), n.z) / FOV;
    float phi = atan2(n.y, n.x);
    float2 uv = float2(cos(phi), sin(phi)) * r + .5;
    return saturate(uv);
}