5

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 ?

Alburkerk
  • 1,564
  • 13
  • 19
  • 1
    Cubemap have 6 faces. You are supposed to set all 6 of them but you are only setting one `CubemapFace.PositiveX` in your code. – Programmer Jul 11 '17 at 11:20
  • But how should I proceede to split the image in 6 part then ? In fact I read that the Unity Editor was doing that on itself when we set the Cubemap prperty for the material, but I havn't found any example of doing that with c# script – Alburkerk Jul 11 '17 at 13:04
  • 1
    Are you trying to put the same image on all 6 sides or do you want to unwrap the image? – user1767754 Jul 11 '17 at 14:49
  • Well it is an image taken on purpose to be a 360 image, so I just want the same image to be the whole skybox. What I do in Editor to obtain that manually is basically this : https://www.youtube.com/watch?v=kUROoI6_SmA (1m30 video) – Alburkerk Jul 11 '17 at 15:59
  • Unity doesn't support changing texture import setting at runtime. Import settings seen in inspector belong to AssetImporter class which is not availabel at runtme. Instead try to convert 360 image to 6 images and then download those images from server. Now set the shader of material to skybox/6 sided and assign all those textures to it at runtime – jagadheeswar reddy Aug 01 '17 at 12:11
  • Thank you for you comment jagadheeswar. Do you think instead of downloading 6 images, find a way to format my images as skybox on server side with some automation process (maybe some bash programm with modules like imagemagick makes it possible ) would be a more efficient way ? – Alburkerk Aug 01 '17 at 15:27
  • What about making assetbundle of skybox and hosting it on server. Then download that assetbundle and retrieve skybox at runtime. If you are okay with assetbundle approach, then let me know if you need any help with the whole process of making and hosting assetbundle – jagadheeswar reddy Aug 02 '17 at 12:18
  • What's the cubemap image format? – Programmer Aug 03 '17 at 09:56

2 Answers2

6

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
Max Play
  • 3,717
  • 1
  • 22
  • 39
0

I used the solution with a image on the computer.

I had a probleme where the image's color have lost intensity, I found that it was because sRGB wasn't set to true. I fixed it by replacing :

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

in the setImage function by:

Cubemap c = new Cubemap(CubeMapResolution, DefaultFormat.LDR, TextureCreationFlags.None);
Ticherhaz FreePalestine
  • 2,738
  • 4
  • 20
  • 46
  • Hi @BarbasOyun, I am having problem with the intensity and resolution of the skybox as well. I tried your solution, but it still creates a pixelated and intensity-less skybox. Here is what I get vs what I want: https://snipboard.io/JLCd0B.jpg Could you please suggest what could I try other than what you answered? – Sanket Kale Sep 21 '21 at 17:31