gpt4 book ai didi

c++ - 将 Cubemap 转换为 Equirectangular Panorama

转载 作者:IT老高 更新时间:2023-10-28 23:18:21 34 4
gpt4 key购买 nike

我想从立方体贴图 [图 1] 转换为等角全景图 [图 2]。

图1 Figure1

图2 enter image description here

可以从 Spherical 转到 Cubic(通过以下方式:Convert 2:1 equirectangular panorama to cube map),但不知道如何反转它。

Figure2 将使用 Unity 渲染成一个球体。

最佳答案

假设输入图像为以下立方体贴图格式:

Cubemap image

我们的目标是将图像投影为 equirectangular 格式,如下所示:

Equirectangular image

转换算法相当简单。为了在给定具有 6 个面的立方体贴图的情况下计算 equirectangular 图像中每个像素的颜色的最佳估计:

  • 首先,计算图中每个像素对应的极坐标球面图像。
  • 其次,利用极坐标形成一个 vector 并确定立方体贴图的哪个面和那个面的哪个像素是 vector 谎言;就像来自立方体中心的光线转换会击中其中一个它的侧面和那一侧的特定点。

请记住,在给定立方体贴图特定面上的归一化坐标 (u,v) 的情况下,有多种方法可以估计 equirectangular 图像中像素的颜色。最基本的方法是一种非常原始的近似值,为简单起见将在此答案中使用,是将坐标四舍五入到特定像素并使用该像素。其他更高级的方法可以计算几个相邻像素的平均值。

算法的实现会因上下文而异。我在 Unity3D C# 中做了一个快速实现,展示了如何在现实世界场景中实现算法。它运行在CPU上,有很大的改进空间,但很容易理解。

using UnityEngine;

public static class CubemapConverter
{
public static byte[] ConvertToEquirectangular(Texture2D sourceTexture, int outputWidth, int outputHeight)
{
Texture2D equiTexture = new Texture2D(outputWidth, outputHeight, TextureFormat.ARGB32, false);
float u, v; //Normalised texture coordinates, from 0 to 1, starting at lower left corner
float phi, theta; //Polar coordinates
int cubeFaceWidth, cubeFaceHeight;

cubeFaceWidth = sourceTexture.width / 4; //4 horizontal faces
cubeFaceHeight = sourceTexture.height / 3; //3 vertical faces


for (int j = 0; j < equiTexture.height; j++)
{
//Rows start from the bottom
v = 1 - ((float)j / equiTexture.height);
theta = v * Mathf.PI;

for (int i = 0; i < equiTexture.width; i++)
{
//Columns start from the left
u = ((float)i / equiTexture.width);
phi = u * 2 * Mathf.PI;

float x, y, z; //Unit vector
x = Mathf.Sin(phi) * Mathf.Sin(theta) * -1;
y = Mathf.Cos(theta);
z = Mathf.Cos(phi) * Mathf.Sin(theta) * -1;

float xa, ya, za;
float a;

a = Mathf.Max(new float[3] { Mathf.Abs(x), Mathf.Abs(y), Mathf.Abs(z) });

//Vector Parallel to the unit vector that lies on one of the cube faces
xa = x / a;
ya = y / a;
za = z / a;

Color color;
int xPixel, yPixel;
int xOffset, yOffset;

if (xa == 1)
{
//Right
xPixel = (int)((((za + 1f) / 2f) - 1f) * cubeFaceWidth);
xOffset = 2 * cubeFaceWidth; //Offset
yPixel = (int)((((ya + 1f) / 2f)) * cubeFaceHeight);
yOffset = cubeFaceHeight; //Offset
}
else if (xa == -1)
{
//Left
xPixel = (int)((((za + 1f) / 2f)) * cubeFaceWidth);
xOffset = 0;
yPixel = (int)((((ya + 1f) / 2f)) * cubeFaceHeight);
yOffset = cubeFaceHeight;
}
else if (ya == 1)
{
//Up
xPixel = (int)((((xa + 1f) / 2f)) * cubeFaceWidth);
xOffset = cubeFaceWidth;
yPixel = (int)((((za + 1f) / 2f) - 1f) * cubeFaceHeight);
yOffset = 2 * cubeFaceHeight;
}
else if (ya == -1)
{
//Down
xPixel = (int)((((xa + 1f) / 2f)) * cubeFaceWidth);
xOffset = cubeFaceWidth;
yPixel = (int)((((za + 1f) / 2f)) * cubeFaceHeight);
yOffset = 0;
}
else if (za == 1)
{
//Front
xPixel = (int)((((xa + 1f) / 2f)) * cubeFaceWidth);
xOffset = cubeFaceWidth;
yPixel = (int)((((ya + 1f) / 2f)) * cubeFaceHeight);
yOffset = cubeFaceHeight;
}
else if (za == -1)
{
//Back
xPixel = (int)((((xa + 1f) / 2f) - 1f) * cubeFaceWidth);
xOffset = 3 * cubeFaceWidth;
yPixel = (int)((((ya + 1f) / 2f)) * cubeFaceHeight);
yOffset = cubeFaceHeight;
}
else
{
Debug.LogWarning("Unknown face, something went wrong");
xPixel = 0;
yPixel = 0;
xOffset = 0;
yOffset = 0;
}

xPixel = Mathf.Abs(xPixel);
yPixel = Mathf.Abs(yPixel);

xPixel += xOffset;
yPixel += yOffset;

color = sourceTexture.GetPixel(xPixel, yPixel);
equiTexture.SetPixel(i, j, color);
}
}

equiTexture.Apply();
var bytes = equiTexture.EncodeToPNG();
Object.DestroyImmediate(equiTexture);

return bytes;
}
}

