Search Unity

PixelUV Partially Working

Discussion in 'Scripting' started by kittik, May 14, 2018.

  1. kittik

    kittik

    Joined:
    Mar 6, 2015
    Posts:
    565
    I am converting square Perlin Maps into hexagonal maps. To do this, I am using Raycasts to find colors on the original map, and retexturing hexagons, which are put into place.

    For the first half of hexagons, they successfully read the correct color, the second half doesn't. This half working/half broken pattern is constant.

    An example map, which only half works is below



    As you can see, the lower half shows the correct land formations, while the upper half shows blobs of color that does not correspond with the map.

    The relevant code is in a method GetMapColor(), which is below
    Code (CSharp):
    1. void GetMapColor()
    2.     {
    3.         for(int i = 0; i < hexManager.numOfHexes; i++)
    4.         {
    5.             RaycastHit hit;
    6.             if(!Physics.Raycast(hexGenerator.seperateHexagons[i].transform.position, Vector3.down, out hit))
    7.             {
    8.                 Debug.LogError("No raycast");
    9.                 continue;
    10.             }
    11.  
    12.             Renderer rend = mapGen.display.textureRenderer;
    13.             MeshCollider meshCol = hit.collider as MeshCollider;
    14.  
    15.             if(rend == null || rend.sharedMaterial == null || rend.sharedMaterial.mainTexture == null || meshCol == null)
    16.             {
    17.                 Debug.LogError("No Renderer!");
    18.                 continue;
    19.             }
    20.  
    21.             Texture2D tex = rend.material.mainTexture as Texture2D;
    22.             Vector2 pixelUV = hit.textureCoord;
    23.             pixelUV.x *= tex.width;
    24.             pixelUV.y *= tex.height;
    25.  
    26.             Color myColor = tex.GetPixel((int)pixelUV.x, (int)pixelUV.y);
    27.             Debug.Log("Hex " + i.ToString() + " has the RGB value of " + myColor.ToString());
    28.  
    29.             Renderer hexRend = hexGenerator.seperateHexagons[i].GetComponent<Renderer>();
    30.             string terrainType = "";
    31.  
    32.             foreach(TerrainType tt in mapGen.regions)
    33.             {
    34.                 if(getApproximate(myColor.r, tt.color.r, colorTolerance) && (getApproximate(myColor.g, tt.color.g, colorTolerance)) && (getApproximate(myColor.b, tt.color.b, colorTolerance)))
    35.                 {
    36.                     Debug.Log("Hex " + i.ToString() + " is " + tt.name + " and has the coordinates of " + hexGenerator.seperateHexagons[i].transform.position.ToString());
    37.                     Debug.Log("Hex " + i.ToString() + " also has the pixelUV.x " + pixelUV.x.ToString() + " y " + pixelUV.y.ToString());
    38.                     hexDictionary.SetHexTerrain(i, tt.name.ToLower());
    39.                     terrainType = tt.name;
    40.                     terrainCells.Add(tt.name);
    41.  
    42.                     hexRend.material.color = tt.color;
    43.  
    44.                     break;
    45.                 }
    46.             }
    47.         }
    48. }

    If anyone has a solution, as to why the upper half of the map is wrong, I would really appreciate the support!
     
  2. McDev02

    McDev02

    Joined:
    Nov 22, 2010
    Posts:
    664
    Out of the blue I would say that the error is within your foreach loop, which I have no idea what it is doing. What is going on inside getApproximate? You seem to try to interpolate a color.

    You can check by skipping the foreach loop and simply add myColor to the hexagon or new Color(pixelUV.x,pixelUV.y,0);

    Does the pixel map have propper colliders? I think it needs a non-convext meshcollider.
     
  3. kittik

    kittik

    Joined:
    Mar 6, 2015
    Posts:
    565
    Thanks @McDev02, getApproximate is used, as I had a bug where taken colors of a color code, were slightly out from those needed for specific terrains. It checks if a figure is within a margin of error. The original maps have Mesh Colliders, which are not convex.

    After commenting out the foreach loop and applying the colors straight to hexagons, the same problem occurs. The top half of the map does not correspond with the original Perlin Map.


    Code (CSharp):
    1.     void GetMapColor()
    2.     {
    3.         for(int i = 0; i < hexManager.numOfHexes; i++)
    4.         {
    5.             RaycastHit hit;
    6.             if(!Physics.Raycast(hexGenerator.seperateHexagons[i].transform.position, Vector3.down, out hit))
    7.             {
    8.                 Debug.LogError("No raycast");
    9.                 continue;
    10.             }
    11.  
    12.             Renderer rend = mapGen.display.textureRenderer;
    13.             MeshCollider meshCol = hit.collider as MeshCollider;
    14.  
    15.             if(rend == null || rend.sharedMaterial == null || rend.sharedMaterial.mainTexture == null || meshCol == null)
    16.             {
    17.                 Debug.LogError("No Renderer!");
    18.                 continue;
    19.             }
    20.  
    21.             Texture2D tex = rend.material.mainTexture as Texture2D;
    22.             Vector2 pixelUV = hit.textureCoord;
    23.             pixelUV.x *= tex.width;
    24.             pixelUV.y *= tex.height;
    25.  
    26.             Color myColor = tex.GetPixel((int)pixelUV.x, (int)pixelUV.y);
    27.             Debug.Log("Hex " + i.ToString() + " has the RGB value of " + myColor.ToString());
    28.  
    29.             Renderer hexRend = hexGenerator.seperateHexagons[i].GetComponent<Renderer>();
    30.             string terrainType = "";
    31.  
    32.             hexRend.material.color = myColor;
    33.  
    34.         }
    35.     }
    Could it be that the maps wrapping could cause be the problem? I am using TextureWrapMode.Clamp.

    Maybe, it is because some of the hexagons are minus numbers, while others are positive. I can't think why that would be the reason.

    I am still having problems with this.
     
  4. tonemcbride

    tonemcbride

    Joined:
    Sep 7, 2010
    Posts:
    1,089
    Your hit.textureCoord UV values may not be between 0 and 1(since meshes can have UV values outside of 0..1). You could do something like:

    pixelUV.x = ( pixelUV.x + 1024.0f ) % 1.0f;
    pixelUV.y = ( pixelUV.y + 1024.0f ) % 1.0f;
     
    kittik likes this.
  5. kittik

    kittik

    Joined:
    Mar 6, 2015
    Posts:
    565
    @tonemcbride, if you're referring to using the above as a replacement to:

    pixelUV.x = tex.width;
    pixelUV.y = tex.height;

    It leads to every node becoming the same color. How are you suggesting I implement your change?
     
  6. McDev02

    McDev02

    Joined:
    Nov 22, 2010
    Posts:
    664
    It should be placed before multiplying with texture size.

    Are you sure that the game objects are alligned properly so they hit the right target?
    Does your map consist of one texture or multiple ones? Are there any errors like "No Renderer!"?

    Please try the following and make a screenshot. Because the upper image sseems like a scaled up version. If there is no break then maybe tex is wrong.
    Code (CSharp):
    1. //Replace
    2. hexRend.material.color = myColor;
    3. //With
    4. hexRend.material.color = new Color(pixelUV.x,pixelUV.y,0);
     
    kittik likes this.
  7. kittik

    kittik

    Joined:
    Mar 6, 2015
    Posts:
    565
    After making those changes, the hex map consists largely of yellow tiles, which is interesting, as there's no yellow in the scene. "No Renderer!" and "No raycast" never display in the logs. The only oddity which does seem to occur is in the previous foreloop, where "Hex " + i.ToStrong() + " also has the pixelUV.x " + pixel.x.ToString() + " y " + pixelUV.y.ToString()" as the first Hexagon 0 would have the pixelUV of x 100, y 100 and the final Hexagon would have the pixelUV of x 40.3125, y 40.3125. Even though the hexagons are in a square formation when they are taking on the colors of the Perlin Map. Later, the hexagons revert back to their original positions (when they were formed in a rectangle and pink (shown in a provided video))

    The Perlin Map is scaled 100 on the x and z, and hexagons are spaced evenly on the map. The hexagons are placed by getting the number of hexagons in a column, and row, dividing the Perlin Maps bounds X and Z and using the sum as the transform for each node. Not every part of the Perlin Map is being shot at.

    I have produced a video showing the code and outcome this attempt.



    Thank you for your assistance so far.

    Edit

    I have added a video of the original way it looked, when the thread was created. As you can see, the lower half of hexagons take on the correct color, while the upper half do not.

     
    Last edited: May 14, 2018
  8. McDev02

    McDev02

    Joined:
    Nov 22, 2010
    Posts:
    664
    If you used what I suggested "new Color(pixelUV.x,pixelUV.y,0);" then I mean raw UV data, before multiplied by texture size. Therefore the yellow pixels (1,1,0). Sorry for that.
    The result should be a smooth fade from black to green and red towards each corner.

    Also I would not use Raycast at all, it is not necessary and less stable. I would simply provide the texture to the method and simply calculate UVs based on world position and offset values.

    If the lower right corner of the map is at the origin then simply using Hexagon positon x,y for pixelUV would do the same. You only have to normalize positions by dividing by scale of the map, so the spacing of the hexagons.

    Code (CSharp):
    1. var wPos = hexGenerator.seperateHexagons[i].transform.position;
    2. Vector2 uv = new Vector2(wPos.x,wPos.z);
    3. //scaleModifer = The scale of the map. When you multiply the top right hexagon's position by that the result should be close to (1,1)
    4. uv.x = scaleModifer.x * tex.width;
    5. uv.y = scaleModifer.y * tex.height;
     
    Last edited: May 14, 2018
    kittik likes this.
  9. kittik

    kittik

    Joined:
    Mar 6, 2015
    Posts:
    565
    Thank you @McDev02, your advice has really helped me. I hadn't considered removing the Raycasts and looking at pixels differently. A working example is -



    The much condensed script is -

    Code (CSharp):
    1. void GetMapColor()
    2.     {
    3.         int hexesColored = 0;
    4.         int numOfHexes = hexManager.numOfHexes;
    5.  
    6.         Renderer rend = mapGen.display.textureRenderer;
    7.         Texture2D tex = rend.material.mainTexture as Texture2D;
    8.  
    9.         Color[] perlinPixels = tex.GetPixels(0, 0, tex.width, tex.height);
    10.  
    11.         Debug.Log("perlinPixels contains " + perlinPixels.Length.ToString() + " entries");
    12.  
    13.         while(hexesColored < numOfHexes)
    14.         {
    15.             for(int x = 0; x < tex.width; x++)
    16.             {
    17.                 for(int y = 0; y < tex.height; y++)
    18.                 {
    19.                     Color pixelColor = perlinPixels[(x * tex.width) + y];
    20.                     Renderer hexesRenderer = hexGenerator.seperateHexagons[hexesColored].GetComponent<Renderer>();
    21.  
    22.                     hexesRenderer.material.color = pixelColor;
    23.  
    24.                     hexesColored ++;
    25.                 }
    26.             }
    27.         }
    28.     }
     
    McDev02 likes this.