Search Unity

Grabpass UVs independent from camera position

Discussion in 'Shaders' started by milanhenkrich, Aug 19, 2019.

  1. milanhenkrich

    milanhenkrich

    Joined:
    Jul 26, 2018
    Posts:
    15
    Hey Guys, I have problem with grab pass shader.

    I am developing 2D water and I am using grab pass to add distortion. I am also adding texture "ColorPatter" to the water. Everything is working fine, but when I move the camera, my texture which is on the water is moving too.

    I tied to solve it by something like:

    i.uvgrab = i.uvgrab.x - _WorldSpaceCameraPos.x , i.uvgrab.y - _WorldSpaceCameraPos.y .... ??? but it didnt work out ...

    Please see bellow frag function from grabpass:

    Code (CSharp):
    1.       half4 frag (vertexOutput i) : SV_Target
    2.       {
    3.         half4 bump = tex2D(_MainTex, i.uvmain);
    4.         half2 distortion = UnpackNormal(bump).rg;
    5.         i.uvgrab.xy += distortion * _Magnitude;
    6.  
    7.         half4 col = tex2D(_BackgroundTex, (i.uvgrab));
    8.         col *= tex2D(_ColorPattern, (i.uvgrab))*_ColorPatternBrightness;
    9.         return col;
    10.       }
    Water:
    water1.PNG

    Water - when camera is moved (it can be clearly seen that pattern is at a different position): 33.PNG

    Thank you in advance for any help
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    7,153
    The grab pass UVs, if handled properly, are screen space UVs. That means the position of the object doesn't matter, only the position of the pixel on the screen. If you want the pattern to stay with the water, the easiest solution is to use the mesh's uvs, ie: your "uvmain". Alternatively, you could use the world space position as UVs. You can't subtract the camera's world position from the grab UVs since they aren't in the same space or scale.
     
  3. milanhenkrich

    milanhenkrich

    Joined:
    Jul 26, 2018
    Posts:
    15
    Thank you for reply bgolus.
    I tried your first suggestion, but when I try this then I don't see the pattern at all. And for your second one I am not sure I understand.

    I sent you my shader code just in case.
    Code (CSharp):
    1.  
    2.  
    3. Shader "Custom/Water2D" {
    4.  
    5. Properties {
    6.     _MainTex ("Normalmap - Distortionmap", 2D) = "bump" {}
    7.     _Magnitude("Distortion Magnitude", Range(0,0.5)) = 0.05
    8.     _ColorPattern("Water Patter",2D) = "white" {}
    9.     _ColorPatternScaller("Water Pattern Scaller",Range(1,5))= 1.0
    10.     _ColorPatternOffsetX("Water Pattern Offset X",Range(1,5))= 1.0
    11.     _ColorPatternOffsetY("Water Pattern Offset Y",Range(1,5))= 1.0
    12.     _ColorPatternBrightness("Water Pattern Alpha", Range(1,5)) = 2.0
    13. }
    14.  
    15. Category {
    16.  
    17.     Tags { "Queue"="Transparent" }
    18.  
    19.     SubShader {
    20.  
    21.         // This pass grabs the screen behind the object into a texture.
    22.  
    23.         GrabPass { "_BackgroundTex" }
    24.      
    25.         // Main pass: Take the texture grabbed above and use the bumpmap to perturb it
    26.         // on to the screen
    27.         Pass {
    28.             Name "BASE"
    29.          
    30.       CGPROGRAM
    31.       #pragma vertex vert
    32.       #pragma fragment frag
    33.       #include "UnityCG.cginc"
    34.  
    35.       struct vertexInput {
    36.           float4 vertex : POSITION;
    37.           float2 texcoord: TEXCOORD0;
    38.       };
    39.  
    40.       struct vertexOutput {
    41.           float4 vertex : SV_POSITION;
    42.           float4 uvgrab : TEXCOORD1;
    43.           float2 uvmain : TEXCOORD2;
    44.           UNITY_FOG_COORDS(3)
    45.       };
    46.  
    47.       sampler2D _MainTex;
    48.       sampler2D _ColorPattern;
    49.    
    50.       float4 _MainTex_ST;
    51.       float _BumpAmt;
    52.       float4 _BumpMap_ST;
    53.       float  _Magnitude;
    54.       float _ColorPatternScaller;
    55.       float _ColorPatternOffsetX;
    56.       float _ColorPatternOffsetY;
    57.       // float3 _WorldSpaceCameraPos;
    58.       float _ColorPatternBrightness;
    59.  
    60.       vertexOutput vert (vertexInput v)
    61.       {
    62.           vertexOutput o;
    63.           o.vertex = UnityObjectToClipPos(v.vertex);
    64.           #if UNITY_UV_STARTS_AT_TOP
    65.           float scale = -1.0;
    66.           #else
    67.           float scale = 1.0;
    68.           #endif
    69.           o.uvgrab.xy = (float2(o.vertex.x, o.vertex.y*scale) + o.vertex.w) * 0.5;
    70.           o.uvgrab.zw = o.vertex.zw;
    71.           o.uvmain = TRANSFORM_TEX( v.texcoord, _MainTex );
    72.          UNITY_TRANSFER_FOG(o,o.vertex);
    73.           return o;
    74.       }
    75.  
    76.       sampler2D _BackgroundTex;
    77.       float4 _GrabTexture_TexelSize;
    78.       sampler2D _BumpMap;
    79.  
    80.       half4 frag (vertexOutput i) : SV_Target
    81.       {
    82.           // Calculate perturbed coordinates
    83.         half4 bump = tex2D(_MainTex, i.uvmain);
    84.  
    85.         half2 distortion = UnpackNormal(bump).rg;
    86.         i.uvgrab.xy += distortion * _Magnitude;
    87.  
    88.         // Get pixel in GrabTexture, rendered in previous pass
    89.  
    90.         half4 col = tex2D(_BackgroundTex, (i.uvgrab));
    91.         float4 newUV = i.uvgrab;
    92.         newUV = (newUV* _ColorPatternScaller) + float4(_ColorPatternOffsetX + _WorldSpaceCameraPos.x*0.1*_ColorPatternScaller , _ColorPatternOffsetY +  _WorldSpaceCameraPos.y*0.2f*_ColorPatternScaller ,0,0);
    93.         half4 waterPattern = tex2D(_ColorPattern, (newUV))*_ColorPatternBrightness;
    94.         col = col+ waterPattern/4;
    95.         return col;
    96.       }
    97.       ENDCG
    98.         }
    99.     }
    100. }
    101.  
    102. }
    103.  
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    7,153
    First, why aren't you using the built in ComputeGrabScreenPos function?
    Code (csharp):
    1. o.uvgrab = ComputeGrabScreenPos(o.vertex);
    Probably won't change anything, but good to do anyway.

    Secondly, you're not using the grab uvs properly. You can't just pass i.uvgrab into a tex2D() function. You should be using the tex2Dproj() function, or dividing the i.uvgrab.xy by i.uvgrab.w.
    Code (csharp):
    1. half4 col = tex2Dproj(_BackgroundTex, i.uvgrab);
    2. // or
    3. half4 col = tex2D(_BackgroundTex, i.uvgrab.xy / i.uvgrab.w);
    The tex2D() function only uses the xy values, but the grab UVs, or any screen space UVs, are going to be in a special format to correct for linearly interpolation of projection space positions. It's not important for you to understand that unless you want to dig into homogeneous clip space and GPU rasterization. Just know that the grabuv value is a float4 for a good reason.

    On old mobile devices, they had an actual tex2Dproj function that handles that divide by w in the hardware. All modern GPUs just remap that tex2Dproj into a tex2D and do the divide in the shader. So basically, unless you're planning on this running on OpenGL ES 2.0 mobile devices, there's no reason to use tex2Dproj, apart from it being slightly less code to write.

    Using the main uvs for the color pattern? I suspect you've set your "main tex", which you're using as the distortion map, to have a high repetition. That would mean your color pattern is also using that same high repetition which you probably don't want. You'll need separate UVs for both in that case. Or more specifically use o.uvmain = v.texcoord.xy; and apply the TRANSFOR_TEX macro in the fragment shader like this:
    Code (csharp):
    1. float4 bump = tex2D(_MainTex, TRANSFORM_TEX(i.uvmain, _MainTex));
    And again, you cannot use the _WorldSpaceCameraPos here. Neither the screenspace UVs or the mesh UVs are in world space, so trying to adjust them using a world space position is just going to cause unexpected results.

    Calculate the world position in the vertex shader as another value you pass to the fragment shader.
    Code (csharp):
    1. o.worldPos = mul(unity_ObjectToWorld, float4(v.vertex.xyz, 1.0)).xyz;
    Then use the .xy values of that as the UVs for your color pattern. You can even scale those or offset then as you wish like any other UVs. You also shouldn't have to try to counter-move them with the camera position since since they'll already be "stable" and not follow the camera around.
     
    Last edited: Aug 23, 2019
    milanhenkrich likes this.
  5. milanhenkrich

    milanhenkrich

    Joined:
    Jul 26, 2018
    Posts:
    15
    I am sorry for later reply.

    I just tried your advice and I get it to work. Thank you so much for your help.
    I am still not sure whether I understand your last line of code, but this one I will have to do on my own.
     
  6. milanhenkrich

    milanhenkrich

    Joined:
    Jul 26, 2018
    Posts:
    15
    Hello bgolus,

    Sorry to came back to this after long time. When I tried to implement it I was thinking it is working, but now I realised that actually pattern (blue squares - see above in my question) are not distorted at all... So grab pass is applied only on what is behind but not on actually pattern.

    Please see my modified code after your suggestions (I hope I followed it correctly):

    Code (CSharp):
    1. // Upgrade NOTE: commented out 'float3 _WorldSpaceCameraPos', a built-in variable
    2.  
    3. // Upgrade NOTE: commented out 'float3 _WorldSpaceCameraPos', a built-in variable
    4.  
    5. // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
    6.  
    7. Shader "Custom/Water2D" {
    8.  
    9. Properties {
    10.     _MainTex ("Normalmap - Distortionmap", 2D) = "bump" {}
    11.     _Magnitude("Distortion Magnitude", Range(0,0.5)) = 0.05
    12.     _ColorPattern("Water Patter",2D) = "white" {}
    13.     _ColorPatternScaller("Water Pattern Scaller",Range(1,5))= 1.0
    14.     _ColorPatternOffsetX("Water Pattern Offset X",Range(1,5))= 1.0
    15.     _ColorPatternOffsetY("Water Pattern Offset Y",Range(1,5))= 1.0
    16.     _ColorPatternBrightness("Water Pattern Alpha", Range(1,5)) = 2.0
    17. }
    18.  
    19. Category {
    20.  
    21.     Tags { "Queue"="Transparent" }
    22.  
    23.     SubShader {
    24.  
    25.         // This pass grabs the screen behind the object into a texture.
    26.  
    27.         GrabPass { "_BackgroundTex" }
    28.        
    29.         // Main pass: Take the texture grabbed above and use the bumpmap to perturb it
    30.         // on to the screen
    31.         Pass {
    32.             Name "BASE"
    33.            
    34.       CGPROGRAM
    35.       #pragma vertex vert
    36.       #pragma fragment frag
    37.       #include "UnityCG.cginc"
    38.  
    39.       struct vertexInput {
    40.           float4 vertex : POSITION;
    41.           float2 texcoord: TEXCOORD0;
    42.       };
    43.  
    44.       struct vertexOutput {
    45.           float4 vertex : SV_POSITION;
    46.           float4 uvgrab : TEXCOORD1;
    47.           float2 uvmain : TEXCOORD2;
    48.           float3 worldPos : TEXCOORD0;
    49.           UNITY_FOG_COORDS(3)
    50.       };
    51.  
    52.       sampler2D _MainTex;
    53.       sampler2D _ColorPattern;
    54.      
    55.       float4 _MainTex_ST;
    56.       float _BumpAmt;
    57.       float4 _BumpMap_ST;
    58.       float  _Magnitude;
    59.       float _ColorPatternScaller;
    60.       float _ColorPatternOffsetX;
    61.       float _ColorPatternOffsetY;
    62.       float _ColorPatternBrightness;
    63.  
    64.       vertexOutput vert (vertexInput v)
    65.       {
    66.           vertexOutput o;
    67.           o.vertex = UnityObjectToClipPos(v.vertex);
    68.           #if UNITY_UV_STARTS_AT_TOP
    69.           float scale = -1.0;
    70.           #else
    71.           float scale = 1.0;
    72.           #endif
    73.  
    74.          o.uvgrab = ComputeGrabScreenPos(o.vertex);
    75.          o.uvmain = TRANSFORM_TEX( v.texcoord, _MainTex );
    76.          o.worldPos = mul(unity_ObjectToWorld, float4(v.vertex.xyz, 1.0)).xyz;
    77.          UNITY_TRANSFER_FOG(o,o.vertex);
    78.          return o;
    79.       }
    80.  
    81.       sampler2D _BackgroundTex;
    82.       float4 _GrabTexture_TexelSize;
    83.       sampler2D _BumpMap;
    84.  
    85.       half4 frag (vertexOutput i) : SV_Target
    86.       {
    87.           // Calculate perturbed coordinates
    88.         half4 bump = tex2D(_MainTex, TRANSFORM_TEX(i.uvmain, _MainTex));
    89.  
    90.         half2 distortion = UnpackNormal(bump).rg;
    91.         i.uvgrab.xy += distortion * _Magnitude;
    92.    
    93.         // Get pixel in GrabTexture, rendered in previous pass
    94.  
    95.         half4 col = tex2Dproj(_BackgroundTex, (i.uvgrab));
    96.         half4 waterPattern = tex2D(_ColorPattern, (i.worldPos))*_ColorPatternBrightness;
    97.         col = col+ waterPattern/4;
    98.         return col;
    99.       }
    100.       ENDCG
    101.         }
    102.     }
    103. }
    104.  
    105. }
     
  7. milanhenkrich

    milanhenkrich

    Joined:
    Jul 26, 2018
    Posts:
    15
    Okay so what I have done was:

    Code (CSharp):
    1.  i.worldPos.xy +=  distortion * _Magnitude;
    in frag shader. It is working but perhaps you might know of better solution