Search Unity

What's the name of the effect that has been applied to these texures?

Discussion in 'General Graphics' started by Davidblackberry1, Sep 3, 2018.

  1. Davidblackberry1

    Davidblackberry1

    Joined:
    Sep 8, 2017
    Posts:
    76
    Hello. So I was snooping around in the assets of a game called Voxel Turf and found that its textures were stored in an atlas as I had thought, but there appears to be some strange padding around each texture, and I'm wondering what exactly they have done to achieve this effect.

    https://imgur.com/a/siosrON
     
    Last edited: Sep 3, 2018
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    11,191
    Looks like they took each texture and stretched the outer most edge pixels to fill the space. Not entirely sure why they choose that particular solution to fill the padding, using XNormal’s photoshop plugin dilation filter would have been easier and been better for visuals. Better yet would have been repeating the texture.
     
  3. Davidblackberry1

    Davidblackberry1

    Joined:
    Sep 8, 2017
    Posts:
    76
    @bgolus

    I just assumed that they had found some clever way to do it. So since I'm dealing with pixel textures, would it be easiest to just go ahead and take a minute to make padding myself?

    https://imgur.com/a/xPW2Hpl

    The first part of the picture is a 16x16 brick texture that I made, the second part is the padding, and the third part is the texture plus the padding, which is 20x20 in size. As you can see, I just took all the edge pixels on every side and extended them out by two pixels.
     
    Last edited: Sep 3, 2018
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    11,191
    Yep, that's more akin to what your usual dilation filter will do. It's better than what they did for handling texture filtering on the edges. As I said before, the best option is to repeat the texture. If the intent is for the texture to look like it's tiling, then your best option is for it to actually be tiling in the atlas' padding too. Make a 2x2 grid of your texture, then offset it by 1/2 the tile size, done.

    I also wonder if Photoshop's content aware fill would work here...
     
    Davidblackberry1 likes this.
  5. Davidblackberry1

    Davidblackberry1

    Joined:
    Sep 8, 2017
    Posts:
    76
    Oh man yeah, I can see how great repeating would be for brick and grass and stuff, but what about textures for a furnace or workbench or anything that needs to look a specific way?
     
  6. Davidblackberry1

    Davidblackberry1

    Joined:
    Sep 8, 2017
    Posts:
    76
    @bgolus

    I've followed your advice and tiled as well as inset each texture in my atlas by eight pixels on every side, but the padding doesn't seem to be working at long range and is even going so far into the texture beside it, that it is penetrating into two more textures beside that one as well. I simply don't understand how something like this can be happening.

    https://imgur.com/a/IRgffty
     
  7. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    11,191
    Here’s the thing. The padding does two things. Mainly it helps prevent obvious seams at the edge of the texture, especially when using bilinear or anisotropic filtering, and it helps delay, but not prevent, issues stemming from mip maps. The easiest solution is to disable mip maps for your texture, but that's really ugly.

    In custom engines (ie: Minecraft) the atlas texture's mip chain is clamped so that the smallest mip still has at least 1 pixel per tile, maybe even 4 pixels per tile if they're using texture compression, rather than the usual which is for the smallest mip to be a 1x1 texture for the entire atlas. Unity doesn’t offer any control over the number of mips in each texture and always builds a full chain, so clamping the mip level has to be solved within the shader.

    There are several threads on the topic of proper atlas handling here on the forums. But you can search for tex2Dlod and/or tex2Dgrad to help find them.
     
    Davidblackberry1 likes this.
  8. Davidblackberry1

    Davidblackberry1

    Joined:
    Sep 8, 2017
    Posts:
    76
    That's disappointing. I wonder why Unity doesn't offer support for controlling the number of mipmaps in a texture...
     
  9. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    11,191
    Because some platforms (and potentially texture formats?) don't support it, and will refuse to display the texture without a full chain.
     
  10. Davidblackberry1

    Davidblackberry1

    Joined:
    Sep 8, 2017
    Posts:
    76
    Refuse to display? doesn't that mean that even if I try to do it my own way with a custom shader it still won't work?
     
  11. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    11,191
    Doing it in a shader is fine. That's how you have to do it in Unity if you're going to use an atlas. That or ignore it and don't do anything, which some people do too.

    If the texture itself has mip maps enabled, but doesn't have a full mip chain, some platforms will simply not allow the texture to be loaded. The result would be an error and either a corrupted texture displayed, or possibly your application not rendering at all. For a long time Unity took the stance that if a feature didn't work on all platforms, it wouldn't offer it at all. That's been relaxed pretty heavily, but using a partial mip map chain is a fairly uncommon thing. I also don't know if any modern systems that Unity supports has this limitation anymore, OpenGL has had support for it since OpenGL 2.0 (including ES), and Direct3D has had support since 10. Direct3D 9 didn't support it, any they only just killed support for that at the end of last year in 2017.3. But, it's likely not been a high priority to add, and I doubt many people have asked for it if anyone.

    Again, fixing it in the shader works just fine, even if it's a little extra work.
     
    Davidblackberry1 likes this.
  12. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    11,191
    Because shaders run on GPUs, and GPUs don't run C#, they run shader code which is written in HLSL or some other shader language.
     
  13. Davidblackberry1

    Davidblackberry1

    Joined:
    Sep 8, 2017
    Posts:
    76
    This post says that the only way to have a custom Mipmap is to use a prebuilt one in the form of a DDS file.
     
  14. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    11,191
    But you don't want custom mipmaps. The mipmaps Unity generates already have the correct colors for what you want. You just want to clamp the max mip level, which you have to do in a custom shader.

    Custom mipmaps are for when you want the contents of the mipmaps to be different than that of the full resolution texture. Like if you want each mip to be a different color (often used for mipmap visualization) or otherwise change their content for some kind of effect. Super Mario Sunshine's water is a prime example of creative use of custom mipmaps, but one only relevant in a world of fixed screen resolutions.

    Also, as far as I know, importing in a DDS with partial chain of precomputed mipmaps just results in the smallest mipmaps beyond what's in the DDS file being black. That's not what you want either.
     
  15. Davidblackberry1

    Davidblackberry1

    Joined:
    Sep 8, 2017
    Posts:
    76
    What exactly is it that I'm clamping? I searched for a node that has mip or mipmap in its name, and nothing came up.
    EDIT: I'm starting to think that Sample Texture 2D LOD might be what I'm looking for.
     
    Last edited: Sep 5, 2018
  16. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    11,191
    The mip level, or mip LOD. (It's the same thing, just different people refer to it differently.) If you're using the new Shader Graph to make shaders for the LW or HD pipelines, you'll have to use the Sample Texture 2D LOD node. It's fairly easy to calculate the appropriate LOD for a texture in HLSL or GLSL, but Shader Graph hides some of the values that make it easy so you have to bodge around that.

    upload_2018-9-5_13-56-20.png
    That's a recreation of the mip map level calculation that GPUs use as detailed here, along with calculating the smallest mip from the texture resolution and atlas tile dimensions value (how many tiles across, not the individual tile size). Make special note of the AtlasTexture_TexelSize property and its reference name. That's something that Unity automatically sets when used as a material, but which the Shader Graph preview doesn't handle properly. The XY values are 1 / texture resolution, and ZW are the texture resolution.
     
  17. Davidblackberry1

    Davidblackberry1

    Joined:
    Sep 8, 2017
    Posts:
    76
    I copied all of that into my shader graph, but it doesn't even seem to care what I set any of the input values to, and it still doesn't use Mipmaps.
    EDIT: I deleted the UV and LOD connections to the Sample Texture2D LOD node, and it doesn't make a difference.
     
    Last edited: Sep 5, 2018
  18. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    11,191
    What do you mean by "doesn't use Mipmaps"? Did you turn off mipmaps on your atlas texture asset?
     
  19. Davidblackberry1

    Davidblackberry1

    Joined:
    Sep 8, 2017
    Posts:
    76
    No, Mipmapping is enabled on the texture. Mipmaps work fine with Standard LWP shader, but they've been completely disabled with any custom graph shader I've tried to use.
     
  20. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    11,191
    If you use a Sample Texture 2D node it should look exactly like using the built in shader. If you use a Sample Texture 2D LOD with nothing connected to the LOD input, it will indeed disable mip mapping at that node requires you supply the mipmap level yourself. The graph I posted should have done that, along with clapped the mip level to prevent it from getting too small.

    It's possible I messed up somewhere in my clamped mip level calculation.

    If you take the maximum of the two dot products and pipe that directly into the log node, it should go back to behaving almost exactly like the normal Sample Texture 2D node.
     
  21. Davidblackberry1

    Davidblackberry1

    Joined:
    Sep 8, 2017
    Posts:
    76
    Did you try it for yourself? I don't know why Mipmapping if the LOD has something being put into it. I tried putting in a constant number like 5 and the stone texture became completely grey, while not giving in a LOD has the same effect as giving it the current LOD that requires a bunch of nodes to work out.
     
  22. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    11,191
    Yes. That's the expected result. You're telling it to render mip level 5. For a 2048x2048 texture asset, mip level 5 is a 64x64 texture.
     
  23. Davidblackberry1

    Davidblackberry1

    Joined:
    Sep 8, 2017
    Posts:
    76
    Yeah, but I was making a point that the LOD is indeed making a difference, therefore it is being used, yet Mipmapping is still not functioning.
     
  24. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    11,191
    Because I made a mistake in my math. Try this instead:
    upload_2018-9-6_11-8-29.png
     
    Davidblackberry1 likes this.
  25. Davidblackberry1

    Davidblackberry1

    Joined:
    Sep 8, 2017
    Posts:
    76
    Are you sure that ATlasTexture should be set to Exposed? It is causing everything to become grey. Also, messing with all the input values still doesn't seem to have any effect on the output and Mipmapping is still disabled.
     
  26. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    11,191
    If atlas texture isn't exposed, how else would you set the texture?
     
  27. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    11,191
    All that shader does is limit the mip map level used by the texture. As long as on your material you're setting the texture, number of tiles across in the atlas, and the min tile size (4 is a good option), it should be good to go. If using the built in shader is showing the correct texture on each face, that's all that should be needed.

    The only thing I can think of is that you're not using the primary UV set on the mesh, but if the built in shader "works" (apart from the color shift in the distance) I have no idea what's wrong.

    I tested this by making a giant voxel grid UV'd to a single tile. On the left is the shader above. On the right is the built in shader.
    upload_2018-9-6_14-40-48.png
     
    Davidblackberry1 likes this.
  28. Davidblackberry1

    Davidblackberry1

    Joined:
    Sep 8, 2017
    Posts:
    76
    Changing the variables around doesn't have any effect on the final outcome, it still just looks like the default shader. I tried multiplying the z and w of AtlasTexture_TexelSize by 100,000, and nothing changed.

    EDIT: What is the primary UV set?

    EDIT: I even changed B of the Multiply node that directly connects to the Sample Texture 2D LOD node to 0, and then to 1,000,000, and nothing changed. I'm guessing that it A could be 0 in every case.

    EDIT: Yup, A is always 0 for some reason. Was I supposed to do something outside of the shader to stop this from happening?
     
    Last edited: Sep 6, 2018
  29. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    11,191
    The primary UV set are the uvs modified by mesh.uvs or mesh.SetUVs(0, uvs). It's also what the built in shader uses.


    The property using a reference value of _AtlasTexture_TexelSize should have the Z and W values be the size of the texture held in the property using a reference value of _AtlasTexture. Those two need to match for this to work, otherwise you'll need to expose the TexelSize value and set those values manually.

    The texture resolution is being used to determine what the smallest mip to clamp to. If you have a 2048x2048 texture, a grid of 16x16 tiles, and a min tile size of 4, it'll clamp the mip level to 5. To understand what that means, select your texture asset in the editor. In the inspector there's a preview of the texture and a little slider in the top left of that preview.

    upload_2018-9-6_15-7-58.png
    Here I have the slider showing mip 5.​

    upload_2018-9-6_15-9-26.png
    And mip 6.​

    At mip 5 each tile is 4x4 pixels (that's what the min tile size is selecting). At mip 6 each tile is 2x2, and compression is starting to blur the colors. At mip 7 each tile is 1x1 pixel, and most of the color will be completely wrong unless you're not using compression, and even then the bilinear filtering will produce undesirable results. Beyond that each tile gets less than a pixel, that is multiple tiles are simply averaged together as the image now is smaller than 16x16. This is the source of the discoloration you're seeing when using an atlas, and the whole reason we're jumping through these hoops.

    upload_2018-9-6_15-24-1.png

    If you multiply the texture resolution by 100,000, the calculated clamped mip level will be ~24. A 2048x2048 texture only has 11 mip levels, so it'll effectively be unclamped meaning it'll look exactly like the default shader. Multiply it by a smaller number may have some effect, depending on the values you're setting elsewhere.


    As I explained above, part of the shader graph is just to calculate the same mip level that not using Sample Texture 2D LOD would produce. That's the "bottom" row of nodes that the UVs are connected to. Normally the GPU does all of those calculations on it's own behind the scenes. Effectively the way it works is it figures out how many texels per screen pixel are being displayed, and if it's more than 1 texel on either the vertical or horizontal, it drops down to the next mip level. However, if the texture resolution isn't properly set, that too won't work and you may get a texture that is overly blurry, or never mipmaps at all.
     
    Davidblackberry1 likes this.
  30. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    11,191
    Here's the shader I have which works for me.
     

    Attached Files:

    Davidblackberry1 likes this.
  31. Davidblackberry1

    Davidblackberry1

    Joined:
    Sep 8, 2017
    Posts:
    76
    Heh, I was just going to ask for it, I just realized when I select the shader in the inspector, it says there is like eight errors. They look like this:
    implicit truncation of vector type
    Compiling Vertex program with INSTANCING_ON _ADDITIONAL_LIGHTS _VERTEX_LIGHTS _SHADOWS_ENABLED _SHADOWS_CASCADE
    Platform defines: UNITY_ENABLE_REFLECTION_BUFFERS UNITY_USE_DITHER_MASK_FOR_ALPHABLENDED_SHADOWS UNITY_PBS_USE_BRDF1 UNITY_SPECCUBE_BOX_PROJECTION UNITY_SPECCUBE_BLENDING UNITY_ENABLE_DETAIL_NORMALMAP SHADER_API_DESKTOP UNITY_COLORSPACE_GAMMA UNITY_LIGHT_PROBE_PROXY_VOLUME UNITY_LIGHTMAP_FULL_HDR
     
  32. Davidblackberry1

    Davidblackberry1

    Joined:
    Sep 8, 2017
    Posts:
    76
    Yeah, your's works fine. I think I found the issue. The first two Combine nodes on your shader only took in two parameters, while on mine they took in four. I realized that in the beginning but couldn't figure out how to only make them take in two, so I just assumed that we were on slightly different versions and not putting in two more parameters would result in the Combine node just using only the ones I put in. But looking a little bit closer, it's actually just using zero for the other two parameters, hence why the final result ended up being zero I guess.
     
  33. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    11,191
    All shader graphs do this right now. It can be ignored. It's on Unity to fix their own code that's causing this error as it's part of the generated shader and not something in the graph.
     
  34. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    11,191
    I'm collapsing my nodes which hides unused inputs and outputs. That may be causing some of the confusion.
     
  35. Davidblackberry1

    Davidblackberry1

    Joined:
    Sep 8, 2017
    Posts:
    76
    Alright, I just found the little button that does that. Also, I found the actual problem by comparing the graphs side by side. The left top Split node was giving the Combine node to the right of it B and A, instead of R and G.
     
    Last edited: Sep 7, 2018
  36. Davidblackberry1

    Davidblackberry1

    Joined:
    Sep 8, 2017
    Posts:
    76
    Mipmapping is working fine now with your shader, thanks for all your help.
     
  37. Jenzo83

    Jenzo83

    Joined:
    Oct 22, 2013
    Posts:
    33
    Hi! I would like some help! I downloaded your shader and I get this log:
    redefinition of '_AtlasTexture_TexelSize'

    Using 2019.4.28
    URP 7.6.0

    I don't know how to fix that with my current knownledge of Shader Graph.(I only use Amplify Shader.)
    Please help!

    Best regards
    Jenz
     
  38. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    11,191
    Since posting this, Unity added support for getting the texture size with a node instead of using the hack I was having to use in the original graph. The short version is change the start of the graph to look like this:
    upload_2021-7-5_11-26-52.png
    Then delete the AtlasTexture_TextureSize property from the blackboard, as well as those two now orphaned nodes.
     
unityunity