- Home /
create readable colour map
Hi =]
I want to output my 2D array to a texture, then read that texture back later to populate that array with the same information
So (thanks to Unity Answers) I have the save part of the script (The chunk of script at the end of the question). Now that part seems to be working excellently, the console returns nice values that are multiples of 0.02
Here is an output for a simple 4x4 map (scaled of course!) :

ok, so now for my reader. It does not return nice friendly values that are multiples of 0.02 at all :
 // me =]
 
 function ReadFromTexture()
 {
     // reset lists
     tileHeight = new int[ heightTexture.width, heightTexture.height ];
     
     Debug.Log( "gridSize H " + heightTexture.width + "," + heightTexture.height + " : T " + typeTexture.width + "," + typeTexture.height );
     
     // populate list with pixel info
     
     for ( var y : int = 0; y < heightTexture.height; y ++ ) 
     {
         for ( var x : int = 0; x < heightTexture.width; x ++ ) 
         {
             // -- HEIGHT --
             
             var heightPxlColour : Color = heightTexture.GetPixel( x, y );
             
             //var tempHeight : float = ( heightPxlColour.r + heightPxlColour.g + heightPxlColour.b ) / 3.0;
             var tempHeight : float = heightPxlColour.r;
             Debug.Log( "tempHeight = " + tempHeight );
             
             tempHeight = tempHeight * 50.0; // 1/0.02;
             Debug.Log( "tempHeight * 50 = " + tempHeight ); 
             
             tempHeight -= 10.0; // from +
             Debug.Log( "tempHeight - 10 = " + tempHeight ); 
             
             //              
             Debug.Log( " " + x + " " + y + " : tileHeight = " + tempHeight );            
             
             tileHeight[ x, y ] = parseInt( tempHeight );
         }
     }
     
     // assign all to TileManager ....
 }
I have set the Texture Import settings to :
 Advanced
 (power of 2 is greyed out, otherwise would be set to none/single)
 Read/Write enabled = true
 mode = Clamp
 max size = 32
 format = automatic truecolor
How can I generate a 'heightmap' from a 2 dimensinal array, then read that texture to return the exact same values.
The 2D array is an array of integers.
Thanks.
Write Texture Script :
 function GenerateMap() 
 {
     // Create a new texture and assign it to the renderer's material
     var texHeight = new Texture2D( tileManager.gridSize.x, tileManager.gridSize.y ); // height texture
     
     // Fill the texture information from tileHeight array
     for ( var y : int = 0; y < texHeight.height; y ++ ) 
     {
         for ( var x : int = 0; x < texHeight.width; x ++ ) 
         {
             // -- height texture --
             
             var hColor : Color;
             
             // - set the colour range -
             
             // range is based on tiles being between
             // -10 and 40 on the Y_axis
             
             var tempColour : float = 0.0;
             
             tempColour = ( parseFloat( tileHeight[ x, y ] ) + 10.0 ); // +10 makes range = 0 to 50
             
             Debug.Log( "tempColour at   height + 10 = " + tempColour );
             
             tempColour = tempColour * 0.02; // 1/50 of the position
             
             Debug.Log( "tempColour at   tempColour * 0.02 = " + tempColour );
             
             tempColour = Mathf.Clamp( tempColour, 0.0, 1.0 ); // clamp 
             
             Debug.Log( "tileHeight " + (x+(x*y)) + " " + tileHeight[ x, y ] + " : tempColour " + tempColour );
             
             hColor = Color( tempColour, 0.0, 0.0 );
             
             texHeight.SetPixel( x, y, hColor );
         }
     }
     
     // Apply all SetPixel calls
     texHeight.Apply();
     
     // save to system
     var timeStamp : String = GetSystemDateTime();
     
     SaveTextureToFile( texHeight, timeStamp + "_heightmap.png" ); // height texture
 }
 
 // http://answers.unity3d.com/questions/245600/saving-a-png-image-to-hdd-in-standalone-build.html
 
 function SaveTextureToFile( texture : Texture2D, fileName )
 {
     var bytes = texture.EncodeToPNG();
     var file = new File.Open( Application.dataPath + "/" + fileName, FileMode.Create );
     var binary = new BinaryWriter(file);
     binary.Write(bytes);
     file.Close();
 }
