First off, let me say this, it makes no sense why Unity uses a one dimensional array to set pixels. It should be that you use a two dimensional array, but I'll have to work with Unity's system. Anyway, I tried making something where I would pass in a 2 dimensional array, like Code (CSharp): Color[,] newImage = new Color[,] { { Color.red, Color.blue }, { Color.green, Color.black } }; And it would create a custom texture that looked like that; Top left is red, Top Right is blue, Bottom Left is green, and Bottom Right is black. So, I wrote this method up. Code (CSharp): void changeImage(Color[,] image) { int width = image.GetLength(0); int height = image.GetLength(1); Renderer renderer = GetComponent<Renderer>(); Texture2D newTexture = new Texture2D(width, height); Color[] imageOneD = new Color[width * height]; for (int x = 0; x < width; x++) { //then, while Y is 0, x will go to 0 and 1 for (int y = 0; y < height; y++) { imageOneD[(width * y) + x] = image[x, y]; //doing (0, 1), then (1, 1)...WHY!?!? print(image[x, y]); print(imageOneD[width * y + x] + " "); } } renderer.material.shader = Shader.Find("Unlit/Texture"); newTexture.filterMode = FilterMode.Point; newTexture.SetPixels(0, 0, width, height, imageOneD); newTexture.Apply(); renderer.material.mainTexture = newTexture; } So, what I am doing is flattening the 2 dimensional array into a 1 dimensional array so that I can use it with texture.setPixels, but when I look at the object I am using it on, it looks like this: blue black red green, when it should be: red green blue black So...What happened? Well, it is switched. The first row is on the bottom, and the second row is on the top. Can someone help?
Edit: Ok, so, I gave it a new image array for testing, I gave it this: { Color.red, Color.blue, Color.gray }, { Color.green, Color.black, Color.white } And, the output is a 2x3 image with gray and white on top...So...It must be rotated weirdly. I don't want to have to rotate the actual gameObject, so, can someone help?
Its because a texture's rectangle starts in the bottom left corner and goes to the top right corner. When your passing in that array: Your probably wanting red,blue,gray to be on top. But since its the first Colors in your array they start at the bottom left and tile their way up to the top. You either need to pass your array in to match the bottom->top drawing of a texture or flip it like this: Code (CSharp): for (int x = 0; x < width; x++) { //then, while Y is 0, x will go to 0 and 1 for (int y = 0; y < height; y++) { // Read from the end of the image array to get the // bottom first imageOneD[(width * y) + x] = image[x, (height-1-y]; //doing (0, 1), then (1, 1)...WHY!?!? print(image[x, y]); print(imageOneD[width * y + x] + " "); } } Another side note: Code (CSharp): newTexture.SetPixels(0, 0, width, height, imageOneD); is the same as Code (CSharp): newTexture.SetPixels( imageOneD); You only need to specify a start x,y and a width/height if your setting a partial block of the texture. SetPixels assumes your setting the whole thing.
Just noticed your loop is inverted as well thats why you are getting the 3x2 flipped into a 2x3: Code (CSharp): for (int y=0;y<height;y++) { for (int x=0;x < width;x++) { imageOneD[width*y]+x] = image[x,(height-1-y)]; } } This code reads the entire last row of your array and copies it into the "first row" of the OneD array. That way your bottom row is the bottom row of the image.
Thanks, but, this doesn't work how I would expect... It's still a 2x3 image (Should be 3x2, with red, blue, and grey on top), but now, it's still a 2x3, but with grey and white on bottom now. This is the code so far: Code (CSharp): for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { imageOneD[(width * y) + x] = image[x, height - 1 - y]; print(image[x, y]); print(imageOneD[width * y + x] + " "); } }
Its probably this: Code (CSharp): int width = image.GetLength(0); int height = image.GetLength(1); GetLength(0) returns the rows -- in your case height GetLength(1) returns the columsn in your case that would be width. Try: Code (CSharp): int height = image.GetLength(0); int width = image.GetLength(1);
I'm sorry, I've been trying to debug this for a few, but whenever I do what you just said, it always gives me an index out of range on this line: Code (CSharp): imageOneD[(width * y) + x] = image[x, y]; I can't say I know why, though. I used a try catch, and they x, y of the error is 2, 0. If I do what you just said, (Width * y) + x is (3 * 0) + 2...Which should NOT be an index out of range.
Though, you were not wrong. After printing off the values, I found out that the width and height are correct now...Just need to find out why it errors.
So I was both right and wrong about the GetLength calls. GetLength(0) does in fact get the length of first index of the array (what we were calling x). GetLength(1) does return the second index. (y). The problem was in how you initialized the array. After looking up initializing arrays (never done it really) C# does it column by column so your initalization makes 2 Columns (x value) with 3 items each (y value) so its a 2x3 array if your thinking of it in terms of Color[2,3] creates an array that looks like this: RED (0,0) GREEN (1,0) BLUE(0,1) BLACK (1,1) GRAY(0,2) WHITE (1,2) Which is why it was working when you had the width/height backwards.. because your initalization was "backwards" too. So to create this setup of colors: RED (0,0) BLUE (1,0) GRAY(2,0) GREEN(0,1) BLACK(1,1) WHITE (2,1) you need this: Code (CSharp): Color[,] image = new Color[,] { { Color.red, Color.green}, { Color.blue, Color.black }, {Color.gray, Color.white } }; This sets up a 3x2 array. The first row (y value =0) takes the first item from each set. The 2nd row (y=1) takes the 2nd item from each set. If you initalize your colors like above and flip the width/height back to the original way you had it : Code (CSharp): int width = image.GetLength(0); int height = image.GetLength(1); Then the loop I wrote should work. Its all about making sure you understand the implementation of how the 2D array is setting things up.. and how you read it back out.
Thanks, this works great! On thing I will try to do is write something to rotate it, because, when I initialize the array, I want it to look like the final image.
Well if you initialize it like this: Code (CSharp): { Color.red, Color.blue, Color.gray }, { Color.green, Color.black, Color.white } Then i think this loop will create the texture to look like your initialization Red,blue,gray across the top, then green,black,white across the bottom. if Code (CSharp): int width = image.GetLength(0); int height = image.GetLength(1); int count =0; for (int x=0;x<width;x++) for (int y=0;y<height;y++) imageOneD[count++] = image[width-1-x,y]; That is assumine imageOneD is applied to a 3x2 texture. (Note: the image your passing in is a 2x3.. but that doesn't matter. we are flipping it around to work into a 3x2)
WOW! I know I'm way late to this post, but, I just want to say I am really impressed by the amount of effort that went into answering this question. Very cool to see.
It's still ridiculous that we have to write up an array converter for a basic feature, especially when Unity suggests using this instead of looping GetPixel.
new recommended way of doing things fast https://docs.unity3d.com/ScriptReference/Texture2D.GetRawTextureData.html