- 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