当前位置: 首页 > 知识库问答 >
问题:

将立方体贴图转换为等矩形全景图

庄嘉
2023-03-14

我想将立方体贴图[图1]转换为等矩形全景图[图2]。

从球形到立方体是可能的(通过以下步骤:将2:1等矩形全景图转换为立方体贴图),但在如何反转它上迷失了方向。

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

共有2个答案

瞿和硕
2023-03-14

cube2sphere使整个过程自动化。例子:

$ cube2sphere front.jpg back.jpg right.jpg left.jpg top.jpg bottom.jpg -r 2048 1024 -fTGA -ostitched
刘明朗
2023-03-14

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

目标是将图像投影为等矩形格式,如下所示:

转换算法相当简单。为了计算等矩形图像中每个像素的颜色的最佳估计,给定一个具有6个面的立方体地图:

  • 首先,计算球面图像中每个像素对应的极坐标

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

算法的实现将根据上下文的不同而有所不同。我在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中为输入图像设置正确的导入设置:

  • 点过滤
 类似资料:
  • 我目前正在为一个网站开发一个简单的3D全景查看器。出于移动性能的原因,我使用了CSS3渲染器。这需要一个立方体贴图,分成6个单独的图像。 我正在用谷歌Photosphere应用程序或类似的创建2:1等矩形全景的应用程序在iPhone上录制图像。然后,我使用此网站调整这些大小并将其转换为立方体贴图:http://gonchar.me/panorama/(闪光) 更可取的是,我想自己做转换,如果可能的

  • 我有完整的等矩形图像,可以很好地处理三个。js: 但我的图像实际上只包含180x180度(球体的一半),因此我尝试在不拉伸整个球体的情况下,在球形网格上部分应用方形纹理。我想这和纹理有关。抵消xyz参数,但我没有成功。虽然我可以继续填充图像以符合2x1等矩形标准,但我宁愿将此步骤从处理工作流中删除。 在下面,你会看到完整的等矩形图像和我正在努力工作的正方形图像。有人对如何做到这一点有任何线索吗?谢

  • 我们已经使用2D纹理很长时间了,但除此之外仍有更多的纹理类型等着我们探索。在本节中,我们将讨论的是将多个纹理组合起来映射到一张纹理上的一种纹理类型:立方体贴图(Cube Map)。 简单来说,立方体贴图就是一个包含了6个2D纹理的纹理,每个2D纹理都组成了立方体的一个面:一个有纹理的立方体。你可能会奇怪,这样一个立方体有什么用途呢?为什么要把6张纹理合并到一张纹理中,而不是直接使用6个单独的纹理呢

  • 有没有更好的方法来转换贴图

  • 我的目标是使图像圆形并显示它。如果图像是方形的,那么我可以通过简单地使用CSS的属性将其转换为圆形。但是当图像是矩形时,使用这个CSS属性会给我椭圆形的图像。 剪辑的部分是不可见的,但仍然存在。所以即使现在我也在尝试使用属性,它给我椭圆形图像,右侧和左侧被剪辑。 我有什么办法可以解决这个问题吗?

  • 问题内容: 这是Highchart项目符号图表的示例http://jsfiddle.net/jlbriggs/LdHYt/1/ 如何将其转换为列项目符号图表? 问题答案: 这是您要找的东西吗?小提琴