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