Search Unity

save texture generated by a shader

Discussion in 'General Graphics' started by ghiboz, May 18, 2015.

  1. ghiboz

    ghiboz

    Joined:
    Sep 7, 2012
    Posts:
    465
    hi all!
    I made a shader that combine more textures to create a single 'texture'
    I wish save this 'texture' generated into a png file, how can I do?
    thanks in advance
     
  2. Zicandar

    Zicandar

    Joined:
    Feb 10, 2014
    Posts:
    388
    "Simplest" option might be to dump it to the screen, then read that and save.
    Another not to hard option, (assuming your on windows DX10+), is to use a compute shader to copy the, (I'm assuming render texture, as normal textures have a solution already), into a compute buffer, then you can add the png header, and dump it to disk.
    "Better" options include getting the native texture pointer and using some more low level calls to get the texture.
    The thing with textures is that they normally reside only in the GPU, so they would have to be copied out to the CPU to be savable. And while this copying is going on, the GPU more or less goes into lockdown, so I understand and almost all of the time agree with it. (Until I need to do something that requires me to get it >.<...)
     
  3. mholub

    mholub

    Joined:
    Oct 3, 2012
    Posts:
    123
    1. Render into RenderTexture with Camera or Graphics.Blit
    2. Use Texture2D.Read to read from GPU RAM to RAM.
    3. Texture2D.EncodeToPNG to get PNG representation
     
  4. ghiboz

    ghiboz

    Joined:
    Sep 7, 2012
    Posts:
    465
    thanks @mholub , but how can I render my material into rendertexture?
     
    Rachan likes this.
  5. mholub

    mholub

    Joined:
    Oct 3, 2012
    Posts:
    123
    twobob likes this.
  6. ghiboz

    ghiboz

    Joined:
    Sep 7, 2012
    Posts:
    465
    hmmm @mholub ... but my situation is this:
    I've in my scene a plane that have my shader as material, so I need to save it into a 2048x2048 texture, reading the documentation Graphics.Blit needs the inverse thing: a texture as input and material as output...
     
  7. mholub

    mholub

    Joined:
    Oct 3, 2012
    Posts:
    123
    Texture and Material are inputs, RenderTexture is output.

    Here is simple example
     

    Attached Files:

    twobob and theANMATOR2b like this.
  8. ghiboz

    ghiboz

    Joined:
    Sep 7, 2012
    Posts:
    465
    thanks, I've tried to add to my project: here is the situation before baking:


    and this after the baking:

    the result is all black...
    the code is this:
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.IO;
    4.  
    5. public class BakeMaterial : MonoBehaviour
    6. {
    7.     public RenderTexture ResultTexture;
    8.     public int Size = 2048;
    9.  
    10.     public Material ReplaceMaterial;
    11.     public bool DoBake = false;
    12.     void Update()
    13.     {
    14.         if (DoBake)
    15.         {
    16.             DoBake = false;
    17.             Bake();
    18.         }
    19.     }
    20.  
    21.     // Use this for initialization
    22.     public void Bake()
    23.     {
    24.         if (ResultTexture == null)
    25.         {
    26.             ResultTexture = new RenderTexture(Size, Size, 0);
    27.             ResultTexture.name = "Baked Texture";
    28.         }
    29.  
    30.         bakeTexture();
    31.  
    32.         if (ReplaceMaterial != null)
    33.         {
    34.             GetComponent<Renderer>().material = ReplaceMaterial;
    35.             ReplaceMaterial.mainTexture = ResultTexture;
    36.         }
    37.     }
    38.  
    39.     void bakeTexture()
    40.     {
    41.         var renderer = GetComponent<Renderer>();
    42.         var material = Instantiate(renderer.material);
    43.         Graphics.Blit(material.mainTexture, ResultTexture, material);
    44.  
    45.         Texture2D frame = new Texture2D( ResultTexture.width, ResultTexture.height );
    46.         frame.ReadPixels( new Rect( 0, 0, ResultTexture.width, ResultTexture.height ), 0, 0, false );
    47.         frame.Apply();
    48.         byte[] bytes = frame.EncodeToPNG();
    49.         FileStream file = File.Open( @"C:\Works.png", FileMode.Create );
    50.         BinaryWriter binary = new BinaryWriter( file );
    51.         binary.Write( bytes );
    52.         file.Close();
    53.     }
    54. }
    55.  
    @mholub have you got any idea?
    thanks in advance
     
  9. mholub

    mholub

    Joined:
    Oct 3, 2012
    Posts:
    123
    You are using Standard shader. It doesn't bake for me either. I don't know the cause.

    My ideas:
    1. Standard shader defines a lot of passes. Default Graphics.Blit call without 4-th param uses all passes in sequence. It can mess the result. You probably need to get pass index for "forward base" pass and use only it.
    2. You use shader which depends on lighting. Graphics.Blit doesn't have lighting information, so it can result in black appearance.

    I will probably debug it on weekend cause I am interested why does it not work.

    What is the end result you try to achieve BTW? Why do you want to bake decals and albedo into one texture? What's the point? The whole reason people use decals is to have detailed high-frequency texture over low-frequency base texture to hide it's low resolution.
     
  10. ghiboz

    ghiboz

    Joined:
    Sep 7, 2012
    Posts:
    465
    thanks @mholub, I have some different shaders that create a car texture, that may be have more than one texture, and to optimize I want create 'offline' the final texture and ingame use directly the baked texture, to reduce the gpu work
     
  11. ghiboz

    ghiboz

    Joined:
    Sep 7, 2012
    Posts:
    465
    hi @mholub have you tried something?
     
  12. BigDill

    BigDill

    Joined:
    Jan 1, 2013
    Posts:
    31
    Was there ever a solution found?
     
  13. Eknoes

    Eknoes

    Joined:
    Sep 15, 2015
    Posts:
    56
    Have the same problem. Anyone found a solution now?
     
  14. PixelSpartan

    PixelSpartan

    Joined:
    May 11, 2015
    Posts:
    26
    Pretty sure its not working because its not unlit. Might wanna edit the shader to make it unlit or instead of diffuse output to emissive
     
    twobob likes this.
  15. Voxel-Busters

    Voxel-Busters

    Joined:
    Feb 25, 2015
    Posts:
    1,966
    If you face any problem in future regarding Graphics.Blit and custom Material, here is what you can do.

    There is an issue with Surface Shaders and Graphics.Blit. Try converting to Cg shader and it should be fine.
    I request Unity team to look into the reason behind the issue. [@Aras]

    Thanks,
    VB Team
     
  16. yty

    yty

    Joined:
    Aug 10, 2009
    Posts:
    82
    Is there any progress in this issue?
    I want to use the alpha fade of the Surface Shaders and the shadow.
     
  17. ecv80

    ecv80

    Joined:
    Oct 1, 2017
    Posts:
    28
    Isn't it logical that surface shaders won't work with blit? After all blit input is just a RenderTexture and the shaders won't have access to lighting setup or specific geometry... so only fragment shaders would work?
    Please do correct me if wrong. Thanks.
     
    Last edited: Apr 17, 2019
  18. ImOneIvan

    ImOneIvan

    Joined:
    Feb 11, 2022
    Posts:
    2
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.IO;
    4.  
    5. public class PNGUploader : MonoBehaviour
    6. {
    7.     public CustomRenderTexture rTex;
    8.     // Save immediately
    9.     IEnumerator Start()
    10.     {
    11.         yield return UploadPNG();
    12.     }
    13.  
    14.     IEnumerator UploadPNG()
    15.     {
    16.         yield return new WaitForEndOfFrame();
    17.  
    18.         RenderTexture.active = rTex;
    19.         Texture2D tex = new Texture2D(rTex.width, rTex.height, TextureFormat.RGBA4444, false);
    20.        
    21.         tex.ReadPixels(new Rect(0, 0, rTex.width, rTex.height), 0, 0);
    22.         tex.Apply();
    23.  
    24.         byte[] bytes = tex.EncodeToPNG();
    25.        
    26.         File.WriteAllBytes(Application.dataPath + "/../SavedTexture.png", bytes);
    27.     }
    28. }
     
  19. BionicWombatGames

    BionicWombatGames

    Joined:
    Oct 5, 2020
    Posts:
    33