How can I get the exact color value in the output of ReadPixels?

What I am doing is screenshotting a quad whose attached material creates a texture. That texture has Perlin noise values stored in the red channel. To test how accurate the values are, I am setting the blue value outputting from the fragment shader to 0.65.

In my ShaderLab code…

mainColor.b = 0.65;

In script, I am getting 0.6509804 in my Debug.Log. Here is my script:

using UnityEngine;
using UnityEngine.Experimental.Rendering;

public class PixelReader : MonoBehaviour
{
    Material material;
    Camera perlinCamera;
    RenderTexture renderTexture;
    Texture2D blankTexture, texture;
    public Color[] GetNoise(int chunkX, int chunkY, int chunkSize)
    {
        if (perlinCamera == null)
        {
            perlinCamera = GetComponent<Camera>();
            material = GetComponentInChildren<MeshRenderer>().materials[0];
            renderTexture = new RenderTexture(chunkSize, chunkSize, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
            blankTexture = new Texture2D(chunkSize, chunkSize, TextureFormat.RGBA32, false, false);
            texture = new Texture2D(chunkSize, chunkSize, TextureFormat.RGBA32, false, false);
        }

        RenderTexture.active = renderTexture;
        perlinCamera.targetTexture = renderTexture;

        // Set up shader variables
        material.SetTexture("_MainTex", blankTexture);
        material.SetFloat("_ChunkX", chunkX);
        material.SetFloat("_ChunkY", chunkY);
        material.SetFloat("_ChunkSize", chunkSize);

        perlinCamera.Render();
        texture.ReadPixels(new Rect(0, 0, chunkSize, chunkSize), 0, 0);
        texture.Apply();

        byte[] bytes = texture.EncodeToPNG();
        System.IO.File.WriteAllBytes("C:/Users/vmc4/Downloads/TextureExport.png", bytes);

        RenderTexture.active = null;
        perlinCamera.targetTexture = null;

        //chunkQuad.SetActive(false);
        return texture.GetPixels();
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetKey(KeyCode.Space))
        {
            Debug.Log(GetNoise(8, 1, 16)[0].b);
        }
    }
}

How can I get the output to be exact?

I have tried using different color space settings. It seems that setting the RenderTexture to linear color space made the output more exact. The value was way off before because it was in gamma-corrected color space.

I have read online it could be a post-processing setting but that didn’t seem to do the trick when I changed them. Perhaps I missed one or some other graphics setting. Any help would be appreciated.

mainColor.b is a byte. If you try to store .65 in a byte (which is an int between 0 and 255), so to store .65 you have to rescale it from floating point 0-1 to an integer scale from 0 to 255. (0.65*255 =165.75) which is impossible to store in a byte, so in mainColor.b I guess it’s rounded to 166. Then when you rescale it back to a float (166/255) you get… 0.65098039… So, there’s nothing unusual about what you are seeing. That’s just expected.

You might be able to use a 16 bit color format for more precision, but there’s no possibility of getting precise decimal values from color channels

3 Likes

A wise man once observed,

“Think of [floating point] as JPEG of numbers.” - orionsyndrome on the Unity3D Forums

And everything kdgalla says is right on point… you have eight bits of precision, most likely, as a Color is interchangeable with a Color32, which is a 4-byte struct.

Color() accepts channel params from 0.0f to 1.0f

Color32() accepts channel params from 0 to 255

More on Color vs Color32, lossless, comparisons, etc.

https://discussions.unity.com/t/891296/2

1 Like

Appreciate the help!