Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Filter Mode: Point and Linear filter on a texture based on the distance.

Discussion in 'General Graphics' started by SebGames, Mar 24, 2017.

  1. SebGames

    SebGames

    Joined:
    Dec 9, 2014
    Posts:
    42
    I'm looking to have my texture filters as "Point" when it is closer and "Bilinear" when it is farther. Several years ago, when programming in DirectX, we had the option to specify the texture filters like this.

    SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR)
    SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT)

    That was useful to control the texture filter depending on the scale factor. Unity does not seem to allow this. Probably because that feature was exclusive to DirectX and Unity tries to be more generic to port to all platform.

    Is there is a way to achieve the same texture filter effect in Unity?
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    You can use something like the RetroAA asset which is a special shader that modifies the texture UVs to keep the "pixel" look of texture assets during magnification, but also gives you free anti-aliasing with the MIN/MAG filter states don't get you.

    If you're experienced with shaders you could write a similar shader on your own. The basic version is test if fwidth(uv) > texelSize and floor the UVs to per-texel center positions to simulate point sampling. The more complex, RetroAA style is to floor the UVs, but keep ~1 pixel of linear filtering at the texel "edges", basically doing saturate(frac(texeluv) / fwidth(texeluv)), but with a few extra steps and some careful use of +/- 0.5.
     
    SebGames likes this.
  3. SebGames

    SebGames

    Joined:
    Dec 9, 2014
    Posts:
    42
    Thanks bgolus. Your solutions are interesting. The RetroAA could be a solution. The screenshot seems to match what I want. I see that RetroAA uses shadesr to achieve the result. I'm wondering how it will react with other graphic plugins. I will probably buy it.

    Concerning writing shaders, the only experience I have is a little experimentation with ShaderForge. I will probably use custom shaders for my game. I might buy RetroAA and see if I can modify the shaders with ShaderForge in the worst case scenario to adapt other needs. At least, there's hope.
     
  4. Lost-in-the-Garden

    Lost-in-the-Garden

    Joined:
    Nov 18, 2015
    Posts:
    176
    This is one of the main pain points I have with unity. Proper texture filtering is so 90's, I still don't understand why there is no support for it.

    Anyways, this is how we solved it: It goes along the lines of what bgolus mentioned and works quite ok for us. Anisotropy is the tricky part here, and making it blend nicely. (the code is a bit of a mess, haven't had the time yet to refactor everything nicely)

    Code (CSharp):
    1. float4 crispMipMapTex2D(sampler2D tex, float2 uv, float4 texelSize)
    2. {
    3.     float2 dx = ddx(uv);
    4.     float2 dy = ddy(uv);
    5.  
    6.     // approximate texel size in texture
    7.     // this is not correct since the derivatives are float2
    8.     //float lod = max(dx * texelSize.zw.x, dy *  texelSize.zw.x);
    9.     float lod = sqrt(pow(dx * texelSize.zw.x, 2) + pow(dy *  texelSize.zw.x, 2));
    10.  
    11.     float t = mapAndClamp(0.5, 1, 0, 1, lod);
    12.     return lerp(pointSampleTex2D(tex, uv, texelSize), tex2D(tex, uv, dx, dy), t);
    13.     //return (lod > 1) ? (tex2D(tex, uv, dx, dy)) : pointSampleTex2D(tex, uv, texelSize);
    14. }
    15.  
    16. float4 pointSampleTex2D(sampler2D tex, float2 uv, float4 st)
    17. {
    18.     float2 snappedUV = ((float2)((int2)(uv * st.zw + float2(1, 1))) - float2(0.5, 0.5)) * st.xy;
    19.     return tex2Dlod(tex, float4(snappedUV.x, snappedUV.y, 0, 0));
    20. }
    21.  
    22. float map(float a, float b, float r, float s, float value)
    23. {
    24.     if (a == b)
    25.     {
    26.         if (value <= a)
    27.         {
    28.             return r;
    29.         }
    30.         else
    31.         {
    32.             return s;
    33.         }
    34.     }
    35.  
    36.     float ratio = (value - a) / (b - a);
    37.     return r + (s - r) * ratio;
    38. }
    39.  
    40. float mapAndClamp(float a, float b, float s, float t, float value)
    41. {
    42.     return clamp(map(a, b, s, t, value), s, t);
    43. }
     
    Invertex likes this.
  5. SebGames

    SebGames

    Joined:
    Dec 9, 2014
    Posts:
    42
    Thanks "Lost-in-the-Garden" for sharing your solution. Writing shaders manually is still complicated because I don't have experience directly in that area. However, I started using the plugin Shader Forge recently and here's my solution below. It works, but there's a glitch in the final picture. I included a few screenshots and details of my experimentation. If anyone has an idea why it does that, let me know. It works almost perfectly.

    The only problem now is the extra pixels ("noise") added between the "retro" pixels. See the screenshot to get more information. Please, download it or look at it in full resolution because it is small. The colors match the colors of the contrasting pixels nearby.

    - I use a low-resolution texture with bilinear filtering.

    - I use the UV coordinates and do mathematical operations to round the UV to the center of the pixels. I don't have a lot of experience with Shader Forge, but that seems to provide the solution.

    - The noise appears everywhere. They are mostly visible when there is a contrast nearby. In my case, the road lines. I also added colors to the test texture to understand the behavior.

    - I do not use Anti-Aliasing in the test.

    - I saw different kinds of artifacts when changing the "Anisotropic Textures" (in the Quality Settings) between "Forced On" and "Per Texture".

    - Strangely, the noise appears only on the left-right axis. It does not appear above or under the pixels. I also tried changing the camera angle and I also inverted the U and V coordinates and the noise is still on the left-right axis.

    - I tried an orthographic camera with perpendicular angles and the noise disappeared. However, if I modify the angle, the noise reappears. Notice that with the perspective camera (on the screenshot attached) that the more distant from the center of the image, the more noise there is.

    This is my best analysis so far. The symptoms are strange. Does anyone here have an idea?

    shader-forge-retro-shader-nodes.png


    retro-shader-bug-01.png
     
    Alvarezmd90 likes this.