4
\$\begingroup\$

I want to write the information from my shader into a texture and save it in a png.

I found this:

http://forum-old.unity3d.com/threads/save-texture-generated-by-a-shader.326561/

and this

System.IO.File.WriteAllBytes(Application.dataPath + "/"+"picture5886.png", texture2.EncodeToPNG());    //app path n1! 

But currently it seems to be unsolved.

Lets say I do Graphics.Blit(src,temp,mat) into a "tempTexture". I would have the temp texture then somewhere in my world, right?. How can I save that into a png?

\$\endgroup\$
7
\$\begingroup\$

First you need to get your shader's output into a Texture2D, so create one of the appropriate size. You'll commonly want ARGB32 format and no mipmaps:

Texture2D outputTex = new Texture2D(width, height, TextureFormat.ARGB32, false)

If the content you want to save has already been rendered by a camera in the scene, you can skip ahead (although in this case Unity's screenshot features may be a shorter route to your goal if you don't need anything fancy)

If you want to use an off-screen buffer instead, we'll need to create a RenderTexture to write it into.

RenderTexture buffer = new RenderTexture(
                               width, 
                               height, 
                               0,                            // No depth/stencil buffer
                               RenderTextureFormat.ARGB32,   // Standard colour format
                               RenderTextureReadWrite.Linear // No sRGB conversions
                           );

(Assuming you don't need a depth/stencil buffer and don't want to work in sRGB - otherwise, use the parameters that are right for your use case)

Now we bind our RenderTexture and material/shader pass, and run our shader across every pixel.

Graphics.Blit(sourceTexture, buffer, material, passIndex);

If your shader doesn't require texture input, you might need to supply a dummy texture for sourceTexture - I haven't tested how it handles null there. ;)

Now we need to copy the data out of the current render target (whether it was the buffer we created above, or one used internally by a scene camera) and into our outputTex:

RenderTexture.active = buffer;           // If not using a scene camera
outputTex.ReadPixels(
          new Rect(0, 0, width, height), // Capture the whole texture
          0, 0,                          // Write starting at the top-left texel
          false                          // No mipmaps
);

Now you can use outputTex.EncodeToPNG() and write the results to a file as described in the question. You should use persistentDataPath rather than dataPath if this is something you're doing at runtime though - dataPath is not guaranteed to be a writeable location in a release build.

\$\endgroup\$
0
0
\$\begingroup\$

copy this:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SaveShaderTexture : MonoBehaviour
{
    public int TextureLength =1024;

    public Texture2D texture;
    public void Save()
    {
        RenderTexture buffer = new RenderTexture(
                               TextureLength, 
                               TextureLength, 
                               0,                            // No depth/stencil buffer
                               RenderTextureFormat.ARGB32,   // Standard colour format
 RenderTexture buffer = new RenderTexture(
                               TextureLength, 
                               TextureLength, 
                               0,                            // No depth/stencil buffer
                               RenderTextureFormat.ARGB32,   // Standard colour format
                               RenderTextureReadWrite.sRGB // No sRGB conversions
                           );

        texture = new Texture2D(TextureLength,TextureLength,TextureFormat.ARGB32,true);

        MeshRenderer render = GetComponent<MeshRenderer>();
        //texture = render.sharedMaterial.GetTexture("_MainTex") as Texture2D;
        Material material = render.sharedMaterial;

        Graphics.Blit(null, buffer, material);
        RenderTexture.active = buffer;           // If not using a scene camera

        texture.ReadPixels(
          new Rect(0, 0, TextureLength, TextureLength), // Capture the whole texture
          0, 0,                          // Write starting at the top-left texel
          false);                          // No mipmaps

        System.IO.File.WriteAllBytes(Application.dataPath + "/"+"SkinLut.png", texture.EncodeToPNG()); 
        // texture.Save();

    }

    // Start is called before the first frame update
    void Start()
    {
        // Save();
    }

    // Update is called once per frame
    void Update()
    {
        // Save();
        if(Input.GetKeyDown(KeyCode.Space))
        {
            Save();
        }
    }
}
\$\endgroup\$
2
  • \$\begingroup\$ Welcome to Game Development Stack Exchange. Thank you for providing this solution. But it would be even better if you would explain a bit about it so people can learn from it. How exactly does it work? Are there any possible drawbacks or limitations people should be aware of when they use your solution? And is "copy this" really all the instructions people need to make use of this code? \$\endgroup\$ – Philipp May 29 '20 at 11:30
  • \$\begingroup\$ I also notice that your code is declaring RenderTexture buffer twice. I assume that's a mistake? Which of the two versions is the one you would recommend? \$\endgroup\$ – Philipp May 29 '20 at 11:32

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.