Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Voting for the Unity Awards are OPEN! We’re looking to celebrate creators across games, industry, film, and many more categories. Cast your vote now for all categories
    Dismiss Notice
  3. Dismiss Notice

Smooth out float value to remove sharp edge?

Discussion in 'Shaders' started by VileGoo, Sep 8, 2018.

  1. VileGoo

    VileGoo

    Joined:
    Apr 29, 2017
    Posts:
    220
    I've added functionality to make shader that makes it invisible when the player is a certain distance away from it. It works well but there is a very sharp edge visible where the shader becomes invisible, I want to smooth out this edge so it nicely fades out as the player gets closer. How can I accomplish this?

    EDIT: Actually, is it possible for me to fade out all the objects UV's equally when the player gets closer, rather than fading based on what vertex points are currently close enough to the player, which is what is causing the sharp edge?

    Code (CSharp):
    1. float dist = distance(i.worldPos, _PlayerPos);
    2. if (dist < _FadeDistance)
    3. {
    4.     float fadeAmount = 0.0; // This value needs to fade based on players distance so there isn't a sharp edge
    5.     col.a *= fadeAmount;
    6. }
    7. else
    8. {
    9.     col.a *= fresnel * _AlphaStrength * fade;
    10. }
     
    Last edited: Sep 8, 2018
  2. VileGoo

    VileGoo

    Joined:
    Apr 29, 2017
    Posts:
    220
    Update:

    I tried this to see if it would work, but i'm getting an error: scalar value expected. No idea what this means or how I can fix it.

    Code (CSharp):
    1. float dist = distance(i.worldPos, _PlayerPos);
    2. if (dist < _FadeDistance)
    3. {
    4.     for (int j = 0; j < i.uv; j++)
    5.     {
    6.         float fadeAmount = saturate(fresnel * (dist - _FadeDistance));
    7.         col.a *= fadeAmount;
    8.     }
    9. }
    10. else
    11. {
    12.     col.a *= fresnel * _AlphaStrength * fade;
    13. }
    14.  
     
    Last edited: Sep 8, 2018
  3. neoshaman

    neoshaman

    Joined:
    Feb 11, 2011
    Posts:
    6,466
    j < i.uv

    you can't do that, uv is actually an "array"
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,229
    It's an array in C#. It's a vector in the shader.
     
  5. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,229
    Really this comes down to you have a significant misunderstanding about how shaders work. That’s not a dig at you, just lack of knowledge.

    Vertex and fragment shaders work on a single element at a time with no knowledge of anything but itself. For vertex shaders it is a single mesh vertex. For fragment shaders it is a single on screen pixel of a single triangle. In the case of UVs, this is generally a float2 value defining what part of a texture should be displaced. UVs are in normalized texture space, that is to say 0.0, 0.0 is the bottom left corner, 1.0, 1.0 is the top right corner, regardless of the resolution of the texture. The vertex shader usually just passes this value on to the fragment shader with minimal modifications (in Unity shaders, they default to applying an optional scale and offset). In the fragment shader this is still a single float2 value, but it is a value that is interpolated between the triangle’s vertices based on barycentric interpolation.

    This means you can have a quad (actually two triangles) with the four corners of the quad set to use UVs placed in the four corners of the texture. Then when the fragment shader renders it gets the values inbetween so the contents of the texture is rendered rather than just the corner values.

    I’ve written about this a ton elsewhere, but if statements in shaders don’t do what you think they do if you come from a C# or other more traditional programming background. Well, they kind of do, but most of the time an if statement will not skip the “other side” of the conditional, but rather all possible outcomes are calculated, and then the unneeded ones thrown out. And even when the if statement really does skip the other side, that branch has a significant fixed cost on its own that can often be higher than the work you’re trying to avoid.


    Going back to the issue at hand. You want to fade something out when the player gets close to it, is that correct? You already have a fixed distance you want this to happen (_FadeDistance), and you know the distance the player is from each pixel (dist). Assuming you’re okay with it fading out within the range of 1 world space unit, you only need this:

    float dist = distance(i.worldPos, _PlayerPos);
    float fadeAmount = saturate(dist - _FadeDistance);
    col.a *= fadeAmount;
    col.a *= fresnel * _AlphaStrength * fade;

    All of that if / else isn’t saving you anything really, and may be making it slower. If you want the fade to happen with a little more shape, you can add or subtract the fresnel or uv.y or some noise texture value, etc. to make it fade out in a more interesting way than just purely distance. You could even change it to fade by the object’s mesh pivot distance to the player rather than the mesh surface distance by extracting the world position from the object to world transform matrix (as long as the object’s mesh isn’t being batched).
     
    SugoiDev likes this.