2
\$\begingroup\$

I am using radiosity method to compute the inter-reflected light within a room. The room is gridded into small patches and the light flux in lumen is calculated for each. This part is done. Now I want to change the colour of each patch so that the room surfaces show a continuous colour banding contours like this image, but have no clue how to this.

enter image description here

Update:

Here is a sample data for a room surface. The data is in this format: (x, y): value

\$\endgroup\$
  • \$\begingroup\$ You need to provide more information on your data. \$\endgroup\$ – jgallant Jun 4 '16 at 15:43
  • \$\begingroup\$ Thanks Jon. The data is(for each surface sub patch) the ratio of the total flux received by the patch to the total lamp flux. \$\endgroup\$ – ali Jun 4 '16 at 18:22
  • \$\begingroup\$ Thanks. Room surfaces are divided into grid squares of 0.3mx0.3m and the value for the centre of each grid cell is computed. Patches values vary between 0-5. The closer the surface to the source of light(ex. the window) the higher the value. Towards the end of the room values linearly go down to zero. \$\endgroup\$ – ali Jun 6 '16 at 8:20
  • \$\begingroup\$ @ali just give Jon some sample data, what's so difficult about that? \$\endgroup\$ – Tyyppi_77 Jun 6 '16 at 11:32
  • \$\begingroup\$ @Jon sorry guys I updated a link to sample data. \$\endgroup\$ – ali Jun 6 '16 at 16:06
6
\$\begingroup\$

Here is a little help on getting you started with this rendering. This may not be the best way to accomplish this task however, it should work.

First I created a structure to hold your data for parsing from text file:

public struct Data {

    public Data(float x, float y, float value){
        X = x;
        Y = y;
        Value = value;
    }

    public float Value;
    public float X;
    public float Y;
}

I then initialized the color palette based on your above image (reversed because I messed up the ordering initially):

Color[] Colors;

private void InitColors()
{
    Colors = new Color[20];

    Colors [19] = new Color (230 / 255f, 29 / 255f, 0 / 255f, 1);
    Colors [18] = new Color (230 / 255f, 74 / 255f, 0 / 255f, 1);
    Colors [17] = new Color (230 / 255f, 119 / 255f, 0 / 255f, 1);
    Colors [16] = new Color (230 / 255f, 164 / 255f, 0 / 255f, 1);
    Colors [15] = new Color (230 / 255f, 207 / 255f, 0 / 255f, 1);
    Colors [14] = new Color (185 / 255f, 207 / 255f, 0 / 255f, 1);
    Colors [13] = new Color (140 / 255f, 207 / 255f, 0 / 255f, 1);
    Colors [12] = new Color (94 / 255f, 207 / 255f, 0 / 255f, 1);
    Colors [11] = new Color (49 / 255f, 207 / 255f, 23 / 255f, 1);
    Colors [10] = new Color (3 / 255f, 207 / 255f, 73 / 255f, 1);
    Colors [9] = new Color (0 / 255f, 207 / 255f, 173 / 255f, 1);
    Colors [8] = new Color (0 / 255f, 198 / 255f, 223 / 255f, 1);
    Colors [7] = new Color (0 / 255f, 153 / 255f,  230 / 255f, 1);
    Colors [6] = new Color (0 / 255f, 108 / 255f, 230 / 255f, 1);
    Colors [5] = new Color (0 / 255f, 63 / 255f, 230 / 255f, 1);
    Colors [4] = new Color (37 / 255f, 18 / 255f, 230 / 255f, 1);
    Colors [3] = new Color (82 / 255f, 0 / 255f, 230 / 255f, 1);
    Colors [2] = new Color (128 / 255f, 0 / 255f, 230 / 255f, 1);
    Colors [1] = new Color (173 / 255f, 0 / 255f, 230 / 255f, 1);
    Colors [0] = new Color (219 / 255f, 1 / 255f, 230 / 255f, 1);
}

I then wrote a parsing function to get the data from your text file into memory, and analyze parts of the data for future use:

private HashSet<float> XValues;
private HashSet<float> YValues;

private List<Data> DataList;

private float Min, MinX, MinY;
private float Max, MaxX, MaxY;

private void ParseData()
{
    Min = float.MaxValue;
    Max = float.MinValue;
    MinX = float.MaxValue;
    MaxX = float.MinValue;
    MinY = float.MaxValue;
    MaxY = float.MinValue;

    DataList = new List<Data> ();
    XValues = new HashSet<float> ();
    YValues = new HashSet<float> ();

    if (File.Exists ("data.txt")) {

        StreamReader file = new StreamReader ("data.txt");
        string line = "";

        while ((line = file.ReadLine ()) != null) {

            line = line.Replace("(", string.Empty);
            line = line.Replace(")", string.Empty);
            line = line.Replace(":", ",");
            string[] split = line.Split (',');

            float x = System.Convert.ToSingle(split [0]);
            float y =  System.Convert.ToSingle(split [1]);
            float value =  System.Convert.ToSingle(split [2]);

            Data data = new Data (x, y, value);

            Min = Mathf.Min (Min, value);
            Max = Mathf.Max (Max, value);
            MinX = Mathf.Min (MinX, x);
            MaxX = Mathf.Max (MaxX, x);
            MinY = Mathf.Min (MinY, y);
            MaxY = Mathf.Max (MaxY, y);

            XValues.Add (x);
            YValues.Add (y);

            DataList.Add (data);
        }

        file.Close ();
    }
}

