Editing a Cubemap Skybox from remote image

陌路散爱 提交于 2019-12-10 16:55:49

问题


I need to download an image from a server, then transform it in a Cubemap and finally put this CubeMap in my Skybox.

I work with C#.

I came up with this code :

public string url = "image/url.jpg";

void Update() {
    // When trigger, we start the process
    if (Input.GetKeyDown("f")) {

        // start Coroutine to handle the WWW asynchronous process
        StartCoroutine("setImage");
    }
}

IEnumerator setImage () {

    Texture2D tex;
    tex = new Texture2D(2048, 2048, TextureFormat.RGBA32, false);

    WWW www = new WWW(url);
    //Texture myGUITexture = Resources.Load("23") as Texture;

    Debug.Log (www.bytesDownloaded);
    Debug.Log (www.progress);
    Debug.Log (www.texture);

    yield return www;

    // we put the downloaded image into the new texture
    www.LoadImageIntoTexture(tex);

    // new cubemap 
    Cubemap c = new Cubemap(2048, TextureFormat.RGBA32, false);
    Color[] CubeMapColors;
    CubeMapColors = tex.GetPixels();
    c.SetPixels(CubeMapColors, CubemapFace.PositiveX);
    // we set the cubemap from the texture pixel by pixel
    c.Apply();

    //NewTexture.isPowerOfTwo = true;

    //Debug.Log (RenderSettings.skybox.GetTexture ("_Tex"));

    // We change the Cubemap of the Skybox
    RenderSettings.skybox.SetTexture("_Tex", c);
}

I commented all the code to explain what I think I am doing.

I made this "trick" of creating the Cubemap pixel by pixel because the Editor way to do it (which is incredibly simple by the way) seems to be not possible from other people posts I could read.

Eventually, the result was just a bunch of grey pixels.

I don't really know what screw up so much in my process, the only "shadow" point I see is the TextureFormat.

I chose RGBA32 because when I look at my Unity Editor, I see BC7 format, which is error logged as non possible for SetTexture and from the doc they explain it may be decompressed in RGBA32.

Of course there is no remaining error in the console.

I am really surprised there is not a easy way to do it. I mean not necessarily taking from http an image and put it in skybox, but just change the texture of a skybox. Am I missing something ?


回答1:


I think I found a solution for you.

The following code is basically an extension to your code.

I fixed some stuff that was bothering me, but the code does basically the same.

using System.Collections;
using UnityEngine;

public class ReplaceCubemap : MonoBehaviour
{
    public string url = "your file name";
    public int CubemapResolution = 256;

    private Texture2D source;

    /// <summary>
    /// These are the faces of a cube
    /// </summary>
    private Vector3[][] faces =
    {
        new Vector3[] {
            new Vector3(1.0f, 1.0f, -1.0f),
            new Vector3(1.0f, 1.0f, 1.0f),
            new Vector3(1.0f, -1.0f, -1.0f),
            new Vector3(1.0f, -1.0f, 1.0f)
        },
        new Vector3[] {
            new Vector3(-1.0f, 1.0f, 1.0f),
            new Vector3(-1.0f, 1.0f, -1.0f),
            new Vector3(-1.0f, -1.0f, 1.0f),
            new Vector3(-1.0f, -1.0f, -1.0f)
        },
        new Vector3[] {
            new Vector3(-1.0f, 1.0f, 1.0f),
            new Vector3(1.0f, 1.0f, 1.0f),
            new Vector3(-1.0f, 1.0f, -1.0f),
            new Vector3(1.0f, 1.0f, -1.0f)
        },
        new Vector3[] {
            new Vector3(-1.0f, -1.0f, -1.0f),
            new Vector3(1.0f, -1.0f, -1.0f),
            new Vector3(-1.0f, -1.0f, 1.0f),
            new Vector3(1.0f, -1.0f, 1.0f)
        },
        new Vector3[] {
            new Vector3(-1.0f, 1.0f, -1.0f),
            new Vector3(1.0f, 1.0f, -1.0f),
            new Vector3(-1.0f, -1.0f, -1.0f),
            new Vector3(1.0f, -1.0f, -1.0f)
        },
        new Vector3[] {
            new Vector3(1.0f, 1.0f, 1.0f),
            new Vector3(-1.0f, 1.0f, 1.0f),
            new Vector3(1.0f, -1.0f, 1.0f),
            new Vector3(-1.0f, -1.0f, 1.0f)
        }
    };

