Search Unity

Limiting the amount of mipmap levels

Discussion in 'General Graphics' started by TheOnePoly, Mar 25, 2019.

  1. TheOnePoly

    TheOnePoly

    Joined:
    Dec 24, 2016
    Posts:
    2
    Is their any way to limit the amount of mipmap levels a texture has? I can't find anyway to do it.
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    There's no way to do it from the texture settings, no. Unity always provides the GPU a full chain of mips if mip maps are enabled. You can import in a premade dds or pvrtc file with a limited mip chain, but this will crash Unity!

    The only solution is to limit the mip level manually in the shader by using tex2Dlod and calculating the mip level and clamping it, or using tex2Dgrad and limiting the derivatives.
     
  3. richardkettlewell

    richardkettlewell

    Unity Technologies

    Joined:
    Sep 9, 2015
    Posts:
    2,285
    FYIs we are adding this feature, I think it's shipping in 2019.2.
     
    Shushustorm, hungrybelome and bgolus like this.
  4. hungrybelome

    hungrybelome

    Joined:
    Dec 31, 2014
    Posts:
    336
    Hi, I saw this in the release notes for 2019.2.0:
    Graphics: Added support for partial mipmap chains in texture classes

    How can I set the mipmap limit? I don't see any new options in the Texture inspector window.
     
  5. Prodigga

    Prodigga

    Joined:
    Apr 13, 2011
    Posts:
    1,123
    Bump, I don't see this either @richardkettlewell - did we end up getting a public API for this? :)
     
    hungrybelome likes this.
  6. hungrybelome

    hungrybelome

    Joined:
    Dec 31, 2014
    Posts:
    336
    Bump. If this isn't possible at the moment, despite the 2019.2 changelog, it would at least be good to know!
     
  7. richardkettlewell

    richardkettlewell

    Unity Technologies

    Joined:
    Sep 9, 2015
    Posts:
    2,285
    I asked @vandreev about this. He is going to post an answer soon. Sorry for the delay.
     
    MaxPirat, Prodigga and hungrybelome like this.
  8. hungrybelome

    hungrybelome

    Joined:
    Dec 31, 2014
    Posts:
    336
  9. vandreev

    vandreev

    Unity Technologies

    Joined:
    Mar 2, 2016
    Posts:
    6
    Heya folks, sorry about the delay. All texture constructors (Texture2D, Cubemap, RenderTexture, etc) now have an overload that takes an explicit mip count. For instance,
    new Texture2D(width:256, height:256, textureFormat:TextureFormat.ARGB32, mipCount:3, linear:true)
    will create a 256x256 texture with only 3 mip levels instead of 8.

    There's a special mip count constant, Texture.GenerateAllMips, which basically means "set the mipCount to the maximum mip count for this texture size", and, finally, a mip count field in the RenderTextureDescriptor, along with the appropriate constructor overloads.
     
    GamerTobbe, hungrybelome and bgolus like this.
  10. Prodigga

    Prodigga

    Joined:
    Apr 13, 2011
    Posts:
    1,123
    Is there a way try limit the lightmap count after the texture is created? We have a asset post processor for lightmaps, and we'd like the limit the number of mipmaps for lightmaps to prevent bleeding.
     
  11. hungrybelome

    hungrybelome

    Joined:
    Dec 31, 2014
    Posts:
    336
    Awesome, thanks for the info! Is there a way I can save a Texture2D created with the mip count constructor to disk? I only know of the File.Write/Texture2D.EncodeToPNG way which writes mip0 to disk, resulting in the texture importer creating the max mip count. Like a lot of people needing an explicit mip count, I'm trying to save a generated texture atlas to disk.
     
    Prodigga likes this.
  12. cholleme

    cholleme

    Unity Technologies

    Joined:
    Apr 23, 2019
    Posts:
    31
    You can just register it with the asset database to save it to disc

    Code (CSharp):
    1.        
    2.         Texture2D limitedMips = new Texture2D(1024, 1024, TextureFormat.ARGB32, 3, true);
    3.         // ... fill it ...
    4.         AssetDatabase.CreateAsset(limitedMips, "Assets/LimitedMips.asset");
    5.         AssetDatabase.SaveAssets();
    6.  
    This will not be an image file but directly create a unity texture asset you can assign to shaders etc.
     
    JamesArndt likes this.
  13. Prodigga

    Prodigga

    Joined:
    Apr 13, 2011
    Posts:
    1,123
    And while we have you here, what about lightmaps? The textures are already created and the only way I can edit the texture is via the importer settings? I can also alter the texture 2D via a post processor but you can't seem to change the mipmap count on an already created texture2d.
     
  14. MaxPirat

    MaxPirat

    Joined:
    Sep 7, 2015
    Posts:
    12
    I don't get it, Is it possible to change the mipCount for the imported texture? Kinda like it's possible to change mipMapBias via TextureImporter?
     
    Haozq likes this.
  15. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    Not currently, no. Only for textures created from script, which can be generated from imported textures, but will have to be new assets. You could conceivably do this as a custom asset processor, but you'd still get two assets, the original imported one with full mips, and script generated one with limited mips.
     
    MaxPirat likes this.
  16. cAyouMontreal

    cAyouMontreal

    Joined:
    Jun 30, 2011
    Posts:
    315
    But you will lose the compression in the process @bgolus....I want to use it in texture atlases, to avoid texture bleeding on high mipmap levels, but it defeats the purpose of saving memory if I can't enable compression...Except if there is a way to compress the generated texture?
     
  17. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
  18. cAyouMontreal

    cAyouMontreal

    Joined:
    Jun 30, 2011
    Posts:
    315
    I tried first to use Texture2D.Compress() but it generates more mipmaps...And they are worse than the original texture...Will try EditorUtility.CompressTexture.
     
  19. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    Texture2D.Compress()
    is intended for real time compression. Basically for content you're importing at runtime on PC. The
    EditorUtility.CompressTexture()
    should have much better quality, but it may also still blow away your mipmap settings. If it does, then yeah, time to complain to Unity!

    If that's still broken you could work around that by compressing each mip individually and using CopyTexture to copy the results into the "real" one. But oy vey that's a massive pain. I'm with you in that I was hoping it'd be exposed on the importer's advanced settings.
     
  20. cAyouMontreal

    cAyouMontreal

    Joined:
    Jun 30, 2011
    Posts:
    315
    I agree, that should be just put into texture settings...I will try to compress them individually, I fear nothing!
     
  21. cAyouMontreal

    cAyouMontreal

    Joined:
    Jun 30, 2011
    Posts:
    315
    When I try to compress using EditorUtility.CompressTexture Unity crashes with many formats, including DXT1.
    Here's the output stack:
    Code (CSharp):
    1. 0x00007FF708D3EA85 (Unity) prcore::RemapFullPixelShift_RGB888_ARGB8888
    2. 0x00007FF708D3BE62 (Unity) prcore::BlitImageRemapNoScale
    3. 0x00007FF708D3B95E (Unity) prcore::BlitImage
    4. 0x00007FF707BAF5CC (Unity) CompressSingleImageTexture
    5. 0x00007FF707BB018C (Unity) CompressTextureWithMultipleImages
    6. 0x00007FF708A229AF (Unity) EditorUtility_CUSTOM_CompressTexture
    7. 0x000001F1ACF6A210 (Mono JIT Code) (wrapper managed-to-native) UnityEditor.EditorUtility:CompressTexture (UnityEngine.Texture2D,UnityEngine.TextureFormat,int)
    8. 0x000001F1ACF6A093 (Mono JIT Code) UnityEditor.EditorUtility:CompressTexture (UnityEngine.Texture2D,UnityEngine.TextureFormat,UnityEditor.TextureCompressionQuality)
    any idea why?
     
  22. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    Is that when using a limited mip range, or always? I can totally imagine it's just straight up broken for the limited mip case.
     
  23. cAyouMontreal

    cAyouMontreal

    Joined:
    Jun 30, 2011
    Posts:
    315
    Oh, good point! Only when I want less mipmaps, it crashes...If I use the same amount as the original one, everything is fine...I guess you're right, it's not meant to do that.
     
  24. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    So, yeah. This means you have to construct a Texture2D with no mips for each mip level, copy the data from the original texture into it. Compress that. Copy each mip into a new Texture2D created with the wanted compressed format and limited mip count.
     
  25. hungrybelome

    hungrybelome

    Joined:
    Dec 31, 2014
    Posts:
    336
    Here's the method I've been using for creating limited mip maps:

    Code (CSharp):
    1.         public static Texture2D CreateLimitedMipmapsTexture(Texture2D sourceTexture, TextureFormat format, int mipmapCount, bool isLinear, int anisoLevel, FilterMode filterMode)
    2.         {
    3.             var texturePath = AssetDatabase.GetAssetPath(sourceTexture);
    4.          
    5.             var limitedMips = new Texture2D(sourceTexture.width, sourceTexture.width, format, mipmapCount, isLinear);
    6.             limitedMips.anisoLevel = anisoLevel;
    7.             limitedMips.filterMode = filterMode;
    8.          
    9.             for (var i = 0; i < mipmapCount; i++)
    10.             {
    11.                 Graphics.CopyTexture(sourceTexture, 0, i, limitedMips, 0, i);
    12.             }
    13.          
    14.             var outputPath = $"{texturePath.Replace($"{sourceTexture.name}.png", $"{sourceTexture.name}__LimitedMipCount-{mipmapCount}.asset")}";
    15.          
    16.             AssetDatabase.CreateAsset(limitedMips, outputPath);
    17.             AssetDatabase.SaveAssets();
    18.          
    19.             return AssetDatabase.LoadAssetAtPath<Texture2D>(outputPath);
    20.         }
    21.  
    It requires the source texture to already be compressed. It also expects a .png, but you can probably edit the method to extract the file extension, if needed.
     
    Last edited: May 2, 2020
  26. cAyouMontreal

    cAyouMontreal

    Joined:
    Jun 30, 2011
    Posts:
    315
    @hungrybelome looks like it doesn't work for me, at least on the texture preview, there is still like 11 mipmaps level, and all the level above the one I set on the asset creation are corrupted.
    Edit: nevermind, it worked once I propertly opened my eyes and fixed my stuff....
    So the thing is:
    - precompress the texture
    - copy each mip you want into a new texture
    - save it

    Clever!
     
    NEMO-CMQ likes this.
  27. Waffle1434

    Waffle1434

    Joined:
    Jun 1, 2016
    Posts:
    5
    I have a shader based solution for this. My goal was to dynamically limit the highest resolution mip level that could be used. This would essentially be the same as limiting the maximum resolution of the texture.
    Code (HLSL):
    1. // min is a 2d vector that limits the mip in x and y axes. (1,1) will be the lowest resolution mip, (0,0) is no limit and will use highest res mip
    2. inline fixed4 tex2DMinMip(sampler2D tex, float2 uv, float2 min) {
    3.     float2 uv_dx = ddx(uv);
    4.     float2 uv_dy = ddy(uv);
    5.  
    6.     if (length(uv_dx) < min.x) uv_dx = normalize(uv_dx)*min.x;
    7.     if (length(uv_dy) < min.y) uv_dy = normalize(uv_dy)*min.y;
    8.  
    9.     return tex2D(tex, uv, uv_dx, uv_dy);
    10. }
    11.  
    12. // example:
    13. sampler2D _MainTex;
    14. float4 _MainTex_TexelSize;
    15. // in fragment shader:
    16. fixed4 col = tex2DMinMip(_MainTex, IN.uv_MainTex, 8*_MainTex_TexelSize.xy);
    17. // 8 is 2^n where n is desired mip level (3rd highest resolution mip in this example)
    18.  
     
    MaxPirat likes this.
  28. Riptide559

    Riptide559

    Joined:
    Feb 1, 2020
    Posts:
    9
    Thank you for this! I just ran into the "Texture Atlas doesn't play nice with mipmaps" issue after adding a few more tile textures to my atlas. I was getting worried it was going to be a difficult problem to solve - but hoped that something simple like this was out there.

    I added this functionality at runtime along with PackTextures() to create my Texture Atlas.

    It works perfectly - thank you!
     
  29. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,363
    What about rendertexture?
    I switched to 2,3,4 with texture mipcount at 3 like your example and it's not being updated by CopyTexture.
    blit works though
    upload_2020-12-20_16-38-36.png
     
  30. guycalledfrank

    guycalledfrank

    Joined:
    May 13, 2013
    Posts:
    1,672
    Wait, what? According to the docs, you can only use a single bool mipChain, not an int specifying the mip count.

    But... it compiles, at least in 2019.3 (and apparently newer). But doesn't work in 5.6. Hmmm.

    I wonder why does it has an "exclude from docs" tag in the reference code o_O
    https://github.com/Unity-Technologi...db8ea/Runtime/Export/Graphics/Texture.cs#L678
     
  31. joxdev_pavel_glagolev

    joxdev_pavel_glagolev

    Joined:
    Feb 4, 2020
    Posts:
    8
    Is there any chance to apply crunch compression to that texture? (With ".asset" extension).
     
  32. atomicjoe

    atomicjoe

    Joined:
    Apr 10, 2013
    Posts:
    1,869
    The Texture2D.Reinitialize method is lacking the explicit mip count, and it's a problem because there is currently no way to change the mip count of an already created texture.
    It would be great if we could have an explicit mip count in Texture2D.Reinitialize.
     
  33. temurleng1

    temurleng1

    Joined:
    Jun 28, 2018
    Posts:
    17
  34. NEMO-CMQ

    NEMO-CMQ

    Joined:
    Mar 5, 2020
    Posts:
    1
    Thank you! It works!
     
  35. kripto289

    kripto289

    Joined:
    Feb 21, 2013
    Posts:
    505
    Also, RTHandle missed mip levels property.
     
  36. fokozuynen

    fokozuynen

    Joined:
    Aug 29, 2021
    Posts:
    4
  37. kripto289

    kripto289

    Joined:
    Feb 21, 2013
    Posts:
    505
    I render water with full mip chain (for refraction/volumetric lighting, caustic, etc), but for reflection and normals I need to use my own mipmap limits. Global texture limits can brake most of the other effects (such as third-party fog rendering, FSR/DLSS, etc, etc). Any global limits must be removed, because it leads to undefined behavior.
     
  38. Lo-renzo

    Lo-renzo

    Joined:
    Apr 8, 2018
    Posts:
    1,514
    @bgolus once helped me with clamping mip map levels in shader over in this thread here with a very detailed answer. If anyone wants additional reading on the subject, it may help.