Search Unity

Changing Texture Filter Mode in a Sampler

Discussion in 'Shaders' started by ferretnt, Jun 15, 2017.

  1. ferretnt

    ferretnt

    Joined:
    Apr 10, 2012
    Posts:
    412
    I have a specific texture that needs to be both point filtered and bilinear filtered in two different texture reads. It's also massive (4k-sq, on mobile) and must be RGBA32 uncompressed.

    The only way currently to do this is to have two complete copies of the texture loaded and set different filter modes on the texture, which is a bit of a disaster :)

    Is there a way to override filter mode on the sampler state in a Shaderlab shader? For bonus points, if I'm targeting a fairly wide range of ios/android hardware (but it has to be capable of rendering Unity's standard shader with occlusion and normal maps, so not totally ghetto hardware) is this likely to work in practice even if Unity supports it?
     
  2. larku

    larku

    Joined:
    Mar 14, 2013
    Posts:
    1,422
    By 4k-sq are you saying the texture is 4096x4096 ?

    You may find there's still android hardware that can't support over 2048x2048, I'd be cautious going over this.
     
  3. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,348
    You can define the sampling state directly in the shader, but I think only in DX11, not mobile. However you can just do some basic UV manipulation in the shader to get the same results as point sampling.

    sampler2D _MyTexture;
    float4 _MyTexture_TexelSize;

    ...

    fixed4 linear = tex2D(_MyTexture, uv);

    float2 pointUV = (floor(uv * _MyTexture_TexelSize.zw) + 0.5) * _MyTexture_TexelSize.xy;
    fixed4 point = tex2Dlod(_MyTexture, float4(pointUV, 0, 0));
     
  4. ferretnt

    ferretnt

    Joined:
    Apr 10, 2012
    Posts:
    412
    This is true - I probably should have said "on iOS." On android there's a QualitySettings adjustment done during a boot scene to throw the top mipmap away, so they're only 2k-square. Occasionally I consider doing a glGet(MAX_TEXTURE_SIZE) and letting some android devices have 4k-sq textures, but I don't know if I trust what the driver reports anyway.
     
    larku likes this.
  5. ferretnt

    ferretnt

    Joined:
    Apr 10, 2012
    Posts:
    412
    Awesome, thanks @bgolus! That works great.

    Do you know how well tex2dLoD is supported on Android hardware. Like I said, it has to be capable of rendering full screens of standard shader, so not complete garbage devices, but I still barely trust droids to support rendertextures in 2017, nevermind any feature that became mainstream after 1998...
     
  6. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,348
    If it's OpenGL ES 3.0 capable hardware, it has support. Otherwise it's not great as the ES 2.0 spec only requires tex2Dlod in the vertex shader. If the texture doesn't have mip-maps then you can obviously skip it all together and just use tex2D. However Unity's fallback has been to use tex2Dbias which is supported on all hardware in the fragment shader.

    float2 pointUV = (floor(uv * _MyTexture_TexelSize.zw) + 0.5) * _MyTexture_TexelSize.xy;
    #if defined(SHADER_API_GLES) // is compilation target gles 2.0
    fixed4 point = tex2Dbias(_MyTexture, float4(pointUV, 0, -11)); // bias for keeping largest mip for a 2048x2048 texture
    #else
    fixed4 point = tex2Dlod(_MyTexture, float4(pointUV, 0, 0));
    #endif


    You could just use bias on all platforms, Unity does for light cookies. Also note that doing this kind of in-fragment shader UV manipulation will significantly slow down the shader on those old ES 2.0 platforms ... as will the fact it's an uncompressed 2048 texture, but presumably you're not too worried about framerate, just compatibility.
     
    Knosis_Co-Verse and ferretnt like this.