Search Unity

Encode to PNG

Discussion in 'Editor & General Support' started by unity3dcoder, Dec 2, 2013.

  1. unity3dcoder

    unity3dcoder

    Joined:
    Apr 15, 2013
    Posts:
    8
    I am using the following javascript to try and export my splatmaps as png files :

    Code (csharp):
    1. // For saving splat map as PNG file.
    2. // place the JS script in the assets/editor folder, and it then appears in the Unity3d assets pulldown.
    3. import System.IO;
    4.  
    5. @MenuItem("Assets/Export Texture")
    6. static function Apply () {
    7.    var texture : Texture2D = Selection.activeObject as Texture2D;
    8.    if (texture == null)
    9.    {
    10.       EditorUtility.DisplayDialog("Select Texture", "You Must Select a Texture first!", "Ok");
    11.       return;
    12.    }
    13.  
    14.    var bytes = texture.EncodeToPNG();
    15.    File.WriteAllBytes(Application.dataPath + "/exported_texture.png", bytes);
    16. }
    The script appears to work ok as when it is run, I get an RGB-colored png file with the correct filename. My problem is when I open it in Photoshop it only has the blue channel present (in all channels).

    There is probably something insanely easy that I'm not doing, can anyone offer advice ?

    Thanks in advance.
     
  2. Kragh

    Kragh

    Joined:
    Jan 22, 2008
    Posts:
    657
    The problem is how the splatmap works, and how this is very much problematic for a format like .png and Photoshop. When you work with files in photoshop, there are two ways of representing transparency. In the layers themselves, or in an actual alpha channel. When you load a .png file into photoshop, it will NOT create an alpha channel, it will make the layers have transparency. Now this is a problem with splatmaps. In a splatmap the alpha is just a part of the four different values of a color: rgba.

    So here's the deal: Imagine you have four different textures you are painting your terrain with. Their strength will be encoded into each of the four components of a color object.
    Texture1 goes into the r component,
    Texture2 in the g component,
    Texture3 in the b component
    -and Texture4 in the a component.
    And so we have a full color object: rgba

    Imagine now you paint the Terrain only with Texture1. So the splatmap will have all pixel defined with "1.0" in the r channel. See the problem? The Texture has "0.0" in the a component (and also in the g and b component for that matter). So, seen from photoshop it is simply transparent. All over. Each pixel will look like this (r=1, g=0, b=0, a=0). So transparent (a value of "1.0" in the a component is always full opaque, and "0.0" is...transparent)

    Only when you actually paint with the fourth texture will you have pixels that show up in photoshop.

    There exists a workaround? Yeah... :)

    When you export the splatmap you take each pixel in the texture, and modify it. You simply set the a component to "1.0".
    Like this (C#, you have to modify for JS) This is untested code just to show you the concept!!!:



    Code (csharp):
    1.  
    2. Texture2D texture = Instantiate(splatTexture) as Texture2D;
    3.  
    4. Color[] textureColors = texture.GetPixels();
    5.  
    6. for (int i = 0; i < textureColors.Length; i++) {
    7.  
    8. textureColors[i].a = 1;
    9.  
    10. }
    11.  
    12. texture.SetPixels(textureColors);
    13. texture.Apply();
    14.  
    15. //And then you export to .png as normal!

    Now surely this splatmap is no longer valid for use on a terrain. But you can work on it. When you get it into Photoshop, black pixels will be the ones defining your fourth texture.

    When you have modified it, or whatever, you need to translate it back to some valid format for the terrain. In a splatmap the main thing is that each component of a color, when summed together, needs to add up to "1.0". They are "normalized" in other words. After your little trick, all pixels will add up to more, besides the ones that define your fourth texture.
    For instance this, where you have Texture1 and Texture2 painted on at 50% strength each: (r=0.5, g=0.5, b=0, a=1). Which will add up to "2.0".

    So you need to do this:

    Code (csharp):
    1. Color[] textureColors = modifiedTexture.GetPixels();
    2.  
    3. for (int i = 0; i < textureColors.Length; i++)
    4. {
    5.  
    6. textureColors[i].a = 1.0-(textureColors[i].r+textureColors[i].g+textureColors[i].b); //Your [B]a[/B] components will be set to whatever is leftover from the other components, when summed)
    7.  
    8. }
    9.  
    10. modifiedTexture.SetPixels(textureColors );
    11.  
    12. //Now do whatever you want to do to set it back as a splatmap. You could simply modify the pixels of the splatmap directly, using the modified texture as a source.
    Good luck. I hope by this explanation I have made this a little less magical to you. It makes perfect sense why it goes wrong, when you know how it works under the hood...:)

    EDIT: I just changed my code to make sure you don't modify the actual splat texture, as it will look awful untill you put back a valid version. No harm done, though, should you modify the splat texture directly. You can ALWAYS recalculate the correct a value, by subtracting the sum of the other channels from "1.0"
     
    Last edited: Dec 3, 2013
  3. unity3dcoder

    unity3dcoder

    Joined:
    Apr 15, 2013
    Posts:
    8
    Tusind tak, Kragh !

    Your explanation makes perfect sense, even to a noob like me !!

    I will check the code out when I get home :)