Edit :
Here is the test code I wrote using Set/GetPixels32. It stores ints between 0 and 255, and returns correct values from the texture :
 #pragma strict
 
 private var myArray : int[ , ];
 
 public var inputTexture : Texture2D;
 private var readArray : int[ , ];
 
 function Start() 
 {
     PopulateArray();
 }
 
 function Update() 
 {
     if ( Input.GetMouseButtonDown(0) || Input.GetKeyDown(KeyCode.F1) )
     {
         GenerateTexture();
     }
     else if ( Input.GetMouseButtonDown(1) || Input.GetKeyDown(KeyCode.F2) )
     {
         ReadTexture();
     }
 }
 
 // ----
 
 function PopulateArray() 
 {
     myArray = new int[ 4, 4 ];
     
     for ( var i:int = 0; i < 4; i ++ )
     {
         myArray[ 0, i ] = i;
         myArray[ 1, i ] = i * 20;
         myArray[ 2, i ] = i * 50;
         myArray[ 3, i ] = i * 62;
     }
 }
 
 // ----
 
 function GenerateTexture() 
 {
     var tempTexture : Texture2D = new Texture2D( 4, 4 );
     var tempColourArray : Color32[] = new Color32[ 16 ];
     var index : int = 0;
     
     for ( var y : int = 0; y < tempTexture.height; y ++ ) 
     {
         for ( var x : int = 0; x < tempTexture.width; x ++ ) 
         {
             tempColourArray[ index ++ ] = Color( myArray[ x, y ] / 255.0, 0.0, 0.0 );
         }
     }
     
     tempTexture.SetPixels32( tempColourArray, 0 );
     tempTexture.Apply( false );
     
     // save to system
     var timeStamp : String = GetSystemDateTime();
     SaveTextureToFile( tempTexture, timeStamp + "_colourmaptest.png" );
 }
 
 // ----
 
 function ReadTexture() 
 {
     var tempArray : Color32[] = inputTexture.GetPixels32( 0 );
     readArray = new int[ 4, 4 ];
     
     var success : boolean = true;
     var index : int = 0;
     
     for ( var y:int = 0; y < 4; y ++ )
     {
         for ( var x:int = 0; x < 4; x ++ )
         {
             readArray[ x, y ] = parseInt( tempArray[ index ].r * 1.0 );
             
             if ( readArray[ x, y ] != myArray[ x, y ] )
             {
                 Debug.Log( "index " + index + " mismatch at ( " + x + ", " + y + " ) : read = " + readArray[ x, y ] + " : write = " + myArray[ x, y ] );
                 success = false;
             }
             
             index ++;
         }
     }
     
     Debug.Log( "success = " + success );
 }
 
 // ----
 
 // http://answers.unity3d.com/questions/245600/saving-a-png-image-to-hdd-in-standalone-build.html
 
 function SaveTextureToFile( texture : Texture2D, fileName )
 {
     var bytes = texture.EncodeToPNG();
     var file = new File.Open( Application.dataPath + "/" + fileName, FileMode.Create );
     var binary = new BinaryWriter(file);
     binary.Write(bytes);
     file.Close();
 }
 
 // ----
 
 // Me
 
 function GetSystemDateTime() : String
 {
     // get the date time
     var tempStamp : String = System.DateTime.Now.ToString();
     // remove "/"
     var dateStampSplit : String[] = tempStamp.Split( "/"[0] );
     // remove ":"
     var timeStampSplit : String[] = dateStampSplit[2].Split( ":"[0] );
     // remove " "
     var space1split : String[] = timeStampSplit[0].Split( " "[0] );
     var space2split : String[] = timeStampSplit[2].Split( " "[0] );
     // put timeStamp together
     var timeStamp : String = "";
     timeStamp = dateStampSplit[0] + dateStampSplit[1] + space1split[0] + space1split[1] + timeStampSplit[1] + space2split[0] + space2split[1];
     return timeStamp;
 }
 
 // ----
