- Home /
Why does tex3d return a different value than tex2d?
I'm hoping someone can shed some light on how texture sampling works with Texture3D and tex3d. I am seeing different results when sampling from a 2D texture then when sampling from a similar 3D texture.
As a simple test case, I create a 3D texture in code, and have a shader that maps a slice of that 3D texture to a 2D quad. The texture is 8x8x8, and is gradient (or step) from black to white in the x/u direction. So for a given value of x/u, all values of y/v and z/w are the same.
When sampling in the shader, I map the u and v from the vertex positions as you would with any 2D texture, and set the w to a fixed value. At the same time as I create the 3D texture, I also create a 2D texture, using the same pixel values as the 3D texture for one slice in the z-direction, when z = 0.
Both textures have point filtering and clamp wrap mode.
Why are the colors brighter on the 3D version?
Here is the code to create the textures:
for(int k = 0; k < texDim; k++)
{
for(int j = 0; j < texDim; j++)
{
for(int i = 0; i < texDim; i++)
{
byte colorByte = (byte)(255 * i / (float)(texDim - 1));
pixels[i + (j * texDim) + (k * texDim * texDim)].r = colorByte;
pixels[i + (j * texDim) + (k * texDim * texDim)].g = colorByte;
pixels[i + (j * texDim) + (k * texDim * texDim)].b = colorByte;
pixels[i + (j * texDim) + (k * texDim * texDim)].a = 1;
if(k == 0)
{
pixels2d[i + (j * texDim)].r = colorByte;
pixels2d[i + (j * texDim)].g = colorByte;
pixels2d[i + (j * texDim)].b = colorByte;
pixels2d[i + (j * texDim)].a = 1;
}
}
}
}
tex3d.SetPixels32(pixels);
tex3d.wrapMode = TextureWrapMode.Clamp;
tex3d.filterMode = FilterMode.Point;
tex3d.Apply();
tex2d.SetPixels32(pixels2d);
tex2d.wrapMode = TextureWrapMode.Clamp;
tex2d.filterMode = FilterMode.Point;
tex2d.Apply();
Here is the 3d shader code:
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
sampler3D _Tex3d;
float _Slice;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
// sample the texture
float3 uvw = float3(i.uv.x, i.uv.y, _Slice);
fixed4 col = tex3D(_Tex3d, uvw);
return col;
}
And 2D shader code:
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
v2f vert(appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
fixed4 frag(v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
return col;
}
Can you show the code where you instantiate the textures? They are using the same TextureFormat, right?
Yes, same texture format:
tex3d = new Texture3D(texDim, texDim, texDim, TextureFormat.ARGB32, false);
Color32[] pixels = new Color32[texDim * texDim * texDim];
tex2d = new Texture2D(texDim, texDim, TextureFormat.ARGB32, false);
Color32[] pixels2d = new Color32[texDim * texDim];
No idea then... It looks like that code should produce identical results.
Answer by Dante_CyberdeckGames · Apr 12, 2017 at 07:43 PM
Texture2D has sRGB gamma correction by default: https://docs.unity3d.com/ScriptReference/Texture2D-ctor.html
Texture3D does not have an option to choose which color space you use. I believe it is always linear.
Try this code for Texture2D, which makes it linear color space instead of sRGB. Then they should be the same.
tex3d = new Texture3D(texDim, texDim, texDim, TextureFormat.ARGB32, false);
Color32[] pixels = new Color32[texDim * texDim * texDim];
// The final parameter bool is linear color space (true)
tex2d = new Texture2D(texDim, texDim, TextureFormat.ARGB32, false, true);
Color32[] pixels2d = new Color32[texDim * texDim];
Exactly, problem solved. Thanks very much for taking the time to reply.
Answer by mbzdmvp · Apr 13, 2017 at 09:48 AM
I know you mentioned they have they both have point filtering, but I would double check the filterMode property on the textures, it could be changing itself?
No, it was linear vs gamma colour space as described in the answer above.