Создание шейдера Fisheye Skybox в Unity - PullRequest
0 голосов
/ 24 марта 2020

Unity имеет встроенный шейдер Skybox, который принимает либо текстуру куба, либо экватериальную angular текстуру, подобную этой

Загрузка и следование инструкциям чтобы использовать его в качестве скайбокса

skybox from equirectangular image in unity

Я хочу расширить его для обработки изображения типа "рыбий глаз", подобного этому

fisheye 180 degress

Код для шейдера доступен из встроенных шейдеров , и доступна бета-версия (которая на первый взгляд выглядит одинаково) здесь

При просмотре кода шейдера трехмерное направление вычисляется в вершинном шейдере и передается фрагментному шейдеру. Затем предполагается, что фрагментный шейдер принимает это трехмерное направление и генерирует текстурную координату.

Вот код для равных angular images

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));
}

Но это не работает.

enter image description here

Мне кажется, что я упускаю что-то очевидное.

Весь проект здесь . Чтобы переключиться между примером «рыбий глаз» и примером equirect angular, вам нужно открыть Window-> Rendering-> Light Settings, а затем перетащить SkyboxMaterialEquirect angular в слот Skybox Material в окне Lighting.

Ответы [ 2 ]

1 голос
/ 24 марта 2020

Я немного поиграл с этим и решил, что выложу. Единственное, что нужно добавить к вашему ответу, это в 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);
}
1 голос
/ 24 марта 2020

Немного поболтать, и это похоже на работу

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);
}
...