    void Update()
    {
        // When trigger, we start the process
        if (Input.GetKeyDown(KeyCode.F))
        {

            // start Coroutine to handle the WWW asynchronous process
            StartCoroutine(setImage());
        }
    }

    IEnumerator setImage()
    {
        WWW www = new WWW(url);
        //Texture myGUITexture = Resources.Load("23") as Texture;

        Debug.Log(www.bytesDownloaded);
        Debug.Log(www.progress);
        Debug.Log(www.texture);

        yield return www;

        source = new Texture2D(www.texture.width, www.texture.height);
        // we put the downloaded image into the new texture
        www.LoadImageIntoTexture(source);

        // new cubemap 
        Cubemap c = new Cubemap(CubemapResolution, TextureFormat.RGBA32, false);

        Color[] CubeMapColors;

        for (int i = 0; i < 6; i++)
        {
            CubeMapColors = CreateCubemapTexture(CubemapResolution, (CubemapFace)i);
            c.SetPixels(CubeMapColors, (CubemapFace)i);
        }
        // we set the cubemap from the texture pixel by pixel
        c.Apply();

        //Destroy all unused textures
        DestroyImmediate(source);
        DestroyImmediate(www.texture);
        Texture2D[] texs = FindObjectsOfType<Texture2D>();
        for (int i = 0; i < texs.Length; i++)
        {
            DestroyImmediate(texs[i]);
        }

        // We change the Cubemap of the Skybox
        RenderSettings.skybox.SetTexture("_Tex", c);
    }

    /// <summary>
    /// Generates a Texture that represents the given face for the cubemap.
    /// </summary>
    /// <param name="resolution">The targetresolution in pixels</param>
    /// <param name="face">The target face</param>
    /// <returns></returns>
    private Color[] CreateCubemapTexture(int resolution, CubemapFace face)
    {
        Texture2D texture = new Texture2D(resolution, resolution, TextureFormat.RGB24, false);

        Vector3 texelX_Step = (faces[(int)face][1] - faces[(int)face][0]) / resolution;
        Vector3 texelY_Step = (faces[(int)face][3] - faces[(int)face][2]) / resolution;

        float texelSize = 1.0f / resolution;
        float texelIndex = 0.0f;

        //Create textured face
        Color[] cols = new Color[resolution];
        for (int y = 0; y < resolution; y++)
        {
            Vector3 texelX = faces[(int)face][0];
            Vector3 texelY = faces[(int)face][2];
            for (int x = 0; x < resolution; x++)
            {
                cols[x] = Project(Vector3.Lerp(texelX, texelY, texelIndex).normalized);
                texelX += texelX_Step;
                texelY += texelY_Step;
            }
            texture.SetPixels(0, y, resolution, 1, cols);
            texelIndex += texelSize;
        }
        texture.wrapMode = TextureWrapMode.Clamp;
        texture.Apply();

        Color[] colors = texture.GetPixels();
        DestroyImmediate(texture);

        return colors;
    }

    /// <summary>
    /// Projects a directional vector to the texture using spherical mapping
    /// </summary>
    /// <param name="direction">The direction in which you view</param>
    /// <returns></returns>
    private Color Project(Vector3 direction)
    {
        float theta = Mathf.Atan2(direction.z, direction.x) + Mathf.PI / 180.0f;
        float phi = Mathf.Acos(direction.y);

        int texelX = (int)(((theta / Mathf.PI) * 0.5f + 0.5f) * source.width);
        if (texelX < 0) texelX = 0;
        if (texelX >= source.width) texelX = source.width - 1;
        int texelY = (int)((phi / Mathf.PI) * source.height);
        if (texelY < 0) texelY = 0;
        if (texelY >= source.height) texelY = source.height - 1;

        return source.GetPixel(texelX, source.height - texelY - 1);
    }
}

What the code basically does is:

  • Get the panorama texture from www
  • For each face, calculates the texture
  • Assign the generated texture to the cubemap
  • Collect garbage
  • Assign the cubemap to the shader


来源:https://stackoverflow.com/questions/45032579/editing-a-cubemap-skybox-from-remote-image

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!