Search Unity

Calculate used mipmap level, ddx, ddy?

Discussion in 'Shaders' started by Nims, Jul 4, 2014.

  1. Nims

    Nims

    Joined:
    Nov 11, 2013
    Posts:
    86
    I am looking into a way to calculate the mipmap level used in my fragment program.
    There are 2 objectives to this:
    1. To enable different shader implementations depending on the mipmap level used.
    2. To create a shader which displays the different mipmaps used in a color coded fashion.

    So far the most promising pieces of information i have found regarding this issue are:
    http://stackoverflow.com/questions/...-mipmap-level-in-glsl-fragment-shader-texture
    http://hugi.scene.org/online/coding/hugi 14 - comipmap.htm

    The code would be something like this:

    Code (Cg):
    1. float mip_map_level(in float2 texture_coordinate) // in texel units
    2. {
    3.   float2  dx_vtc  = ddx(texture_coordinate);
    4.   float2  dy_vtc  = ddy(texture_coordinate);
    5.   float delta_max_sqr = max(dot(dx_vtc, dx_vtc), dot(dy_vtc, dy_vtc));
    6.   return 0.5 * log2(delta_max_sqr);
    7. }
    The problem is this code requires the the texture coordinates to be "UN-normalized" texture coordinate.
    So I have a few question regarding the suggested code:
    1. How would I UN-normalize the UV coordinates to Texel coordinates?
    2. Could this be done using ddx and ddy values from the normalized UV texture coordinates alone?
    3. Why do I need the results for ddx and ddy to be 2 dimension variables? (If all I need is the derivative/change along the U or V coordinate - wouldn't a regular float suffice?)
    4. What kind of values (Codomain) do I get from ddx and ddy when applying these functions to the normalized UV texture coordinates? - Would these be something like 0, 1, 1/2, 1/4 ,1/8......1/4096?
    Regarding question 1 - The only way I see this being done is actually supplying the texture texel size into the shader as a uniform?
     
    Last edited: Jul 4, 2014
  2. metaleap

    metaleap

    Joined:
    Oct 3, 2012
    Posts:
    589


    As you said supply the texture size as a uniform, ie. 2048, then mult with normalized uv so 1 yields 2048, 0.5 yields 1024, 0 yields 0 etc.

    Probably not. Calculation of screenspace gradient and mip level necessarily depends on knowing the mip-0 texture size.


    You can see a dot product needs to be performed with the vector results from ddx and ddy, right?
     
  3. Daniel_Brauer

    Daniel_Brauer

    Unity Technologies

    Joined:
    Aug 11, 2006
    Posts:
    3,355
    Unity automatically populates float4 TextureName_TexelSize with (1/width, 1/height, width, height) if you declare it.
     
    AlexTuduran likes this.
  4. Nims

    Nims

    Joined:
    Nov 11, 2013
    Posts:
    86
    While I totaly agree with that - I am still wondering what kind of values (Codomain) do I get from ddx and ddy when applying these functions to the normalized UV texture coordinates..

    This I don't understand - we have 2 functions ddx and ddy. ddx for supplying us with the value of change along the U axis and ddy supplying us with value of change along the V axis hence i would assume that dx_vtc.y and dy_vtc.x are always zero.


    I have never seen that before...is TextureName_TexelSize a uniform that unity supplies automatically? Do I need to declare it? Anycase thats a good one to know...Is there an updated list of all the uniforms unity does supply?
     
  5. Daniel_Brauer

    Daniel_Brauer

    Unity Technologies

    Joined:
    Aug 11, 2006
    Posts:
    3,355
    It is undocumented. You only need to declare it, and Unity will populate it for you. There is no up-to-date list of all of Unity's supplied uniforms. That would be nice, along with thorough documentation of the lighting pipeline and how each uniform is used. A man can dream.
     
    AlexTuduran likes this.
  6. Nims

    Nims

    Joined:
    Nov 11, 2013
    Posts:
    86
  7. Patapom

    Patapom

    Joined:
    Aug 19, 2009
    Posts:
    155
  8. ChowJayYe

    ChowJayYe

    Joined:
    Jun 20, 2017
    Posts:
    3
    Because of mathematics,u,v is un-normalize, u=u(x,y),v=v(x,y). (x,y)->(u1,v1), (x+1, y)->(u2,v2), ddx(u1v1) = (u2-u1, v2-v1), set (u2-u1,v2-v1) is (deltau, deltav), deltau= partial derivatives of u(x,y) function, deltav = partial derivatives of v(x,y) function.so, 1 x direction screen pixel = sqrt(deltau * deltau + deltav * deltav) texel pixel, this is x direction. set sqrt(deltau * deltau + deltav * deltav) = k, when k = 1 -> 1 screen pixel = 1 texel pixel, log2(k) = 0, this level 0, when k = 2, 1 screen pixel = 2 pixel, log2(k) = 1, and so on. set log2(k) = q, 2^q = k -> 2^q * 2^q = k * k -> 2^(2q) = k*k
    -> log2(k*k) = 2q -> q = 0.5 * log2(k*k) -> q = 0.5 * log2(deltau * deltau + deltav * deltav). then y direction is same, we choose the largest level in x, y direction, so......that's all
     
  9. ErdTod

    ErdTod

    Joined:
    Jan 7, 2018
    Posts:
    11
    I, unfortunately, had a stroke reading that, but I would very want to understand what do to get miplevel and color-code it through a fragment shader in Unity...
     
    atomicjoe likes this.
  10. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    Code (csharp):
    1. float mipLevel = mip_map_level(i.uv * _MainTex_TexelSize.zw);
    That’s calling the function from the first post, with the UVs multiplied by the texture’s resolution. That’s it.

    One key thing is for the
    _MainTex_TexelSize
    to work you have to define it in the shader code (just put
    float4 _MainTex_TexelSize;
    someplace outside of a function, but between the
    CGPROGRAM
    and
    ENDCG
    ), and you must be using
    _MainTex
    still. Specifically the texture color needs to be used in the output color, otherwise it’ll be optimized out of the shader, and then the texel size won’t be set. Alternatively you could just have a material property which you manually specify the texture resolution.
     
  11. atomicjoe

    atomicjoe

    Joined:
    Apr 10, 2013
    Posts:
    1,869
    For future google searches, this is the method to calculate the mipmap level:
    HLSL code:
    Code (CSharp):
    1. float mip_map_level(in float2 texture_coordinate) // texture_coordinate = uv_MainTex * _MainTex_TexelSize.zw
    2. {
    3.    float2 dx_vtc = ddx(texture_coordinate);
    4.    float2 dy_vtc = ddy(texture_coordinate);
    5.    float md = max(dot(dx_vtc, dx_vtc), dot(dy_vtc, dy_vtc));
    6.    return 0.5 * log2(md);
    7. }
     
    Last edited: May 9, 2022
    Omaek, HackerOfDreams and lang_fox like this.
  12. atomicjoe

    atomicjoe

    Joined:
    Apr 10, 2013
    Posts:
    1,869
    - there was a function here that has been deleted as it wasn't great in the end :p -
     
    Last edited: May 9, 2022
  13. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    The code might be very slightly faster, but it can result in slower overall rendering compared to the reference implementation due to always using a higher mip level, and thus putting more pressure on the memory bandwidth.

    Though it should be noted … literally no common GPU on the planet actually uses the reference implementation exactly as it’s written. AMD and Nvidia have used various approximations or optimizations over the years, the exact specifications of which are a secret. Also interesting some Mali GPUs use something like your suggested method! But if you’re trying to match the exact behavior of a specific GPU’s hardware mip mapping calculations, you basically won’t be able to without access to confidential documents or a lot of experimentation to reverse engineer what is being done. And you’ll need to do that for every generation of GPU because I’ve found they change every so often.

    Also, on at least recent AMD GPUs, if you’re using this to feed a
    tex2Dlod()
    or similar sample function, that’s significantly slower than using
    tex2Dgrad()
    and passing in the derivatives, even when using 16x anisotropic filtering. Similarly, 16x anisotropic filtering on the last 5 years of Nvidia GPUs is nearly free, and using
    tex2()
    with 16x anisotropic filtering is faster than
    tex2Dlod()
    regardless of the mip level implementation! And
    tex2Dgrad()
    is always slower than that, though not by a ton.

    It can be very non-obvious what is “faster” when it comes to texture sampling on GPUs.
     
    leozzyzheng2, Invertex and atomicjoe like this.
  14. atomicjoe

    atomicjoe

    Joined:
    Apr 10, 2013
    Posts:
    1,869
    Yeah, I actually made that function to have sharper textures on slanted surfaces while being able to use tex2Dlod because I really NEED to be able to specify the mip map level manually since I'm doing realtime projection mapping and it generates ugly seams when the UVs change radically form a pixel to the next.

    The faster calculation was just a bonus.
     
  15. atomicjoe

    atomicjoe

    Joined:
    Apr 10, 2013
    Posts:
    1,869
    You're right!
    In the end it's just better overall to find the difference between the current mip map level and your target mip map level using the vanilla method above and then use that difference as a bias with tex2dbias.

    I'll go ahead and delete that second method to avoid any confusion to anyone in the future.
     
  16. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    It's still a useful option for some use cases, it's just not "faster".

    The bias technique is a decent one too, though again, it's impossible to perfectly replicate the mip level any particular GPU is going to produce from the derivatives. So this too is an appropriation.

    I generally try to always use tex2Dgrad unless the performance implications are especially bad (as they can be on some mobile devices).
     
  17. fukaken5050

    fukaken5050

    Joined:
    Feb 3, 2016
    Posts:
    4
    I knew this technique on seeing POMShader of ATI.
    I have a question to ask you.
    FilterMode of texture does not seem to work as expected any place other than point.
    Do you know the method to change as expected in Bilinear and Trilinear?
     
  18. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    wat?
     
  19. ChowJayYe

    ChowJayYe

    Joined:
    Jun 20, 2017
    Posts:
    3
    ??????? The code is what they say. I just explain how this code is derived
     
  20. ChowJayYe

    ChowJayYe

    Joined:
    Jun 20, 2017
    Posts:
    3
    This is a mathematical problem in essence. Don't worry about it too much. It's a little old, and you don't care about it now. Ha ha ha