Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Editing a Cubemap Skybox from remote image

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 ?

like image 596
Alburkerk Avatar asked Jul 11 '17 10:07

Alburkerk


People also ask

How do you make a textured cubemap?

Select Assets > Create > Legacy > Cubemap from the menu, and drag six textures into empty slots in the inspector. Textures for the corresponding cubemap face. Width and Height of each Cubemap face in pixels. The textures will be scaled automatically to fit this size.

What is a cubemap texture?

A Cubemap Texture is a texture, where each mipmap level consists of six 2D images which must be square. The 6 images represent the faces of a cube. The texture coordinate used to access a cubemap is a 3D direction vector which represents a direction from the center of the cube to the value to be accessed.

What is cubemap reflection?

Specular Indirect Lighting is stored in an array of cubemaps. These are defined by the Reflection Cubemap objects. They specify where to sample the scene's lighting and where to apply it.


1 Answers

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
like image 105
Max Play Avatar answered Oct 13 '22 00:10

Max Play