Search Unity

texture2D Array mipmap troubles

Discussion in 'General Graphics' started by imaewyn, Jul 18, 2016.

  1. imaewyn

    imaewyn

    Joined:
    Apr 23, 2016
    Posts:
    211
    Hey. I've used atlases for my own terrain texturing in my project. And I've had troubles when mip map was enabled. After that Unity 5.4 came with textre2Darrays. So I rewrote code for arrays, but mipmap working like for atlases...

    this is atlas without mips

    Texture2DArray with autogenerated mip levels

    Texture2DArray , mipBias = 3

    how can I fix those artefacts?
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    Looks like a shader mipmap issue rather than a texture mipmap generation issue.

    In your shader I assume you've got some code wrapping the UVs for use with an atlas that should no longer be needed with an array since they'll tile without that hack.

    For future reference you can fix that artifact even when using an atlas using tex2Dgrad instead of tex2D doing something like this:

    float2 scaled_uv = i.uv * scale;
    float2 wrapped_uv = frac(scaled_uv) * atlas_scale + atlas_offset;
    fixed4 col = tex2Dgrad(_Texture, wrapped_uv, ddx(scaled_uv), ddy(scaled_uv));


    The reason for why this is happening, and why the above fixes the issue, is the GPU calculates the mipmap level for a texture by how much the UVs change between pixels (specifically within each 2x2 block of pixels), and if you've got some code that wraps the UVs that means in one pixel the UV might be "0.99" and the next will be "0.01" which is a big jump vs "0.99" and "1.01" so it thinks it should use a smaller mip. The tex2Dgrad is telling it to base the mip map size off of UVs with "0.99" to "1.01", but sample the texture with the "0.99" to "0.01".
     
    Last edited: Jul 18, 2016
    lustgunther likes this.
  3. imaewyn

    imaewyn

    Joined:
    Apr 23, 2016
    Posts:
    211
    Thanks for answer. Before my first message I fixed code after atlas deleting.
    Now I'm getting result color without atlas wrapping and I don't use text2D method...

    my code:

    float3 uvForLanArr = float3(uvoffset.x, uvoffset.y, type);
    float4 result_tex = UNITY_SAMPLE_TEX2DARRAY(_LandArr, uvForLanArr);


    type - is array depth where I want to get texel
    uvoffset - yes, I calculate some offset for each point but this offset depends of distance from neighbor tile (this terrain have hex geometry but I use quad masks above this) and not depends of position in atlas...Earlier I've used my own method UVMultiplayer() and there I calculated atlas offset. But after adding texture2DArray I cleaned code from this.
    Real hex grid + texture quad mask ratio f. e.

    So, UNITY_SAMPLE_TEX2DARRAY... may be I need something instead this...like UNITY_SAMPLE_TEX2DGRADARRAY ? Not sure that it exists
     
    Last edited: Jul 18, 2016
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    You should only need to use tex2Dgrad if you were still using an atlas. It should not be necessary with the array. You'd have to post the full shader code for me to know exactly what's wrong, but my assumption is if you were to just use the texture coordinates unmodified that the issue will go away. Can you try that? I know that's not what you'll want / need, but it will mean you're doing something you shouldn't in how you're handling the calculation of uvoffset.
     
  5. imaewyn

    imaewyn

    Joined:
    Apr 23, 2016
    Posts:
    211
    It's hard to explain what's happaning in UVoffset because I get offset from early prepared texture. That's depends of hex position relatively neighbor hexes.
    Code (CSharp):
    1.    
    2. //This method is call 3 times for each of the possible neighboring hexes, in which could exist this point
    3. float2 UVoffset(float direction, float2 localPos, float2 hexCenter)
    4. {
    5.             float4 DD = tex2D(_DirDecode, float2((direction + 0.05) / 10, 0.1f)) * 2;
    6.             hexCenter.x *= round(DD[0] * (DD[2] - 1));
    7.             hexCenter.y *= round(DD[1] * (DD[3] - 1));
    8.             return localPos + hexCenter;
    9. }
    10.  

    But I really using atlas offset in UVmultiplayer method
    Code (CSharp):
    1.  
    2. float2 UVmultiplayer(float n, float2 localPos)
    3. {
    4.             int y_InAtlas = int(n / _AtlasCount);
    5.             int x_InAtlas = n - (y_InAtlas * _AtlasCount);
    6.             float x_Coords = x_InAtlas / _AtlasCount;
    7.             float y_Coords = y_InAtlas / _AtlasCount;
    8.             return float2((localPos.x / _AtlasCount) + x_Coords, (localPos.y / _AtlasCount) + y_Coords);
    9. }
    10.  

    And I have two code rows in main surface method

    1. float4 result_tex = tex2D(_Atlas, UVmultiplayer(type, uvoffset)).rgba; -
    that's uncommented when using atlas

    2. float3 uvForLanArr = float3(uvoffset.x, uvoffset.y, type);
    float4 result_tex = UNITY_SAMPLE_TEX2DARRAY(_LandArr, uvForLanArr); -

    that's uncommented when usingtext2Darray

    Nothing else. I don't change anything else for chose read texture method
    looks like a magic
     
    Last edited: Jul 19, 2016
  6. imaewyn

    imaewyn

    Joined:
    Apr 23, 2016
    Posts:
    211
    There is two method's
    UNITY_SAMPLE_TEX2DARRAY - first (troubles here)
    UNITY_SAMPLE_TEX2DARRAY_LOD - second. I've tried each mipmap level and they are looking good...
    So, maybe a need to choose mipmap level by hands? How?
     
  7. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    You really need the UNITY_SAMPLE_TEX2DARRAY_GRAD, which Unity doesn't define but you could define yourself. The UNITY_SAMPLE_TEX2DARRAY and similar things are macros that redirect to different function calls depending on the platform being compiled to.

    try

    #if defined(SHADER_API_D3D11) || defined(SHADER_API_XBOXONE) || defined(SHADER_API_GLES3) || defined(SHADER_API_GLCORE)
    #define UNITY_SAMPLE_TEX2DARRAY_GRAD(tex,coord,dx,dy) tex.SampleGrad (sampler##tex,coord,dx,dy)
    #else
    #if defined(UNITY_COMPILER_HLSL2GLSL) || defined(SHADER_TARGET_SURFACE_ANALYSIS)
    #define UNITY_SAMPLE_TEX2DARRAY_GRAD(tex,coord,dx,dy) tex2DArray(tex,coord,dx,dy)
    #endif
    #endif
     
    Am_Un, twhittaker and lustgunther like this.
  8. imaewyn

    imaewyn

    Joined:
    Apr 23, 2016
    Posts:
    211
    Thanks. Should I use this code after that?

    float3 uvForLanArr = float3(myUV.x, myUV.y, type);
    float4 result_tex = UNITY_SAMPLE_TEX2DARRAY_GRAD(_LandArr, uvForLanArr, ddx(myUV), ddy(myUV));


    Now, I'm trying to compile this and have error:
    Shader error in 'Custom/HexTerrain': unable to find compatible overloaded function "texCUBE(samplerCUBE, float3, float2, float2)" at line 183 (on d3d9)

    So, maybe I need to add some code to the macro, or change some preferences...
     
  9. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    Oh, yeah I didn't bother with the dx9 version of the macro. You can add:

    #pragma exclude_renderers d3d9

    or maybe

    #pragma only_renderers d3d11
     
    lustgunther likes this.
  10. imaewyn

    imaewyn

    Joined:
    Apr 23, 2016
    Posts:
    211
    I've tried this strings, but compiler stil show error...
    Also, I've tried insert something like this to the macro

    #else
    #if defined(SHADER_API_D3D9)
    #define float4(0, 0, 0, 0)
    #endif

    Here the same error code like before
     
    Last edited: Jul 31, 2016
  11. imaewyn

    imaewyn

    Joined:
    Apr 23, 2016
    Posts:
    211
    Ok, I've change macro code to this:
    #if defined(SHADER_API_D3D11) || defined(SHADER_API_XBOXONE) || defined(SHADER_API_GLES3) || defined(SHADER_API_GLCORE)
    #define UNITY_SAMPLE_TEX2DARRAY_GRAD(tex,coord,dx,dy) tex.SampleGrad (sampler##tex,coord,dx,dy)
    #else
    #if defined(UNITY_COMPILER_HLSL2GLSL) || defined(SHADER_TARGET_SURFACE_ANALYSIS)
    #define UNITY_SAMPLE_TEX2DARRAY_GRAD(tex,coord,dx,dy) float4(0,0,0,0) //tex2DArray(tex,coord,dx,dy)
    #endif
    #endif


    but now result not too different from previous

    Maybe I should change dx and dy calculaton?
    float3 uvForLanArr = float3(myUV.x, myUV.y, type);
    float4 result_tex = UNITY_SAMPLE_TEX2DARRAY_GRAD(_LandArr, uvForLanArr, ddx(myUV), ddy(myUV));
     
    twhittaker likes this.
  12. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    As I said before the problem is caused specifically by the UVs you're using, and passing the ddx and ddy of those same UVs makes it no different than using the non-GRAD version of the macro.

    Try something like ddx(localPos.xy), though I don't know exactly where that value is coming from, or worse case try just this:

    UNITY_SAMPLE_TEX2DARRAY_GRAD(_LandArr, uvForLanArr, float2(0.05, 0.05), float2(0.05, 0.05));

    That won't be really usable as the textures will become a little blurry, but the lines should go away. Ultimately this method isn't that much different from using the _LOD macro, so it's not that useful.

    So lets try this:

    UNITY_SAMPLE_TEX2DARRAY_GRAD(_LandArr, uvForLanArr, ddx(localPos.xy), ddy(localPos.xy));

    This might also make the textures a little too blurry, or too sharp, I still don't know exactly what you're doing to the UVs since you only sent me a small snippet of your shader code, but they'll have some semblance of mipmapping and the lines should go away. If you want to just get it working try applying a scale factor to localPos until it starts to look right to you.
     
    Last edited: Jul 31, 2016
    Eclectus and lustgunther like this.
  13. imaewyn

    imaewyn

    Joined:
    Apr 23, 2016
    Posts:
    211
    I know this is old theme, but today I've got another one problem with arrays)))
    So far I've used UNITY_SAMPLE_TEX2DARRAY_GRAD(_LandArr, uvForLanArr, ddx(localPos.xy), ddy(localPos.xy)) for my textures and UNITY_SAMPLE_TEX2DARRAY_LOD(_MaskArr, uvForMask, 0).r for B&W masks. Both of these strings are located near. Recently I'm discovered that if I delete second string (with UNITY_SAMPLE_TEX2DARRAY_LOD) then my first string stop works. It gives last one-colored LOD instead full texture as before.
    Code (CSharp):
    1.  
    2. float4 tex = UNITY_SAMPLE_TEX2DARRAY_GRAD(_LandArr, uvForLanArr, ddx(localPos.xy), ddy(localPos.xy));
    3. UNITY_SAMPLE_TEX2DARRAY_LOD(_MaskArr, uvForMask, 0); //even without saving to variable!!!1111
    4. //even if this string will be higher than previous...
    5. o.Albedo = tex.rgb;
    6.  
    it works

    Code (CSharp):
    1.  
    2. float4 tex = UNITY_SAMPLE_TEX2DARRAY_GRAD(_LandArr, uvForLanArr, ddx(localPos.xy), ddy(localPos.xy));
    3.                                                                
    4. o.Albedo = tex.rgb;
    5.  
    It not works...

    How it possible?)) Can't understand....(((
    Can it depends of something in this initialization:
    Code (CSharp):
    1.         #if defined(SHADER_API_D3D11) || defined(SHADER_API_XBOXONE) || defined(SHADER_API_GLES3) || defined(SHADER_API_GLCORE)
    2.         #define UNITY_SAMPLE_TEX2DARRAY_GRAD(tex,coord,dx,dy) tex.SampleGrad (sampler##tex,coord,dx,dy)
    3.         #else
    4.         #if defined(UNITY_COMPILER_HLSL2GLSL) || defined(SHADER_TARGET_SURFACE_ANALYSIS)
    5.         #define UNITY_SAMPLE_TEX2DARRAY_GRAD(tex,coord,dx,dy)  float4(1,1,1,1)//tex2DArray(tex,coord,dx,dy)
    6.  
    7.         #endif
    8.         #endif
    9.  
    10.         UNITY_DECLARE_TEX2DARRAY(_LandArr);
    11.         UNITY_DECLARE_TEX2DARRAY(_MaskArr);
     
    Last edited: Dec 1, 2016
  14. TheElumenati

    TheElumenati

    Joined:
    Jan 22, 2014
    Posts:
    38
    Thank you bgolus. This totally worked for me.
     
  15. smash-ter

    smash-ter

    Joined:
    Sep 6, 2020
    Posts:
    22
    would this macro work for anything besides the 2D array?