为了利用 GPU,我创建了一个进行相同转换的着色器。它比在 CPU 上逐个像素地运行转换要快得多,但不幸的是 Unity 对立方体贴图施加了分辨率限制,因此在使用高分辨率输入图像的场景中它的用处受到限制。

Shader "Conversion/CubemapToEquirectangular" {
Properties {
_MainTex ("Cubemap (RGB)", CUBE) = "" {}
}

Subshader {
Pass {
ZTest Always Cull Off ZWrite Off
Fog { Mode off }

CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest
//#pragma fragmentoption ARB_precision_hint_nicest
#include "UnityCG.cginc"

#define PI 3.141592653589793
#define TWOPI 6.283185307179587

struct v2f {
float4 pos : POSITION;
float2 uv : TEXCOORD0;
};

samplerCUBE _MainTex;

v2f vert( appdata_img v )
{
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv = v.texcoord.xy * float2(TWOPI, PI);
return o;
}

fixed4 frag(v2f i) : COLOR
{
float theta = i.uv.y;
float phi = i.uv.x;
float3 unit = float3(0,0,0);

unit.x = sin(phi) * sin(theta) * -1;
unit.y = cos(theta) * -1;
unit.z = cos(phi) * sin(theta) * -1;

return texCUBE(_MainTex, unit);
}
ENDCG
}
}
Fallback Off
}

通过在转换过程中采用更复杂的方法来估计像素的颜色或通过对生成的图像进行后处理(或两者兼而有之,实际上),可以大大提高生成的图像的质量。例如,可以生成更大尺寸的图像以应用模糊滤镜,然后将其下采样到所需尺寸。

我创建了一个简单的 Unity 项目,其中包含两个编辑器向导,展示了如何正确利用 C# 代码或上面显示的着色器。在这里获取: https://github.com/Mapiarz/CubemapToEquirectangular

记得在 Unity 中为您的输入图像设置正确的导入设置:

  • 点过滤
  • 真彩色格式
  • 禁用 mipmap
  • 2 的非幂:无(仅适用于 2DTextures)
  • 启用读/写(仅适用于 2DTextures)

关于c++ - 将 Cubemap 转换为 Equirectangular Panorama,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34250742/

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