Search Unity

Why is my grass shader 'shaking', not 'billowing'?

Discussion in 'Shaders' started by Reverend-Speed, Sep 19, 2019.

  1. Reverend-Speed

    Reverend-Speed

    Joined:
    Mar 28, 2011
    Posts:
    284
    Hey folks. So, I'm working on the biggest shader I've written so far - a humble grass shader. But I need this shader to apply to a host of weird, knotty tree things, which cover the walls of a cave - and they should all move somewhat in unison, as if seaweed swaying in the tide.

    Unfortunately, my trees currently look more like this - 'epileptic trees', if you get the LOST reference. The individual 'branches' are shaking at the vertex level, and my attempts to fix my problems by changing the environment size and my perlin noise texture don't seem to be having any effect.

    The script was originally inspired by Linden Reid's grass shader, though I wanted it working with the Standard Shader. There's also a lot of Jose M. Olea's grass shader in this... but I seem to have screwed it up royally.

    Can anybody please point me in the right direction? I'm really lost in the weeds (no pun intended) here.

    My shader code...

    Code (CSharp):
    1. Shader "RS/WindGrassRS"
    2. {
    3.     Properties
    4.     {
    5.         // Surface shader parameters
    6.         _Color                ("Color", Color)                    = (1,1,1,1)
    7.         _MainTex            ("Albedo (RGB)", 2D)                = "white" {}
    8.         _Glossiness            ("Smoothness", Range(0,1))            = 0.5
    9.         _Metallic            ("Metallic", Range(0,1))            = 0.0
    10.         // Wind effect parameters
    11.         _WindDirection        ("Wind Direction", vector)            = (1,0, 1,0)
    12.  
    13.         _WindTex            ("Wind Texture", 2D)                = "white" {}
    14.         _WorldSize            ("World Size", vector)                = (1,1,1,1)
    15.         _WindSpeed            ("Wind Speed", vector)                = (1,1,1,1)
    16.  
    17.     }
    18.     SubShader
    19.     {
    20.         Tags {"RenderType" = "Opaque"}
    21.        
    22.         LOD 200
    23.         CGPROGRAM
    24.         #pragma surface surf Standard vertex:vert
    25.        
    26.         struct Input
    27.         {
    28.             float2 uv_MainTex;
    29.         };
    30.         sampler2D    _MainTex;
    31.         half        _Glossiness;
    32.         half        _Metallic;
    33.         fixed4        _Color;
    34.         float3        _WindDirection;
    35.  
    36.         sampler2D    _WindTex;
    37.         float4      _WorldSize;
    38.         float4        _WindSpeed;
    39.  
    40.         // our vert modification function
    41.         void vert( inout appdata_full v )
    42.         {
    43.             float4 localSpaceVertex = v.vertex;
    44.             // Takes the mesh's verts and turns it into a point in world space
    45.             // this is the equivalent of Transform.TransformPoint on the scripting side
    46.             float4 worldSpaceVertex = mul( unity_ObjectToWorld, localSpaceVertex );
    47.             // normalize position based on world Size
    48.             float2 samplePos = ((worldSpaceVertex.xz + _WorldSize.xz/2) / _WorldSize.xz);
    49.  
    50.             // scroll sample position based on time
    51.             samplePos += _Time.x * _WindSpeed.xy;
    52.  
    53.             // Sample wind texture
    54.             float windSample = tex2Dlod (_WindTex, float4(samplePos, 0, 0));
    55.  
    56.             // Height of the vertex in the range (0,1)
    57.             float height = (localSpaceVertex.y/2 + .5);
    58.  
    59.             float sineX = sin(worldSpaceVertex.x * windSample);
    60.             float sineZ = sin(worldSpaceVertex.z * windSample);
    61.  
    62.             worldSpaceVertex.x += sineX * height * _WindDirection.x * v.color;
    63.             worldSpaceVertex.z += sineZ * height * _WindDirection.z * v.color;
    64.             // takes the new modified position of the vert in world space and then puts it back in local space
    65.             v.vertex = mul( unity_WorldToObject, worldSpaceVertex );
    66.         }
    67.         void surf (Input IN, inout SurfaceOutputStandard o)
    68.         {
    69.             // Albedo comes from a texture tinted by color
    70.             fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
    71.             o.Albedo = c.rgb;
    72.             // Metallic and smoothness come from slider variables
    73.             o.Metallic = _Metallic;
    74.             o.Smoothness = _Glossiness;
    75.             o.Alpha = c.a;
    76.         }
    77.         ENDCG
    78.     }
    79.     FallBack "Diffuse"
    80. }
     
  2. Reverend-Speed

    Reverend-Speed

    Joined:
    Mar 28, 2011
    Posts:
    284
    So sorry to bump this, folks, but I'm really stuck and I need to get this working right. =/
     
  3. tcjkant

    tcjkant

    Joined:
    Jan 12, 2019
    Posts:
    15
    The shader seems to have 2 assumptions:

    1. Local space min y position is -1, max y position is 1
    2. Vertex color painted with displacement weights (black doesn't move, white has full motion)

    If your model isn't set up this way then you'll see the entire blade moving, the bottoms not sticking to the ground, etc.
     
  4. Reverend-Speed

    Reverend-Speed

    Joined:
    Mar 28, 2011
    Posts:
    284
    Hello @tenderclawsJK , thanks for responding!

    In reverse order,

    2) The model is painted up with the correct vertex colours. That's working as intended...!

    1) I'm not entirely sure about this one - the model is fairly large and I'm using it at multiple scales in the project... but presumably vertices would be above max Y of 1 in most models. I'm really confused with this. I've attached one of the models for the shader to this response - if you've any ideas here, would really appreciate it.
     

    Attached Files:

  5. tcjkant

    tcjkant

    Joined:
    Jan 12, 2019
    Posts:
    15
    1. // Height of the vertex in the range (0,1)
    2. float height = (localSpaceVertex.y/2 + .5);
    This part, if the local space of your model is > 1, will return a height value of > 1, which then will apply more than 100% of the desired effect (at least suggested by the way the code is written).

    You can clamp the value by changing the line to

    float height = saturate(localSpaceVertex.y/2 + .5);

    And see if that makes any difference. If you have vertex colors you may just be better off removing this height variable at all, I'm not sure if it's necessary to get the effect you're looking for.
     
  6. tcjkant

    tcjkant

    Joined:
    Jan 12, 2019
    Posts:
    15
    Actually looking at the code again it seems that you're not including your Y position into the wind sample pos or sineX and sineZ functions. So if you have a blade of grass that sticks straight up, the entire blade will receive the same exact displacement. If you include the y position as an offset to your sine wave functions, for example, you would see a ripple traveling up or down the blade of grass rather than the entire blade moving in the same way.