Search Unity

How to scale UV coordinates from the center of an offset?

Discussion in 'Shaders' started by Mr_Admirals, Dec 20, 2018.

  1. Mr_Admirals

    Mr_Admirals

    Joined:
    May 13, 2017
    Posts:
    86
    Okay so for the next step on my snow shader, I'm going to have the active deformation area Blit its blurred texture to a larger texture meant to encompass a mesh larger than the active deformation area. This way, snow that's been deformed stays deformed when it is no longer located inside the active deformation area.

    To do this I was wanting to use: public static void Blit(Texture source, RenderTexture dest, Vector2 scale, Vector2 offset);

    The issue, as I found out when trying to blur my depth texture, is that the Blit simply does not write to the depth buffer. It was easy enough to get around as I was dealing with textures of the same size, but now I want to scale and offset the texture so it fits the appropriate region on the larger texture. To do this I basically need to understand how Blit works.

    Think any of you can help me out?
     
  2. aleksandrk

    aleksandrk

    Unity Technologies

    Joined:
    Jul 3, 2017
    Posts:
    3,014
    You need to use a version that accepts a material as a parameter. You'll have a shader there, which you can configure the way you need.
     
  3. Przemyslaw_Zaworski

    Przemyslaw_Zaworski

    Joined:
    Jun 9, 2017
    Posts:
    328
    Copy raw source into destination:

    Code (CSharp):
    1.     void Blit(RenderTexture source, RenderTexture destination)
    2.     {
    3.         if (!InternalMaterial) InternalMaterial = new Material(Shader.Find("Sprites/Default"));
    4.         RenderTexture.active = destination;
    5.         InternalMaterial.SetTexture("_MainTex", source);
    6.         GL.PushMatrix();
    7.         GL.LoadOrtho();
    8.         InternalMaterial.SetPass(0);
    9.         GL.Begin(GL.QUADS);
    10.         GL.MultiTexCoord2(0, 0.0f, 0.0f);
    11.         GL.Vertex3(0.0f, 0.0f, 0.0f);
    12.         GL.MultiTexCoord2(0, 1.0f, 0.0f);
    13.         GL.Vertex3(1.0f, 0.0f, 0.0f);
    14.         GL.MultiTexCoord2(0, 1.0f, 1.0f);
    15.         GL.Vertex3(1.0f, 1.0f, 1.0f);
    16.         GL.MultiTexCoord2(0, 0.0f, 1.0f);
    17.         GL.Vertex3(0.0f, 1.0f, 0.0f);
    18.         GL.End();
    19.         GL.PopMatrix();      
    20.     }
    Copy raw source into destination, pixels are modified by material (shader).
    Code (CSharp):
    1.     void Blit(RenderTexture source, RenderTexture destination, Material mat)
    2.     {
    3.         RenderTexture.active = destination;
    4.         mat.SetTexture("_MainTex", source);
    5.         GL.PushMatrix();
    6.         GL.LoadOrtho();
    7.         GL.invertCulling = true;
    8.         mat.SetPass(0);
    9.         GL.Begin(GL.QUADS);
    10.         GL.MultiTexCoord2(0, 0.0f, 0.0f);
    11.         GL.Vertex3(0.0f, 0.0f, 0.0f);
    12.         GL.MultiTexCoord2(0, 1.0f, 0.0f);
    13.         GL.Vertex3(1.0f, 0.0f, 0.0f);
    14.         GL.MultiTexCoord2(0, 1.0f, 1.0f);
    15.         GL.Vertex3(1.0f, 1.0f, 1.0f);
    16.         GL.MultiTexCoord2(0, 0.0f, 1.0f);
    17.         GL.Vertex3(0.0f, 1.0f, 0.0f);
    18.         GL.End();
    19.         GL.invertCulling = false;
    20.         GL.PopMatrix();      
    21.     }
     
  4. Mr_Admirals

    Mr_Admirals

    Joined:
    May 13, 2017
    Posts:
    86
    Thank you for the replies.

    Right, I figured as much. I'm just curious how Blit() manages to scale and offset its textures so I can replicate the same behavior when copying the depth.
     
  5. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,343
    Nothing to do with blit. Blit is just rendering a quad on screen with a material, nothing more. If you render a quad mesh with a material and change the texture the material uses, it'll always show across the entire mesh regardless of if the texture is 4x4 pixels or 4096x4096 pixels because UVs are a normalized 0.0 to 1.0 range where 0.0, 0.0 is the bottom left and 1.0, 1.0 is the top right no matter the resolution or aspect ratio of the texture.

    If you have two textures of mis-matched resolution and want to copy the contents of one to the other such that they match pixel for pixel rather than using normalized UV space, then you need to calculate the scale difference in the shader and apply it to the UVs used for sampling the _MainTex.

    Or use CopyTexture().
    https://docs.unity3d.com/ScriptReference/Rendering.CommandBuffer.CopyTexture.html
     
    Mr_Admirals likes this.
  6. Mr_Admirals

    Mr_Admirals

    Joined:
    May 13, 2017
    Posts:
    86
    Awesome. Thank you Bgolus! I'll look into it!
     
  7. Mr_Admirals

    Mr_Admirals

    Joined:
    May 13, 2017
    Posts:
    86
    Okay, so I went with calculating the scale and offset myself as I found out CopyTexture also doesn't copy over depth values. I'm very close to getting it working but am struggling to get the UV's to scale from the center of the offset.

    Here's the code I'm working with. You'll note I've tried a few different ways to get it working:
    Code (CSharp):
    1. half4 ScaleAndOffsetFragmentShader(v2f_img i, out float outDepth : SV_Depth) : SV_Target
    2. {
    3.     // Apply backwards transformation
    4.     /*float2 newUV = i.uv * _DepthTransform.zw;
    5.     newUV.xy = newUV.xy - (_DepthTransform.xy - 0.25f);*/
    6.     /*float2 newUV = i.uv * float2(_ScaleX, _ScaleY) + float2(_XMod, _YMod);
    7.     newUV = newUV.xy - float2(_XMod, _YMod);*/
    8.     float2 newUV = i.uv - float2(_XMod, _YMod);
    9.     newUV = newUV.xy * float2(_ScaleX, _ScaleY) + float2(_XMod, _YMod);
    10.  
    11.     // Test bounds and discard if outside 0 - 1 range
    12.     if (newUV.x < 0.0 || newUV.x > 1.0 || newUV.y < 0.0 || newUV.y > 1.0) {
    13.         discard;
    14.     }
    15.  
    16.     // Lookup using new UV
    17.     float depth = SAMPLE_DEPTH_TEXTURE(_MyDepthTex, newUV);
    18.     outDepth = depth;
    19.     return 0;
    20. }
    _XMod and _YMod control the U and V offset, and _ScaleX and _ScaleY should be self explanatory. I have them hooked up to the inspector so I can modify their values directly to see if they're doing what I want them to do.
     
  8. Mr_Admirals

    Mr_Admirals

    Joined:
    May 13, 2017
    Posts:
    86
    Ohp. Solved it already. Had a bit of an epiphany. It was right in front of me:

    Code (CSharp):
    1. float2 newUV = i.uv - float2(_XMod, _YMod);
    2. newUV = (newUV.xy * float2(_ScaleX, _ScaleY)) + float2(0.5f, 0.5f);
    UPDATE: Okay, so the above code works great for flat ground. When you're not on flat ground, there's still a bit of a discrepancy in the offset.

    UPDATE 2: Well, It actually doesn't work on flat ground. Hmm.... When I move the active deformation area to a corner, it doesn't scale from the center anymore.
     
    Last edited: Dec 22, 2018
  9. IIporpammep

    IIporpammep

    Joined:
    Aug 16, 2015
    Posts:
    39
    Hi, could you please share, if you have found the solution for this problem?