Search Unity

How to get distance from camera to pixel in fragment shader?

Discussion in 'Shaders' started by CGDever, Nov 8, 2016.

  1. CGDever

    CGDever

    Joined:
    Dec 17, 2014
    Posts:
    156
    Hello everyone!
    I have a very simple problem)
    It is necessary to have a distance from the camera to each pixel in the pixel shader to change the transparency smoothly.
    Why in the pixel? Because the geometry of the object can be changed and it should not affect the effect.
    This is shader changes the alpha, it is based on the vertex shader:

    Code (csharp):
    1.  
    2. Properties {
    3. _TintColor ("TintColor", Color) = (0.5,0.5,0.5,0.5)
    4. _MainTex ("ParticleTexture", 2D) = "white" {}
    5. _ColorStrength ("ColorStrength", Float) = 1
    6. _CutOutLightCore ("CutOutLightCore", Range(0, 1)) = 0.5
    7.  
    8. //_DistK("Distancekoef",Float)= 10
    9. }
    10.  
    11. Category {
    12. Cull back
    13. cull Off
    14. Lighting Off
    15. ZWrite OFF
    16. ZTest off//Always
    17. Fog { mode off }//Color(0,0,0,0)}
    18. Blend One OneMinusSrcAlpha//SrcAlpha OneMinusSrcAlpha
    19.  
    20. SubShader {
    21. Tags {
    22. "Queue"="Overlay"
    23. "IgnoreProjector"="True"
    24. //"RenderType"="Transparent"
    25. }
    26. Pass {
    27. CGPROGRAM
    28. #pragma vertex vert
    29. #pragma fragment frag
    30.  
    31.  
    32. #include "UnityCG.cginc"
    33.  
    34. sampler2D _MainTex;
    35. uniform fixed4 _TintColor;
    36. //fixed4_CoreColor;
    37. float _CutOutLightCore;
    38. //float_TintStrength;
    39. float _ColorStrength;
    40. //float_DistK;
    41.  
    42. struct appdata_t {
    43. float4 vertex : POSITION;
    44. fixed4 color : COLOR;
    45. float2 texcoord : TEXCOORD0;
    46. };
    47.  
    48. struct v2f {
    49. float4 vertex : POSITION;
    50. float2 texcoord : TEXCOORD0;
    51.  
    52. float dist : TEXCOORD1;
    53. };
    54.  
    55. float4 _MainTex_ST;
    56.  
    57. v2f vert (appdata_t v)
    58. {
    59. v2f o;
    60. o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
    61. o.texcoord = TRANSFORM_TEX(v.texcoord,_MainTex);
    62.  
    63. float k = length(ObjSpaceViewDir(v.vertex));///_DistK;
    64. o.dist = k*k*k;
    65. return o;
    66. }
    67.  
    68. fixed4 frag (v2f i) : COLOR
    69. {
    70. fixed4 tex = tex2D(_MainTex, i.texcoord);//* _TintStrength
    71. fixed4 col = tex;
    72.  
    73. col.a = i.dist;
    74. return clamp(col, 0, 1);
    75. }
    76. ENDCG
    77. }
    78. }
    79. }
    80.  
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,348
    ObjSpaceViewDir is going to get the distance from a point to the camera in object space, which if you've got a scaling factor on your model importer is going to get you a weird length to that vector. What you really want is the world distance from the camera to the vertex.

    For that you just need: o.dist = length(WorldSpaceViewDir(v.vertex));

    However using world distance for near fading might still have some odd problems depending on your camera's near clip distance. Basically you might still see some clipping occur since it's the distance from the camera to the vertex and the distance from the vertex to the near clip is going to be less, so you might want to put a small bias on your distance, like -0.1.
     
  3. CGDever

    CGDever

    Joined:
    Dec 17, 2014
    Posts:
    156
    blogus, thank you for answer. But this is not exactly what I wanted.
    This solution changes color depending on the pixel arrangement of the vertices to the camera.
    For example there is a plane. If the camera closer to the vertices of the plane, then all is well, the color changes as it should. If you bring the camera to the center of the plane, where there are no vertices, the color will not change (in this case, everything is bad).
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,348
    Values calculated on each vertex are interpolated across the surface of the triangle for each pixel. So in your case let's think of just a straight line with two vertices on each end. Both points are 1 unit away from the camera, so the result will be that every pixel will also use the value of 1. It doesn't matter if the center of that line is 0.05 units away from the camera and the two vertices are on nearly opposite sides of the camera, the distance calculated was 1, so the interpolated value is 1.

    So yes, you need to calculate the distance at every pixel. To do that you just need to pass the float3 value from that function from the vertex to the pixel shader, and do the length there, alternatively you can calculate the z depth rather than the distance which will interpolate properly and doesn't need to do any additional work in the pixel shader.

    https://forum.unity3d.com/threads/object-depth-shader.432012/#post-2800670
     
  5. CGDever

    CGDever

    Joined:
    Dec 17, 2014
    Posts:
    156
    Code (csharp):
    1. v2f vert (appdata_t v)
    2. {
    3. v2f o;
    4. o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
    5. o.texcoord = TRANSFORM_TEX(v.texcoord,_MainTex);
    6.  
    7. //floatk=length(WorldSpaceViewDir(v.vertex));///_DistK;
    8. o.pos = UnityObjectToViewPos(v.vertex);// <=====================
    9. //COMPUTE_EYEDEPTH(o.dist);//o.dist=k*k*k;
    10. return o;
    11. }
    12.  
    13. fixed4 frag (v2f i) : COLOR
    14. {
    15. fixed4 tex = tex2D(_MainTex, i.texcoord);//* _TintStrength
    16. fixed4 col = (_TintColor * tex.r*_ColorStrength - _CutOutLightCore); //_CoreColor*
    17.  
    18. //floatdepth=(i.dist-0.1)*10;
    19. float k = length(i.pos);// <=====================
    20. col.a *= k;//depth;
    21. //else
    22. //col.a=i.dist;
    23. return clamp(col, 0, 1);
    24. }
    Fantastic! It seems to be working as it should :)
     
  6. joehot200

    joehot200

    Joined:
    Apr 5, 2016
    Posts:
    9
    Thanks very much for this! It definitely seemed to work very well.

    For future people coming to this thread, you may wish to adjust the distance slightly. Personally, I used

    Code (csharp):
    1.  
    2. float dist = 10 - (length(i.viewPos) / 50); //Note: viewPos is the exact same as pos in the example above, however my shader already used the variable pos.
    3.                 if (dist > 1) dist = 1;
    4.                 if (dist < 0.0001) dist = 0;
    5.                 toReturn.a *= dist;
    6.  
    The dist < 0.0001 was to avoid artifacts that seemed to arise with low values on the transparent object I was using.


    The best thing about this solution (that I struggled to find elsewhere) Is that it doesn't rely on the depth texture - as the object I was modifying the shader for was transparent, and so did not write to the depth texture.
     
    Last edited: Sep 3, 2019
    ttesjt likes this.
  7. astracat111

    astracat111

    Joined:
    Sep 21, 2016
    Posts:
    725
    Here's a fade strength equasion that works well, you can just do:

    Code (CSharp):
    1. col.a = smoothstep(minPercentage,maxPercentage,(curPercentage * pi) / _FadeStrength);
    In my case I just input the camera global position as a Vector4 using material.SetVector() every frame to get the global camera position into the shader.