What does it return? On first reading it looks ok - with the caveat that I'm not 100% on the parseInt being necessary (but JS isn't my language).
 tempHeight = 0.2
 tempHeight * 50 = 10
 tempHeight - 10 = 0
  0,0 : tileHeight = 0
 tempHeight = 0.2196078
 tempHeight * 50 = 10.98039
 tempHeight - 10 = 0.9803925
  1,0 : tileHeight = 0.9803925
 tempHeight = 0.2392157
 tempHeight * 50 = 11.96078
 tempHeight - 10 = 1.960784
  2,0 : tileHeight = 1.960784
 tempHeight = 0.2588235
 tempHeight * 50 = 12.94118
 tempHeight - 10 = 2.941177
  3,0 : tileHeight = 2.941177
I am expecting 0 : 1 : 2 : 3 . I don't know if it is the type of image (png, or should I save to .jpg), don't know about the different formats. Thanks..
That is the Debug when reading, this is the Log for writing :
 tempColour at   height + 10 = 10
 tempColour at   tempColour * 0.02 = 0.2
 tileHeight 0 0 : tempColour 0.2
 0 0
 
 tempColour at   height + 10 = 11
 tempColour at   tempColour * 0.02 = 0.22
 tileHeight 1 1 : tempColour 0.22
 1 1
 
 tempColour at   height + 10 = 12
 tempColour at   tempColour * 0.02 = 0.24
 tileHeight 2 2 : tempColour 0.24
 2 2
 
 tempColour at   height + 10 = 13
 tempColour at   tempColour * 0.02 = 0.26
 tileHeight 3 3 : tempColour 0.26
 3 3
I think you can see what I'm trying to do. Can I use a texture pixel as a base_255 reference (store and return a value between 0 and 255, or just 0 and 50 in my case). Can this be done accurately to return integer values?
Actually ... ignoring my calculations and just looking at what set/get pixels is showing :
the value for color.r in Set is :
 0.2; 0.22; 0.24; 0.26;
the value for color.r in Get is :
 0.2; 0.2196078; 0.2392157; 0.2588235;
this is what I find confusing, it is returning a different value for a pixel set by the same script. Hence my queries on image extension or import settings.
Answer by whydoidoit · Dec 02, 2012 at 12:04 PM
So images are not stored using floating points - they are stored using bytes :) Your code is therefore:
- Converting an int to a float 
- Messing with it 
- Converting back into an int (with only 255 discrete values) by writing it to an image! 
You really want to be using the GetPixels32 to return the whole image as an array and then you can actually access the integers stored in the file :)
Thanks, as this is new ground for me, have some testing to do. Even the API says Using SetPixels32 is faster than calling SetPixels. , so shall let you know how I go with converting my 2D arrays to textures and back. (another command I was not aware of, thanks).
messing with it ... still chuckling. I get that sometimes from my colleague, or as he says doing it longhand. I thought I did ok normalizing an integer value between -10 and +40 :)
Yeah that's why there's so much inaccuracy in the values co$$anonymous$$g back - they've been hacked around with too much. You could probably get away with it by using $$anonymous$$athf.RoundToInt() but it's not really the right method. We think of colours as floats in Unity - but that's really not the case anywhere but in the shader.
thanks for the SetPixels32 help, that was a silly mistake.
Here's what I came up with (see edit to question). $$anonymous$$any thanks for all your help and pointers, have learned a new technique and a new command. Awesome.
What I was having problems with ( and cannot get my head around) is that when the pixel is assigned, I divide by 255 :
 tempColourArray[ index ] = Color( myArray[ x, y ] / 255.0, 0.0, 0.0 );
when I read the pixel, it already comes back multiplied :
 readArray[ x, y ] = parseInt( tempArray[ index ].r * 1.0 );
so I write a value of value/255 and read value for the same value. I shall not question, it works.
Your answer
 
 
             Follow this Question
Related Questions
Dynamically created texture is off in BFE... 0 Answers
GetPixel Returning Inaccurate Color 1 Answer
Texture2D.GetPixel returning wrong colours 3 Answers
getPixels returning nan on iOS 0 Answers
 koobas.hobune.stream
koobas.hobune.stream 
                       
                
                       
			     
			 
                