Now it is easy to convert the parsed data into a Dataset that we can use:

private Data[,] Data;

private void GenerateDataSet()
{
    Data = new Data[XValues.Count, YValues.Count];

    for (int x = 0; x < XValues.Count; x++) {
        for (int y = 0; y < YValues.Count; y++) {
            Data [x, y] = DataList [x + y * XValues.Count];
        }
    }   
}

If we visualize this data now, we get the following:

enter image description here

In order to smooth this out, we are going to add values in between and interpolate based on the distance between known node values:

float[,] Interpolated;

private void Interpolate()
{
    int steps = 25;

    Interpolated = new float[Data.GetLength (0) * steps, Data.GetLength (1) * steps];

    for (int y = 0; y < Interpolated.GetLength (1) - steps; y++) {

        for (int x = 0; x < Interpolated.GetLength (0) - steps; x++) {

            int x1 = x / steps;
            int x2 = x1 + 1;
            int y1 = y / steps;
            int y2 = y1 + 1;

            float distance1 = (new Vector2 (x, y) - new Vector2 (x1 * steps, y1 * steps)).magnitude;
            float distance2 = (new Vector2 (x, y) - new Vector2 (x1 * steps, y2 * steps)).magnitude;
            float distance3 = (new Vector2 (x, y) - new Vector2 (x2 * steps, y1 * steps)).magnitude;
            float distance4 = (new Vector2 (x, y) - new Vector2 (x2 * steps, y2 * steps)).magnitude;

            float value1 = Data [x1, y1].Value;
            float value2 = Data [x1, y2].Value;
            float value3 = Data [x2, y1].Value;
            float value4 = Data [x2, y2].Value;

            int totalCount = 0;

            int count = steps - (int)distance1;
            count = Mathf.Max (count, 0);
            float total = value1 * count;
            totalCount += count;

            count = steps - (int)distance2;
            count = Mathf.Max (count, 0);
            total += value2 * count;
            totalCount += count;

            count = steps - (int)distance3;
            count = Mathf.Max (count, 0);
            total += value3 * count;
            totalCount += count;

            count = steps - (int)distance4;
            count = Mathf.Max (count, 0);
            total += value4 * count;
            totalCount += count;

            total = total / (float)totalCount;

            Interpolated [x, y] = total;
        }
    }
}

Now if we draw this interpolated data, we get something that looks a lot more like what you are trying to output. Here is the Texture generation code that was used:

public Texture2D GetHeatMapTexture()
{
    int width = Interpolated.GetLength (0);
    int height = Interpolated.GetLength (1);

    var texture = new Texture2D (width, height);
    var pixels = new Color[width * height];

    for (var x = 0; x < width; x++)
    {
        for (var y = 0; y < height; y++)
        {               
            float value = Interpolated [x,y];

            float normalized = (value - Min) / (Max - Min);
            normalized *= Colors.Length;
            int colorValue = (int)normalized;
            colorValue = Mathf.Clamp (colorValue, 0, 19);

            pixels [x + y * width] = Colors [colorValue];
        }
    }

    texture.SetPixels(pixels);
    texture.wrapMode = TextureWrapMode.Clamp;
    texture.filterMode = FilterMode.Point;
    texture.Apply();
    return texture;
}

And here is what it looks like with your provided dataset:

enter image description here

There are probably better ways to accomplish this result, but at least this can provide you with a way to at least get started doing what it is you are trying to do.

\$\endgroup\$
  • 1
    \$\begingroup\$ Thanks Jon this is amazing; and sorry I didn't appreciate your asking for sample data first. \$\endgroup\$ – ali Jun 6 '16 at 20:00
  • \$\begingroup\$ No problem at all. The problem looked like fun to tackle, so that is why I was asking for it. Let me know if you have any questions about the above implementation. \$\endgroup\$ – jgallant Jun 6 '16 at 20:07
  • \$\begingroup\$ I believe in Java you can initialize a color with all integers out of 255, so you can get rid of the division. \$\endgroup\$ – Kyranstar Jun 21 '16 at 16:55
  • \$\begingroup\$ @Kyranstar The code above is not java, it is C#. More specifically it is utilizing the Unity Color class. You will of course have to port this code to whatever platform it is you are developing with. \$\endgroup\$ – jgallant Jun 21 '16 at 16:56
  • \$\begingroup\$ Oh sorry, I didn't look closely enough. \$\endgroup\$ – Kyranstar Jun 21 '16 at 16:57

Your Answer

By clicking "Post Your Answer", you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.